diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b74e54c --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +--- +BasedOnStyle: Google + +ColumnLimit: 100 +DerivePointerAlignment: false +AccessModifierOffset: -2 + +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^( download.sh + +variables: + # Vendor solver locations + GECODE_ROOT: "$CI_PROJECT_DIR/vendor/gecode" + GUROBI_ROOT: "$CI_PROJECT_DIR/vendor/gurobi" + CPLEX_ROOT: "$CI_PROJECT_DIR/vendor/CPLEX_Studio/cplex" + OSICBC_ROOT: "$CI_PROJECT_DIR/vendor/cbc" + # CCache settings + CCACHE_DIR: "$CI_PROJECT_DIR/.ccache" + CCACHE_MAXSIZE: "100M" + +# ----------- Quick check ----------- + +check:format: + stage: check + image: dekker1/minibuild:clang-format + script: + - mkdir -p build + - cd build + - cmake -GNinja .. -DCLANG_FORMAT_EXECUTABLE="run-clang-format" -DCLANG_FORMAT_FLAGS="--color always" + - cmake --build . --target format + tags: [linux, docker] + +# ----------- Build MiniZinc ----------- + +.build_template: &build_definition + stage: build + before_script: *download_script + script: + - sh download.sh vendor master vendor:${MZNARCH} vendor.zip + - unzip -q vendor.zip + - mkdir -p build + - cd build + - cmake -G"$CMAKE_ARCH" -DCMAKE_BUILD_TYPE=Release -v .. -DBUILD_REF=$CI_PIPELINE_ID -DUSE_PROPRIETARY=OFF -DCMAKE_INSTALL_PREFIX="$CI_PROJECT_DIR/minizinc" + - cmake --build . --config Release + - cmake --build . --config Release --target install + artifacts: + paths: [minizinc/] + cache: + key: "$CI_JOB_NAME" + paths: [.ccache, vendor.zip*] + +build:linux: + <<: *build_definition + image: dekker1/minibuild:cpp + variables: + MZNARCH: "linux" + CMAKE_ARCH: "Ninja" + tags: [linux, docker] + +build:musl: + <<: *build_definition + image: dekker1/minibuild:alpine + variables: + MZNARCH: "musl" + CMAKE_ARCH: "Ninja" + tags: [linux, docker] + +build:osx: + <<: *build_definition + stage: build + variables: + MZNARCH: "osx" + CMAKE_ARCH: "Ninja" + tags: [osx, cmake, cpp] + +build:win64: + stage: build + variables: + MZNARCH: "win64" + CMAKE_ARCH: "Visual Studio 15 2017 Win64" + before_script: + - curl -o download.sh --location --header "PRIVATE-TOKEN:%ACCESS_TOKEN%" --silent https://gitlab.com/api/v4/snippets/1796163/raw + - dos2unix download.sh + script: + - sh download.sh vendor master vendor:%MZNARCH% vendor.zip + - unzip -q vendor.zip + - if not exist "build" mkdir build + - cd build + - cmake -G"%CMAKE_ARCH%" .. -DBUILD_REF=%CI_PIPELINE_ID% -DUSE_PROPRIETARY=OFF -DCMAKE_INSTALL_PREFIX="%CI_PROJECT_DIR%/minizinc" + - cmake --build . --config Release + - cmake --build . --config Release --target install + artifacts: + paths: [minizinc/] + cache: + key: "win64" + paths: [vendor.zip*] + tags: [win64, cmake, cpp] + +build:wasm_complete: + image: trzeci/emscripten + stage: build + variables: + MZNARCH: "wasm" + CMAKE_ARCH: "Unix Makefiles" + tags: [docker] + before_script: *download_script + script: + - sh download.sh vendor master vendor:wasm vendor.zip + - unzip -q vendor.zip + - mkdir -p build + - cd build + - emconfigure cmake -G"$CMAKE_ARCH" -DCMAKE_FIND_ROOT_PATH="/" -DGECODE_ROOT="$GECODE_ROOT" -DOSICBC_ROOT="$OSICBC_ROOT" -DCMAKE_BUILD_TYPE=MinSizeRel -DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1 -v .. -DBUILD_REF=$CI_PIPELINE_ID -DUSE_PROPRIETARY=OFF -DCMAKE_INSTALL_PREFIX="$CI_PROJECT_DIR/minizinc" + - cmake --build . --config MinSizeRel + - cmake --build . --config MinSizeRel --target install + artifacts: + paths: [minizinc/] + cache: + key: "$CI_JOB_NAME" + paths: [.ccache, vendor.zip*] + when: manual + +build:wasm_minimal: + image: trzeci/emscripten + stage: build + variables: + MZNARCH: "wasm" + CMAKE_ARCH: "Unix Makefiles" + tags: [docker] + before_script: *download_script + script: + - sh download.sh vendor master vendor:wasm vendor.zip + - unzip -q vendor.zip + - mkdir -p build + - cd build + - emconfigure cmake -G"$CMAKE_ARCH" -DCMAKE_BUILD_TYPE=MinSizeRel -DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=1 -v .. -DBUILD_REF=$CI_PIPELINE_ID -DUSE_PROPRIETARY=OFF -DCMAKE_INSTALL_PREFIX="$CI_PROJECT_DIR/minizinc" + - cmake --build . --config MinSizeRel + - cmake --build . --config MinSizeRel --target install + artifacts: + paths: [minizinc/] + cache: + key: "$CI_JOB_NAME" + paths: [.ccache, vendor.zip*] + when: manual + +# ----------- Trigger FindMUS pipeline ----------- + +trigger:findmus: + stage: trigger + image: dekker1/minibuild:package + script: + - "curl --request POST --form token=$TRIGGER --form ref=master https://gitlab.com/api/v4/projects/minizinc%2FFindMUS/trigger/pipeline" + dependencies: [] + tags: [linux, docker] + only: [develop] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..548886d --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +*.o +*~ +mzn2fzn +solns2out +makefile +Makefile +include/minizinc/parser.tab.hh +lib/lexer.yy.cpp +lib/parser.tab.cpp +core +build/ +res/ +vendor/ +CMakeCache.txt +CMakeFiles +CMakeScripts +cmake_install.cmake +install_manifest.txt +libminizinc.build +libminizinc.xcodeproj +CMakeLists.txt.user +*.bak +*.out +*.err +.vscode/c_cpp_properties.json +.vscode/ipch diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 0000000..7e0f275 --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,929 @@ +====================== +# MiniZinc Changelog # +====================== + +All bug numbers refer to the issue tracker at +https://github.com/MiniZinc/libminizinc/issues + +Version 2.3.2 +============= + +Changes: +- Add warm starts and subtour cuts to CBC interface. +- Add documentation and assertion requiring that mdds are deterministic, + and add nondeterministic variant of mdd constraint. +- Add -s to the standard flags supported by MIP interfaces. +- Add flag --output-output-item to include user specified output item in + the formatted JSON and DZN output. + +Bug fixes: +- Fix a bug that could leave unused variables in the resulting FlatZinc. +- bounded_dpath should rewrite to fzn_bounded_dpath. Fixes #300. +- Fix definition of sum_set. +- Check if overloaded function required for output. Fixes #303. +- Move regular constraint with set argument to its own file. +- Flatten assignment generators if necessary. +- Simplify fzn_value_precede_chain_int and avoid use of element predicate. + Fixes #307. +- Only initialise par opt variables as absent if they are not arrays. +- Fix the description of the neural_net predicate. +- Fix regular constraint with regular expressions (stopped working in 2.3.0). +- Fix the model interface output to include the same variables as the generated + output statement. +- Fix CSE for removed variable declarations. Could lead to reified constraints + not being compiled correctly when the control variable got fixed to true. + +Version 2.3.1 +============= + +Bug fixes: + +- Report error when trying to assign an array literal to an array variable with incompatible index set. +- Fix partial evaluation of expressions, so that only par expressions are fully evaluated. Fixes #298. +- Remove carriage returns when reading piped solver output on Windows. +- Canonicalize paths of executables to avoid spurious warnings about multiple executables for the same solver. +- Add implementations for != on arrays. +- Compute quotient bounds before decomposition of int_div in linearisation library. +- Propagate domain constraints on variables that are aliased (previously domain constraints could get lost). +- Propagate domain constraints from left-hand-side to right-hand-side in variable assignments. +- piecewise-linear: reuse decomposition for X when only Y-values change. +- nosets: add set_in_imp(var set) and simplify set_in_reif, set_eq(var set, var set). +- linearisation: improved compilation of set_in constraints. + +Version 2.3 +=========== + +Major Changes: + +- The compiler can now generate FlatZinc with half reified constraints. See + https://www.minizinc.org/doc-2.3.0/en/fzn-spec.html#reified-and-half-reified-predicates + for more details. +- The standard library of global constraints has been reorganised, making it + easier for solvers to override just the bits that they support. See + https://www.minizinc.org/doc-2.3.0/en/fzn-spec.html#solver-specific-libraries + for more details. +- There is experimental support for solvers that can read AMPL NL files. + +Minor Changes: + +- The JSON input and output has been improved, with full support for enums and + optional types. +- A new compiler option -g has been added, which turns variable domain changes + into constraints (useful for debugging models). +- The SCIP interface has been updated, with support for indicator constraints, + bounds disjunctions and a native cumulative constraint. +- Error reporting has been improved, with location information available for + errors in par float expressions as well as include items. +- The timeout command line parameter now also applies to compilation itself + (#281). +- Operations on par float values are now checked for overflows. +- The arg_min/arg_max constraints have been improved, with new special versions + for Boolean variables, and a better standard decomposition. +- if-then-else-endif expressions with variable conditions are now compiled to a + predicate call (rather than handled by the compiler), which enables solver + libraries to implement these as native constraints or special decompositions. +- Dividing a variable by a constant is now translated as a multiplication (to + keep the constraints linear). +- A new piecewise_linear predicate has been added to the library to make it + easier to specify piecewise linear constraints. +- Print number of solutions as mzn-stat after solving (#244). +- Make search annotations work for arbitrary array index sets. + +Bug fixes: + +- Translate let expressions that contain constraints or variables as var + type-inst. Fixes #263. +- Fix JSON array parsing by counting elements instead of commas. +- Fix parsing of the -p flag (#271). +- Fix type checking for array declarations with single enum type inst + identifier. E.g. array[$$T] of $U previously matched any multi-dimensional + array, and now only matches one-dimensional arrays with any enum index set. +- Fix computation of function return type when using type inst variables (#272). +- Evaluate each variable declaration only once in a par let expression. +- Check domain constraints on variable declarations in par let expressions. +- Try .exe/.bat on windows when using (constructed) absolute paths. +- Fix array slicing to support empty slices (#275). +- Fix a bug in the parser that could cause crashes on certain syntax errors. +- Fix the type of bool2int for arrays. +- Initialise counter for introduced variable ids based on names in original + model. This avoids reusing variable names if the user model contains names + such as X_INTRODUCED_0_. +- Fix compilation of nested clause/exist constraints, and improve handling of + negation. Tries to use primitive negation instead of creating negated + constraints. Should help with half-reification by creating more positive + contexts. +- Reorder fields in basic data structures to reduce padding on 64 bit platforms + (improved memory footprint). +- Perform type coercion after desugaring array slicing. +- Translate arguments to bool2int, exists, forall in positive context even if + those functions are redefined. +- Don't evaluate par array literals twice (inefficient, and can lead to + incorrect results when using random number generators). +- Terminate child processes when minizinc process is terminated by signal. +- Fix function return value array index check for empty arrays (#286). +- Fix translation of constant false where clause in array comprehension. +- Report error when json multi-dimensional array is not rectangular. +- Check index sets of function arguments (#273). +- Ignore partiality variables from CSE table when compiling _reif and _imp + predicates (#269). +- Flatten comprehensions with variable generators or where conditions before + evaluating any par functions on them (#259). +- Add missing redefinitions of basic operators and search annotations for + optional integers. +- Resolve filenames given on the command line relative to working directory, + and warn if file in working directory has same name as included file from the + library. Fixes #276. +- Update nosets library with a valid redefinition of set_less over booleans. +- Fix translation of showJSON (#294). +- Only apply set2array coercion for supported types, otherwise report error + (#295). +- Improve special case reasoning for abs on strictly negative variables. +- Add bounds for floating point min/max result in the standard library. + + +Version 2.2.3 +============= + +Bug fixes: + - Fix some typos in the library documentation. + - Fix solution checking. + - Fix line numbers in parsed locations on 64 bit platforms. Fixes #255. + - Fix bounds computation for calls. Partially fixes #255. + - Fix translation of var where clauses with more than 3 par components. + Fixes #253. + +Version 2.2.2 +============= + +Changes: + - Some changes to the optimisation phase of the compiler, to take into account + more variables and constraints. + - Preliminary support for MIP cuts based on graph algorithms (only available + when compiled with boost C++ libraries; not part of the binary distribution). + - Set Release as default build type when nothing is specified (for CMake + platforms that do not support multiple build types, like Makefiles). + - Add builtins outputJSON() and outputJSONParameters() for creating an + array of strings that capture the output and parameters of the model as JSON. + - On Linux and macOS, add /usr/share/minizinc/solvers and + /usr/local/share/minizinc/solvers to list of paths where solver configuration files + can be placed. + - Add OSICBC_INCLUDEDIR and OSICBC_LIBDIR cmake flags. Fixes #247. + - Output search paths for solver configurations using --solvers command line option. + - Add support for Gurobi 8.1 + - Support parsing from stdin and files at the same time. Fixes #242. + +Bug fixes: + - Fix crash when flattening top-level array comprehensions with var where + clauses. Fixes #250. + - Support input files with more than 1M lines. Fixes #245. + - Special case handling for array literals in top-level foralls: flatten in root context. + - Fix translation of if-then-else for branches with undefined right hand sides. + - Only propagate defines_var annotation to the variable that's actually being defined + (not others that arise from the same decomposition). + - Don't flatten arguments of predicates like symmetry_breaking_constraint. + - Remove output_var and output_array annotations from user models (these + could cause crashes). + - Fix precedences for weak operators (~+, ~-, ~=, ~*). + - Fix min and max for opt var arrays to work when the bounds of the arrays are unknown. + - Fix a bug in bounds computations for function calls. + - Add missing superset FlatZinc builtin. + - Fix includes in file values.hh for some platforms. + - Fix a garbage collection issue when printing solutions. + - Deal with the case that a variable that's required for output is assigned to a par variable. + - Throw type error when an array has only absent values. Fixes #239. + - Flatten all arrays in FlatZinc, also those coming from functional definitions. Fixes #240. + - Use list of strings as mzn_solver_path entry in the preferences json file. Fixes #241. + - Fix crash when output variable is defined using recursive function + +Version 2.2.1 +============= + +Changes: + - all_different, all_equal, {int,set,float,bool}_search now accept + multi-dimensional arrays. + - Add exponentiation operator (^). + - Improve layout of generated library documentation for some constraints. + - Relax typechecking to allow assignment of empty array ([]) to + multi-dimensional array variables. This is required to make empty + arrays work in JSON data files. + - Enumerated types can now be initialised using lists of strings. + This enables enumerated type support in JSON. + +Bug fixes: + - Cumulative constraint for linear solvers now accepts empty arrays. + - show2d/show3d functions now do not add quotes around array elements and + work for empty arrays. + - Add support for slicing of arrays with enumerated types. Fixes #229. + - Fix slicing of 1d arrays. + - Fix bounds computation for float variable declarations. Fixes #225. + - When FlatZinc solver is terminated due to a timeout, do not report this + as an error. + - Fix pretty-printing of multi-dimensional arrays where dimensions other than + the first one are empty. Fixes #236. + - Add support for where clauses on generator assignment expressions. Fixes #237. + +Version 2.2.0 +============= +This is a major release of MiniZinc, introducing many new features and improvements. + +Major new features: + - New minizinc command line tool + Previous releases contained a minizinc command line tool that was not much more + than a simple script that could execute the compiler, solver and output processor. + The minizinc executable in version 2.2.0 is now the main frontend to compilation and + solving and integrates all of the functionality. It has access to all installed MiniZinc + solvers (both internal solvers and those interfaced through FlatZinc files), and can + automatically select the required options (e.g., to include the solver-specific MiniZinc + globals library). + You can get a list of available solvers using the --solvers command line option, and + select a solver using --solver. The minizinc executable can now also be used as a replacement + for mzn2fzn (using -c) and solns2out (using --ozn-file). + - Multi-pass compilation + The compiler can now perform multiple passes in order to improve the target FlatZinc code. + This can be controlled using the -O command line flags (-O0 to -O4). Multi-pass compilation + is particularly useful when the target solver requires sophisticated decomposition of + global constraints (such as for MIP solvers). + - Solution checking + You can now supply an additional model that will be used to check each solution produced by + your main model. This can be useful for teaching MiniZinc (to give students automatic feedback) + and if your main model is very complex but checking that a solution is correct is easy. + - MIP solvers: support for FICO Xpress, and loading IBM ILOG CPLEX as a plugin + We have added support for FICO Xpress (this requires compiling MiniZinc from sources). + CPLEX can now be loaded as a plugin, which means that the binary distribution of + MiniZinc has built-in CPLEX support (just bring your own CPLEX dll). + - Language extensions + The MiniZinc language has been extended with two new features. + * Array slicing introduces syntax to conveniently select rows, columns or entire slices + of arrays. For example, x[3,..] selects the third row of array x, while x[..,4] selects + the fourth column, and x[3..5,2..7] selects a slice of rows 3 to 5 and columns 2 to 7. + * Generator expressions can now contain multiple where clauses, e.g. + forall (i in S where foo(i), j in T where i then endif" (where is bool) + - Decomposition of one variable type to another (e.g. set into array of bool) + has been improved. + - MIP solvers Gurobi and IBM ILOG CPLEX use node files when over 3GB working memory + - Gurobi and CPLEX support the MIPfocus parameter + - Gurobi supports MiniZinc search annotations by setting fixed branching priorities + +Bug fixes: + - Consult the bug tracker at https://github.com/MiniZinc/libminizinc/issues + +Version 2.1.7 +============= + +Changes: + - Improved linearisation for some element constraints. + - Improve performance of optimisation phase by using a queue instead of a + stack. + - Add --dll option for Gurobi backend to specify the Gurobi DLL to load. + - Add more defines_var annotations. + +Bug fixes: + - Fix generation of variable names in output model (sometimes could contain + duplicates). + - Fix enum type inference for array literals with empty sets as their first + arguments. Fixes #180. + - Fix incorrect simplification of float domain constraints. Fixes #159. + - Fix ceil builtin for float values. + - Add superset decomposition for solvers that do not support set variables. + - Fix three bugs in the garbage collector. + - Fix a bug in flattening that would create duplicate variables when a + variable declaration referred to another one in its type-inst. + - Fix a crash in flattening of partial functions. Fixes #187. + - Add missing deopt builtins for all par types. + - Fix output for arrays of sets of enums. + - Define more functions on par opt types. Fixes #188. + - Fix type checker to accept arrays of opt set values. + - Support printing of opt enum types. Fixes #189. + - Fix evaluation of comprehensions in recursive functions. + - Fix output of Gurobi backend when used in conjunction with solns2out. + - Fix pthread linking for mzn-cbc. + - Catch type error when set literal is declared that contains another set. + +Version 2.1.6 +============= + +Bug fixes: + + - Fully evaluate parameters before binding formal arguments when evaluating + call expressions. Fixes #177. + - Fix incorrect simplification of Boolean constraints assigned to variables + that are assigned to false + - Fix bug in flattening of linear equations that contain the same variable on + both sides + - Fix un-trailing for let expressions, which could sometimes cause incorrect + code to be emitted when lets are evaluated in nested loops. Fixes #166. + - Fix bug in JSON output of one-dimensional array literals + - Fix unification of enum type-inst variables + +Version 2.1.5 +============= + +Changes: + - Some improvements to the linearisation library. + - Make parser read multiple .mzn files correctly. + - Enable better bounds computation for array access expressions on + fixed arrays. + - Perform better constant folding during optimisation phase. Fixes #155. + - Don't rewrite pow function into multiplication in the case of power of 2. + - Save some memory by making certain internal data structures more compact. + - Improve source code location of identifiers in generator calls (should give + more precise error messages). + - Produce an error message when a comprehension attempts to iterate over an + infinite set. + - Produce better error messages for operations on infinite values (previously + some errors did not contain a source code location). + - Speed up garbage collection by pre-allocating some memory. + +Bug fixes: + - Fix range check for float literals in arrays. + - Fix a bug where a constraint could be removed incorrectly. Fixes #150. + - Include variables for dzn and json output from all included models, not + just the main model. Fixes #153. + - Produce multi-dimensional arrays in json output. Fixes #156 and #157. + - Remove incorrect closing bracket from json output. Fixes #154. + - Fix bounds computation of par int and float arrays. + - Don't allow var access to arrays of strings or annotations (since that would + require an element constraint and var string / var ann types). + - Introduce int2float constraints where necessary for some linearisations. + +Version 2.1.4 +============= + +Changes: + - Add warning for MIP solvers that do not support -a option for satisfaction + problems. + - Print introduced variable names with additional underscore to make + debugging FlatZinc easier. Fixes #147. + - Add support for pow function in linearisation library. + - Add support for parallel solving with CBC. + - Flatten top-level conjunctions in the order defined in the model. + +Bug fixes: + - Fix a garbage collection bug that could cause dangling pointers when + expressions were copied. + - Fix type checker to allow empty arrays to be assigned to variables + declared as arrays of enums. + - Fix infeasibility check in MIP translation for some inequality constraints. + - Improved defines_var annotations for reified xor constraints. Fixes #146. + - Fix output of empty integer sets and deal with empty arrays in output models. + - Fix MIP translation when boolean variables were removed due to aliasing. + - Improve corner cases for linearisation of cumulative constraint. + - Properly report undefinedness in par bool expressions. + - Enable some additional constant folding during flattening. Fixes #149. + +Version 2.1.3 +============= + +Changes: + - Remove more internal annotations from the generated FlatZinc. + - Detect failure earlier if optimisation pass leads to fixing of variables + outside their domains. + +Bug fixes: + - Fix CBC backend to correctly print UNSAT message for models where the + compiler already detected unsatisfiability, and print solution separators + even where there is no other output. + - Add missing var_dom function for arrays of optional integer variables. + Fixes #133. + - Fix aliasing for optional integer variables. Fixes #132. + - Remove all annotations from output model. + - Fix computation of return type for functions that return arrays of enums. + - Don't output newline if user-defined solution separator or status message is empty + - Fix return type computation for functions where return type contains + enums. + - Check finiteness of float literals and bounds. Fixes #138. + - More checks for function return values. Fixes #136. + - Fix var int comprehensions (now triggers error message instead of crash + for var set of int comprehensions). Fixes #135. + - Fix output of variables with quoted identifiers. + - Fix flattening of let expressions that contain variables with undefined + (i.e., partial) right hand side. + - Make printing of error messages to stdout or stderr more consistent + across executables. + - Fix type checking of initialisation of enum types. + - Improve error messages for array access and index set errors. Fixes #131. + - Fix definition of multi-dimensional element constraints to impose + correct bounds on index variables. + - Fix binding analysis during type checking, which did not handle the + shadowing of top-level declarations by comprehension generators correctly. + Fixes #129. + +Version 2.1.2 +============= + +Bug fixes: + - Fix a bug in the type checking for generators that iterate over + arrays of enums. + - Fix a bug in the output handling of arrays of enums. + - Fix handling of multiple output items (only the last item was + compiled, now the concatenation is used for output as defined + in the specification). + +Version 2.1.1 +============= + +Changes: + - Add missing min/max functions for set variables. Can be redefined to solver + builtins using the new redefinitions-2.1.1.mzn library file. + - Add support for option type expressions as objective functions. + - Automatically coerce arrays constructed using ++ to any enum index set + (in addition to array literals and comprehensions). + +Bug fixes: + - Include cmath header to fix compilation issues with some compilers. Fixes #125. + - Fix a garbage collection bug in the type checking for enumerated types that would + sometimes lead to crashes or incorrect error messages. + - Fix type checking of comprehensions that involve enumerated types. + - Fix bounds computation for var sets of enumerated types. + - Support anon_enum function as documented. + +Version 2.1.0 +============= + +Changes: + - MiniZinc now supports enumerated types. + - Solvers can be interfaced directly to the MiniZinc library, and MiniZinc + comes with direct support for the CBC, Gurobi and CPLEX MIP solvers. + - The linearisation library has been updated, resulting in much better FlatZinc + being generated for MIP solvers. + - Data files can be in JSON format, and MiniZinc can produce JSON output + (using the --output-mode command line option). + - Variables can be annotated as ::add_to_output instead of writing an output + item. + - The compiler can output information about the parameters and output variables + of a model (using the --model-interface-only option). + - Floats are handled better (detecting infinities and handling sets of floats). + - Bounds can be computed for more expressions (instead of failing with an error + message). + +Bug fixes: + - Fix a bug in optimization that could remove variables even if they are used. + Fixes #123. + - Fix float variable declarations with sets of floats as domains. + Fixes #117 and #98. + - Fix type checking and evaluation of asserts with array arguments. Fixes #109. + - Fix abs(var float) declaration to work on floats without declared bounds. + Fixes #106. + - Fix a bug in the computation of int and float bounds that could result in + incorrect bounds in some cases. Fixes #94. + - Fix garbage collection when creating output models. Fixes #77. + - Fix binary operators on optional variables (in some cases comparison + operators were reversed). + - Fix optimization of unconstrained variables (could sometimes lead to + constraints being removed although they were not subsumed). + + +Version 2.0.14 +============== + +Changes: + + - Less aggressive aggregation of linear expressions in cases where it + leads to much less efficient FlatZinc. + - Don't create temporary variable for an array literal if it is discarded + immediately anyway. + - Only create new partiality variable for if-then-else expression if there's + at least one var condition. + - Replace recursive definitions of array_intersect and array_union with + iterative ones. + +Bug fixes: + - Don't report warnings about partiality when using extended generator + expressions. + - Include cmath to enable building with some versions of gcc. + - Constrain result of function call based on function return type if necessary. + - Make sure linear expressions generated during binding of variables are + properly flattened (including simplification of the linear expression) + + +Version 2.0.13 +============== + +Bug fixes: + + - Fix a bug in the Common Subexpression Elimination table of the compiler, + which could lead to some constraints being dropped (especially when using + linear redefinitions). + - The output model sometimes did not include all required definitions, + in particular when array declarations used identifiers to specify the + dimensions. + - The generated FlatZinc sometimes still contained bool variables that were + not connected to the rest of the model, which could produce incorrect + solutions being printed. + - Fix a bug where warnings (e.g. about partial functions) could lead to + crashes. + - Fix the bounds computation for integer and float variables, which could + produce incorrect bounds for linear expressions. Fixes #94. + +Version 2.0.12 +============== + +Changes: + + - Partial functions are now always evaluated in their Boolean context, + independent of whether they are par or var. If the result of a partial + function is statically known to be undefined (such as division by zero or + array access out of bounds), and it is used in a constraint expression, + this now results in a warning instead of an + error. Warnings can be turned off using the ::maybe_partial annotation. + Fixes #43 and #74. + +Bug fixes: + + - Fix a bug in the optimisation phase related to unification of aliased + variables. + - Fix short-circuit evaluation of Boolean expressions. + - Fix a bug in the optimisation phase related to repeated simplification of + some Boolean expressions. + - Handle errors in output produced by solver without solns2out crashing. + Fixes #80. + - Fix a bug in the integer bounds computation that caused bool2int with an + embedded conditional to crash. + - Fix a problem with short-circuit compilation of == expressions when one + side was a var opt bool. + - Stop compilation when model is failed. Fixes a bug where mzn2fzn would + sometimes not clean up the FlatZinc enough for the solver. + +Version 2.0.11 +============== + +Bug fixes: + + - Fix parsing of hex and octal literals. Fixes #71. + - Fix compilation of extended comprehensions. Fixes #72. + - Fix computation of float array access bounds. + - Fix aggregation of clauses (could sometimes ignore the negative literals). + +Version 2.0.10 +============== + +Bug fixes: + + - Fix a bug in the optimiser that could lead to undefined variables in the + generated FlatZinc. Fixes #70. + +Version 2.0.9 +============= + +Bug fixes: + + - Need to take return type into account when copying functions to output + model. Fixes #55. + - Evaluate calls that result in arrays using eval_arraylit. Fixes #57. + - Move inverse function to its own library file, so that it remains available + when a solver provides an alternative for the inverse predicate. + - Optimisation phase now recursively checks constraints when elements in an + array become fixed. + - Fix CMakeLists file to work for paths that contain spaces. + - Distinguish between infix operators and regular functions in the generated + html documentation. Fixes #61. + - Made parser more robust against incorrect code. + - Fix increment/decrement operators for IntVals and make all operations throw + correct overflow exceptions. + - Fix automatic type coercion for variables declared in let expressions. + - Fix a crash when printing some error messages. + - Fix compute_div_bounds builtin to return correct result for a division by + zero. + - Fix optimisation of Boolean constraints to use pointer equality instead of + structural equality (same expression can occur multiple times in the + FlatZinc). + - Only optimise constraints that have not been removed yet. + - Fix declaration of functional version of bin_packing_load. Fixes #64. + - Set type of arrays returned from polymorphic functions. Fixes #65. + - Fix parsing of quoted unary operator calls. + - Only compute set functions when bounds are valid. Fixes #66. + - Compute proper bounds for if-then-else expressions. + - Report error when no reified version of a constraint is available. Fixes #67. + - Fix type checking of annotations on binary operators. + - Keep annotations when rewriting linear constraints and remove is_defined_var + annotations from fixed variables. Fixes #69. + +Changes: + + - Integer, Boolean and float literals are now cached to achieve better memory + performance for some models. + - Improve performance of parsing integer literals. + - Improve handling of clause constraints. + - Add source files of MiniZinc specification to the repository. + - Limit maximum array size to enable better error messages. + - Add implied_constraint predicate as a synonym for redundant_constraint. + +Version 2.0.8 +============= + +Bug fixes: + + - Fix incorrect negation of some reified comparisons. + - Make lb/ub functions work in more cases. + - Fix several bugs in the optimisation phase (could lead to incorrect + FlatZinc and crashes). + - Fix a problem with reverse mapper functions when the result of the reverse + mapper can be fixed to a constant. + +Version 2.0.7 +============= + +Changes: + - Improved propagation of Boolean constants in the optimisation phase. This + should result in far fewer aliases and improves simplification of + conjunctions, disjunctions and clauses. + - Add special case handling for integer division by 1. + +Bug fixes: + + - Fix FlatZinc generator phase, need to turn all array literal arguments into + 1-based single dimensional arrays. + - Fix compilation of if-then-else expressions with var conditions (which didn't + implement proper partiality/totality semantics). Fixes #42. + - Provide correct bounds for weak opt var arithmetic. Fixes #51. + - Need to be able to handle unflattened annotations. Fixes #53. + - Fix generation of output model (needs to ignore items that have been removed + previously). + - Add missing lb(var set of int) builtin. Fixes #47. + - Check that var set declarations have a finite element type. Fixes #46. + - Fix translation context for binary operators on arrays. + - Need to access IntVal::infinity as a function, otherwise depending on linker + etc it may become 0 in some cases. Fixes #40. + - Change pretty printer to use one less digit when printing float literals. + This fixes #41 (or at least provides a workaround), but some double + constants may still be rounded incorrectly when pretty printing and reading + them back in. The real fix will be to output hex float literals (coming + soon). + - Distinguish between generalised comprehensions (iterating over sets) and + iterating over arrays. Fixes compilation of comprehensions where iteration + over an array is combined with var where clauses. Fixes #45. + - Fix bug in creation of output model where sometimes chains of variable + definitions could lead to crashes. + - Avoi creating mutually recursive definitions in some corner cases, which + could cause the compiler to run into infinite loops. + - Don't copy vardecl items to output model that are already there. Fixes #44. + - Remove domain from array declarations in FlatZinc (avoids problems with + domains where holes need to be removed and when there are infinities in the + domains) + - Fix flattening of equality operator between non-opt and opt vars + - Check that model contains a single solve and output item during type + checking (previously, multiple output items were not detected and resulted + in incorrect .ozn files). + - Fix flattening of xor (arguments need to be in mixed context). + - Use is_fixed in cumulative definition. + - Fix bug where a par right hand side of a variable mentioned in the output + would cause a crash. + - Fix variable dependency tracking during rewriting in the optimisation phase. + Could previously lead to variables being removed that are still required. + Fixes #54. + +Version 2.0.6 +============= + +Changes: + - Add parser support for hexadecimal floating point constants. + +Bug fixes: + - Fix bounds computation for some calls (abs, int_times). + - Fix flattening of some array declarations (when right hand side is + an identifier). + - Add four missing GC locks (could lead to incorrect garbage collection). + - Compact output model only after optimisation phase (could lead to + incorrect items being removed from output model). + +Version 2.0.5 +============= + +Changes: + - Improve the standard decomposition for the cumulative constraint. + - Better handling of binary operators during type checking and flattening, + can sometimes avoid stack overflows (e.g. for large conjunctions). + - Make ++ operator left associative (avoid stack overflows in the parser). + - Add ::domain annotations to linear constraints generated from + multi-dimensional element constraints. + - Greatly improved linearisation library. + +Bug fixes: + - Fix recursive function calls that contain let expressions. + - Fix compilation of comprehensions inside parametric functions. + - Fix a memory leak in solns2out. + - Fix a problem in the evaluation of binary operators. + - Fix a bug in the flattening of array literals. + - Fix a bug that would crash the parser on certain syntax errors in let + expressions. + +Version 2.0.4 +============= + +Changes: + - Models can now be read from standard input (using the "-" or + "--input-from-stdin" command line options). Thanks to Sebastian Kosch. + - Improved handling of bool2int during FlatZinc generation. + +Bug fixes: + - Fix unification of aliased variables which could sometimes result in + variables being removed although had a constraining right hand side. + - Fix evaluation of set comprehensions. + - Fix command line flag --no-output-ozn + - Fix performance problem when evaluating array expressions inside lets. + - Fix flattening of bool_xor redefinitions. + - Fix partial evaluation of some array access expressions with var indexes. + - Fix definition of geost constraint. + - User-defined functions are now copied correctly into the output model + if they are referenced in the output item. + - Set comprehensions are fully evaluated. + +Version 2.0.3 +============= + + (Internal release that did not contain some essential fixes) + +Version 2.0.2 +============= + +Changes: + - The optimiser now removes simple domain constraints from the FlatZinc + - The compiler now checks for integer overflows in all built-in operations + - Report an error when the FlatZinc or ozn file cannot be opened for writing + - Add support for 3d array literals (e.g. [| |1,2|3,4|,|5,6|7,8| |] ) + - Add show2d and show3d functions for formatting array output + - Add row/col functions for variable arrays (github issue #2) + - Introduce builtins for creating random distributions + - Add reverse library function + - Postpone flattening of some reified constraints + - Slightly improved compilation of partial function calls when it can be + inferred at compile time that their result is undefined + - Allow functions with empty argument lists to be declared as + function int: foo(); instead of just function int: foo; + - Improve error reporting, in particular for errors in comprehensions + - Enable expressions a..b where a and b are integer variables + - Add redundant_constraint and symmetry_breaking_constraint builtins, these + can be rewritten by solver libraries to allow e.g. local search solvers to + ignore redundant constraints. + - Improve flattening of predicates that simply return their arguments (makes + the redundant_constraint and symmetry_breaking_constraint predicates work + in more situations). + - Replace command line option --only-range-domains by optional boolean value + so that solver libraries can set the flag directly in their redefinitions + file. + - Stop flattening immediately when a model has been found to contain an + inconsistency. + - Improve flattening of array access expressions, in particular for nested + array accesses that can be combined into a single element constraint + - Add command line option -s or --statistics to print statistics about the + generated FlatZinc + - Improve bounds computation for if-then-else expressions + - Clause arguments are compiled in positive and negative contexts instead of + mixed. That means that predicates that introduce free variables can now + be used in the positive part of a clause. + +Bug fixes: + - Fix simplification of linear expressions, negative coefficients could + sometimes result in incorrect bounds + - Fix bounds computation for unary minus operator + - Add missing par set comparison builtins + - Fix bounds computation for extended comprehension syntax + - Fix a bug in the garbage collector that could sometimes lead to premature + deletion of expressions + - Fix bounds computation for set difference + - Fix duplication of some arrays in the FlatZinc (github issue #3) + - Fix bounds inference for variables bound to empty sets (github bug #3) + - Fix bug in error reporting function, which would sometimes not report the + entire call stack + - Fix the generation of fresh variable names for generator expressions + - Fix subtype check to allow empty arrays as subtype of arrays of sets + - Fix crash when using assert/2 + - Fix bug when function used in output referred to par variable + - Fix bug in type checker, the detection of cyclic definitions was not + correct and could lead to stack overflows + - Fix parser to accept expressions with two consecutive array accesses + (like x[3][4], which are valid MiniZinc if x is an array of sets) + - Fix error reporting when an evaluation error occurs within a comprehension + generator + - Report type error on some ambiguous function calls + - Report type error on var sets with element type other than int + - Report type error when trying to coerce a var set into an array + - Report error when calling function with a value that is outside the + declared parameter bounds + - Fix arg_sort builtin to implement the correct semantics + - Fix sort_by builtin to sort in non-decreasing order, and work with floats + - Fix bug in type checker, now automatic coercions in functions defined with + type variables (like the comparison operators) work correctly + - Check that index sets match for arrays assigned in let expressions + - Fix bug in bounds inference for integer expressions with annotations + - Fix propagation of defines_var annotation to be pushed through calls + - Fix parser to accept empty 2d and 3d array literals + - Fix flattening to remove defines_var annotations with par argument, e.g. + defines_var(2), which could be introduced by the optimisation pass + - Fix output model creation for variables that have been redefined, and + remove more unused variables from the FlatZinc. + - Fix bug in the garbage collector that could result in function items not + being kept alive in rare cases. + + +Version 2.0.1 +============= + +Major bugs and changes: + - Fix optimisation phase, which was previously incorrectly removing variables + - Add support for trigonometric functions (built-ins were missing in 2.0.0) + and pow (var versions were missing) + - Fix equality operator on par arrays + - All expressions in output model are now made par + - Improve bounds computation for float variables + - Fix translation of functions that need automatic coercion of their return + value + - Fix the array_lb and array_ub builtins, which would return incorrect bounds + in some cases + +Minor bugs and changes: + - Add space between "array" and "[" in the pretty printer, to be compatible + with 1.6 output + - Output all par declarations before the var declarations in FlatZinc + - Fix parser, which could sometimes crash on invalid input + - Improve efficiency of bounds computation on some float expressions + - Add special case handling for division by 1 + - Add missing float_times definition to the flatzinc builtins + - Use correct version of var_dom for float variables + - Output information about which files are included in verbose mode + - Only compute bounds for "then" expressions if the "if" is not fixed to false + +Version 2.0.0 +============= + +MiniZinc 2.0 contains many new features and is based on a complete rewrite of +the MiniZinc-to-FlatZinc compiler. If you are currently using the previous +version 1.6, the new tools can be used as drop-in replacements. The generated +FlatZinc is compatible with version 1.6, so all FlatZinc solvers should work +without changes. + +** MiniZinc language changes ** + + - MiniZinc now supports user-defined functions. Details have been published + in the paper "MiniZinc with Functions". Both functions and predicates can + be recursive. + - MiniZinc now supports option types. Details have been published in the + paper "Modelling with Option Types in MiniZinc". + - Let expressions have been generalised. They can now contain constraint + items in addition to variable declarations. + - Array index sets can be declared using arbitrary set expressions as long as + they evaluate to contiguous ranges. + - The if-then-else expression has been generalised to allow the condition to + be a var bool expression (instead of only par bool). + - Array and set comprehensions as well as generator calls can now iterate + over variables and use var bool where conditions. + - Any bool expression can now automatically coerce to an int expression, + likewise for int and float. This means that you don't have to write + bool2int or int2float in your models any more. + - Equality constraints can now be posted between array expressions. + - Arbitrary expressions can now be included ("interpolated") into strings, + using the syntax "some text \(e) some more text", where e is any + expression. It is the same as writing "some text "++show(e)++" some more + text". + +** New built-in functions ** + +Array functions: array1d, arrayXd, row, col, has_index, has_element, sort_by, +sort, arg_sort, arg_min, arg_max + +** New global constraints ** + + - arg_max, arg_min + - arg_sort + - k-dimensional diffn + - disjunctive + - geost + - knapsack + - network_flow + - regular with NFAs + - symmetric all different + - optional scheduling constraints: alternative, span, disjunctive, cumulative + - functional versions of many global constraints + +** New tool chain ** + + - There are a few new builtins that solvers can reimplement, these are listed + in the redefinitions-2.0 file. + - Include items use a different method for finding included files. Paths are + now interpreted as relative to the file that has the include item. That + way, the mzn2fzn compiler can be called from a different working directory. + - A new tool, mzn2doc, can produce html output from the documentation + comments. The MiniZinc distribution contains the documentation for global + constraints and builtins generated directly from the library source code. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..06b7605 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,119 @@ +cmake_minimum_required(VERSION 3.4.0) + +# ------------------------------------------------------------------------------------------------------------------- +# -- Project information and versioning. + +project(libminizinc + VERSION 2.3.2 + LANGUAGES CXX C) + +if(NOT BUILD_REF) + set(BUILD_REF "") +endif() + +# ------------------------------------------------------------------------------------------------------------------- +# -- Project build options + +# Driver compilation selection +option(USE_CPLEX "Enable the CPLEX solving target" FALSE) +option(USE_GEAS "Enable the Geas solving target" TRUE) +option(USE_GECODE "Enable the Gecode solving target" TRUE) +option(USE_GUROBI "Enable the Gurobi solving target" FALSE) +option(USE_OSICBC "Enable the Osi CBC solving target" FALSE) +option(USE_SCIP "Enable the SCIP solving target" FALSE) +option(USE_XPRESS "Enable the Xpress solving target" FALSE) + +# Static vs. Dynamic linking +option(CPLEX_PLUGIN "Build CPLEX binding as a plugin" ON) +option(GUROBI_PLUGIN "Build Gurobi binding as a plugin" ON) + +# Enforce non proprietary build +option(USE_PROPRIETARY "Enable static linking of proprietary solvers" OFF) +if(NOT USE_PROPRIETARY) + set(CPLEX_PLUGIN ON) + set(GUROBI_PLUGIN ON) + set(USE_SCIP OFF) +endif() + +# Export compile commands (useful when working with visual studio code) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# ------------------------------------------------------------------------------------------------------------------- +# -- CMake initialisation + +# Fix library suffixes for Web Assembly platform +include(cmake/support/emscripten_setup.cmake) + +# Try to find possible dependencies +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif(POLICY CMP0074) +find_package(CPlex) +# TODO: Resolve issues with the Gecode / Geas solver interface +# find_package(Geas) +find_package(Gecode 6.0 COMPONENTS Driver Float Int Kernel Minimodel Search Set Support) +find_package(Gurobi) +find_package(OsiCBC) +find_package(SCIP CONFIG) +find_package(Xpress) + +# Set build type when none is selected +set(DEFAULT_BUILD_TYPE "Release") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.") + set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" + "Release" + "MinSizeRel" + "RelWithDebInfo") +endif() + +# ------------------------------------------------------------------------------------------------------------------- +# -- Compiler configuration + +include(cmake/support/ccache_setup.cmake) +include(cmake/support/compiler_setup.cmake) + +configure_file( + ${PROJECT_SOURCE_DIR}/include/minizinc/config.hh.in + ${PROJECT_BINARY_DIR}/include/minizinc/config.hh +) + +install( + FILES ${PROJECT_BINARY_DIR}/include/minizinc/config.hh + DESTINATION include/minizinc +) + +# ------------------------------------------------------------------------------------------------------------------- +# -- MiniZinc compilation targets. + +find_package(Threads REQUIRED) +include_directories(${PROJECT_SOURCE_DIR}/include) +include_directories(${PROJECT_BINARY_DIR}/include) + +# Libraries +include(cmake/targets/libmzn.cmake) +include(cmake/targets/libmza.cmake) + +# Executables +#include(cmake/targets/minizinc.cmake) +#include(cmake/targets/mzn2doc.cmake) +include(cmake/targets/mznasm.cmake) +include(cmake/targets/mzncc.cmake) + +# ------------------------------------------------------------------------------------------------------------------- +# -- Platform Specific configuration +include(cmake/support/config_emscripten.cmake) + +# ------------------------------------------------------------------------------------------------------------------- +# -- CMake configuration generation + +include(cmake/support/config_export.cmake) +include(cmake/support/config_output.cmake) + +# ------------------------------------------------------------------------------------------------------------------- +# -- Support Actions +include(cmake/support/format.cmake) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..3ce2b38 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,382 @@ +If not noted otherwise in individual file headers, all files that make up +the libminizinc distribution are released under the Mozilla Public License +Version 2.0, a copy of which can be found below. + +A notable exception is the file + include/minizinc/thirdparty/SafeInt3.hpp +which is distributed under the Microsoft Public License (Ms-PL), see the +file header for details. + +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..c56f2fc --- /dev/null +++ b/README.txt @@ -0,0 +1,14 @@ +MiniZinc +======== + +This package contains the MiniZinc constraint modelling +language and tool chain. + +For the full documentation, including installation and compilation +instructions, see http://minizinc.org/doc-latest/index.html. + +** Bugs ** + +If you encounter any problems with MiniZinc, please use the MiniZinc discussion +forum https://www.minizinc.org/forum.html and the bug tracker at +https://github.com/MiniZinc/libminizinc/issues to report any issues or feature requests. diff --git a/cmake/modules/FindCPlex.cmake b/cmake/modules/FindCPlex.cmake new file mode 100644 index 0000000..f8bf716 --- /dev/null +++ b/cmake/modules/FindCPlex.cmake @@ -0,0 +1,65 @@ +# - Try to find CPLEX +# Once done this will define +# CPLEX_FOUND - System has CPLEX +# CPLEX_INCLUDE_DIRS - The CPLEX include directories +# CPLEX_LIBRARIES - The libraries needed to use CPLEX +# CPLEX_COMPILE_FLAGS - The definitions required to compile with CPLEX +# User can set CPLEX_ROOT to the preferred installation prefix + +list(INSERT CMAKE_PREFIX_PATH 0 "${CPLEX_ROOT}" "$ENV{CPLEX_ROOT}") + +set(CPLEX_COMPILE_FLAGS "-fPIC -fno-strict-aliasing -fexceptions -DNDEBUG") + +set(CPLEX_VERSIONS 129 128 1271 127 1263 1262 1261 126) + +foreach(VERSION ${CPLEX_VERSIONS}) + list(APPEND CPLEX_DEFAULT_LOC "/opt/ibm/ILOG/CPLEX_Studio${VERSION}") + list(APPEND CPLEX_DEFAULT_LOC "/opt/IBM/ILOG/CPLEX_Studio${VERSION}") + list(APPEND CPLEX_DEFAULT_LOC "C:\\Program Files\\IBM\\ILOG\\CPLEX_Studio${VERSION}") + list(APPEND CPLEX_DEFAULT_LOC "C:\\Program Files (x86)\\IBM\\ILOG\\CPLEX_Studio${VERSION}") + list(APPEND CPLEX_DEFAULT_LOC "$ENV{HOME}/Applications/IBM/ILOG/CPLEX_Studio${VERSION}") + list(APPEND CPLEX_DEFAULT_LOC "/Applications/IBM/ILOG/CPLEX_Studio${VERSION}") + + list(APPEND CPLEX_LIB_NAMES cplex${VERSION}) +endforeach(VERSION) + +find_path(CPLEX_INCLUDE ilcplex/cplex.h + HINTS ${CPLEX_DEFAULT_LOC} + PATH_SUFFIXES include cplex/include) + +if(CPLEX_PLUGIN) + include(CheckIncludeFiles) + # TODO: Cleanup this mess + check_include_files(dlfcn.h HAS_DLFCN_H) + check_include_files(Windows.h HAS_WINDOWS_H) + if(HAS_DLFCN_H) + find_library(CPLEX_LIBRARY dl) + elseif(HAS_WINDOWS_H) + set(CPLEX_LIBRARY ${CPLEX_INCLUDE}) + endif() +else() + foreach(CPLEX_LIB ${CPLEX_LIB_NAMES}) + find_library(CPLEX_LIBRARY NAMES cplex ${CPLEX_LIB} + HINTS ${CPLEX_DEFAULT_LOC} + PATH_SUFFIXES lib/x86-64_linux/static_pic lib/x86-64_osx/static_pic lib/x64_windows_vs2013/stat_mda cplex/lib/x86-64_linux/static_pic cplex/lib/x86-64_osx/static_pic cplex/lib/x64_windows_vs2013/stat_mda) + if(NOT "${CPLEX_LIBRARY}" STREQUAL "CPLEX_LIBRARY-NOTFOUND") + break() + endif() + endforeach(CPLEX_LIB) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set CBC_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(CPlex DEFAULT_MSG + CPLEX_INCLUDE CPLEX_LIBRARY) + +if(CPLEX_PLUGIN AND HAS_WINDOWS_H AND NOT HAS_DLFCN_H) + unset(CPLEX_LIBRARY) +endif() + +mark_as_advanced(CPLEX_INCLUDE CPLEX_LIBRARY) +list(REMOVE_AT CMAKE_PREFIX_PATH 1 0) + +set(CPLEX_LIBRARIES ${CPLEX_LIBRARY}) +set(CPLEX_INCLUDE_DIRS ${CPLEX_INCLUDE}) diff --git a/cmake/modules/FindGeas.cmake b/cmake/modules/FindGeas.cmake new file mode 100644 index 0000000..af3b006 --- /dev/null +++ b/cmake/modules/FindGeas.cmake @@ -0,0 +1,42 @@ +# - Try to find Geas +# Once done this will define +# GEAS_FOUND - System has Geas +# GEAS_INCLUDE_DIRS - The Geas include directories +# GEAS_LIBRARIES - The libraries needed to use Geas +# User can set GEAS_ROOT to the preferred installation prefix +# Imported target Geas will be created for linking purposes + +list(INSERT CMAKE_PREFIX_PATH 0 "${GEAS_ROOT}" "$ENV{GEAS_ROOT}") + +find_path( + GEAS_INCLUDE geas/c/geas.h + PATH_SUFFIXES include +) + +find_library( + GEAS_LIBRARY NAMES geas libgeas + PATH_SUFFIXES lib +) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set GEAS_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(Geas + FOUND_VAR GEAS_FOUND + REQUIRED_VARS GEAS_INCLUDE GEAS_LIBRARY + FAIL_MESSAGE "Could NOT find Geas, use GEAS_ROOT to hint its location" +) + +mark_as_advanced(GEAS_INCLUDE GEAS_LIBRARY) +list(REMOVE_AT CMAKE_PREFIX_PATH 1 0) + +if(GEAS_FOUND) + add_library(Geas UNKNOWN IMPORTED) + set_target_properties(Geas PROPERTIES + IMPORTED_LOCATION ${GEAS_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${GEAS_INCLUDE} + ) +endif() + +set(GEAS_LIBRARIES ${GEAS_LIBRARY}) +set(GEAS_INCLUDE_DIRS ${GEAS_INCLUDE}) diff --git a/cmake/modules/FindGecode.cmake b/cmake/modules/FindGecode.cmake new file mode 100644 index 0000000..d24ba16 --- /dev/null +++ b/cmake/modules/FindGecode.cmake @@ -0,0 +1,83 @@ +# - Try to find Gecode +# Once done this will define +# GECODE_FOUND - System has Gecode +# GECODE_INCLUDE_DIRS - The Gecode include directories +# GECODE_LIBRARIES - The libraries needed to use Gecode +# GECODE_TARGETS - The names of imported targets created for gecode +# User can set GECODE_ROOT to the preferred installation prefix + +list(INSERT CMAKE_PREFIX_PATH 0 "${GECODE_ROOT}" "$ENV{GECODE_ROOT}") + +find_path(GECODE_INCLUDE gecode/kernel.hh + PATH_SUFFIXES include) + +find_file(GECODE_CONFIG_LOC gecode/support/config.hpp + HINTS ${GECODE_INCLUDE} + PATH_SUFFIXES include) + +if(NOT "${GECODE_CONFIG_LOC}" STREQUAL "GECODE_CONFIG_LOC-NOTFOUND") + file(READ "${GECODE_CONFIG_LOC}" GECODE_CONFIG) + string(REGEX MATCH "\#define GECODE_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"" _ "${GECODE_CONFIG}") + set(GECODE_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "\#define GECODE_LIBRARY_VERSION \"([0-9]+-[0-9]+-[0-9]+)\"" _ "${GECODE_CONFIG}") + set(GECODE_LIBRARY_VERSION "${CMAKE_MATCH_1}") + string(REGEX MATCH "\#define GECODE_STATIC_LIBS ([0-9]+)" _ "${GECODE_CONFIG}") + set(GECODE_STATIC_LIBS "${CMAKE_MATCH_1}") + string(REGEX MATCH "\#define GECODE_HAS_GIST" GECODE_HAS_GIST "${GECODE_CONFIG}") + string(REGEX MATCH "\#define GECODE_HAS_MPFR" GECODE_HAS_MPFR "${GECODE_CONFIG}") +endif() + +set(GECODE_COMPONENTS Driver Flatzinc Float Int Kernel Minimodel Search Set Support) +if(GECODE_HAS_GIST) + list(APPEND GECODE_COMPONENTS Gist) +endif() + +foreach(GECODE_COMP ${GECODE_COMPONENTS}) + # Try to find gecode library + string(TOLOWER "gecode${GECODE_COMP}" GECODE_LIB) + set(GECODE_LIB_LOC "GECODE_LIB_LOC-NOTFOUND") + find_library(GECODE_LIB_LOC NAMES ${GECODE_LIB} lib${GECODE_LIB} ${GECODE_LIB}-${GECODE_LIBRARY_VERSION}-r-x64 ${GECODE_LIB}-${GECODE_LIBRARY_VERSION}-d-x64 + HINTS ${GECODE_INCLUDE} + PATH_SUFFIXES lib) + if(NOT "${GECODE_LIB_LOC}" STREQUAL "GECODE_LIB_LOC-NOTFOUND") + list(APPEND GECODE_LIBRARY ${GECODE_LIB_LOC}) + add_library(Gecode::${GECODE_COMP} UNKNOWN IMPORTED) + set_target_properties(Gecode::${GECODE_COMP} PROPERTIES + IMPORTED_LOCATION ${GECODE_LIB_LOC} + INTERFACE_INCLUDE_DIRECTORIES ${GECODE_INCLUDE}) + set(Gecode_FIND_REQUIRED_${GECODE_COMP} TRUE) + set(Gecode_${GECODE_COMP}_FOUND TRUE) + endif() +endforeach(GECODE_COMP) + +if(WIN32 AND GECODE_HAS_GIST AND GECODE_STATIC_LIBS) + find_package(Qt5 QUIET COMPONENTS Core Gui Widgets PrintSupport) + set_target_properties(Gecode::Gist PROPERTIES + INTERFACE_LINK_LIBRARIES "Qt5::Core;Qt5::Gui;Qt5::Widgets;Qt5::PrintSupport") +endif() + +unset(GECODE_REQ_LIBS) +unset(GECODE_LIB_WIN) +unset(GECODE_LIB_LOC) + +if(GECODE_LIBRARY AND GECODE_HAS_MPFR) + find_package(MPFR) + list(APPEND GECODE_LIBRARY ${MPFR_LIBRARIES}) + list(APPEND GECODE_TARGETS ${MPFR_LIBRARIES}) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set GECODE_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args( + Gecode + REQUIRED_VARS GECODE_INCLUDE GECODE_LIBRARY + VERSION_VAR GECODE_VERSION + HANDLE_COMPONENTS +) + +mark_as_advanced(GECODE_INCLUDE GECODE_LIBRARY) +list(REMOVE_AT CMAKE_PREFIX_PATH 1 0) + +set(GECODE_LIBRARIES ${GECODE_LIBRARY}) +set(GECODE_INCLUDE_DIRS ${GECODE_INCLUDE}) diff --git a/cmake/modules/FindGurobi.cmake b/cmake/modules/FindGurobi.cmake new file mode 100644 index 0000000..1a1717a --- /dev/null +++ b/cmake/modules/FindGurobi.cmake @@ -0,0 +1,69 @@ +# - Try to find Gurobi +# Once done this will define +# GUROBI_FOUND - System has GUROBI +# GUROBI_INCLUDE_DIRS - The GUROBI include directories +# GUROBI_LIBRARIES - The libraries needed to use GUROBI +# GUROBI_COMPILE_FLAGS - The definitions required to compile with GUROBI +# User can set GUROBI_ROOT to the preferred installation prefix + +list(INSERT CMAKE_PREFIX_PATH 0 "${GUROBI_ROOT}" "$ENV{GUROBI_ROOT}") + +option(GUROBI_PLUGIN "Build Gurobi binding as a plugin" ON) + +set(GUROBI_COMPILE_FLAGS "-fPIC -fno-strict-aliasing -fexceptions -DNDEBUG") + +set(GUROBI_VERSIONS 811 810 801 752 702) + +foreach(VERSION ${GUROBI_VERSIONS}) + list(APPEND GUROBI_DEFAULT_LOC "/opt/gurobi${VERSION}/linux64") + list(APPEND GUROBI_DEFAULT_LOC "/opt/gurobi${VERSION}/linux64") + list(APPEND GUROBI_DEFAULT_LOC "C:\\gurobi${VERSION}\\win64") + list(APPEND GUROBI_DEFAULT_LOC "C:\\gurobi${VERSION}\\win32") + list(APPEND GUROBI_DEFAULT_LOC "/Library/gurobi${VERSION}/mac64") + + string(SUBSTRING ${VERSION} 0 2 VERSION) + list(APPEND GUROBI_LIB_NAMES gurobi${VERSION}) +endforeach(VERSION) + +find_path(GUROBI_INCLUDE gurobi_c.h + PATHS $ENV{GUROBI_HOME} + HINTS ${GUROBI_DEFAULT_LOC} + PATH_SUFFIXES include) + +if(GUROBI_PLUGIN) + include(CheckIncludeFiles) + # TODO: Cleanup this mess + check_include_files(dlfcn.h HAS_DLFCN_H) + check_include_files(Windows.h HAS_WINDOWS_H) + if(HAS_DLFCN_H) + find_library(GUROBI_LIBRARY dl) + elseif(HAS_WINDOWS_H) + set(GUROBI_LIBRARY ${GUROBI_INCLUDE}) + endif() +else() + foreach(GUROBI_LIB ${GUROBI_LIB_NAMES}) + find_library(GUROBI_LIBRARY NAMES ${GUROBI_LIB} + HINTS $ENV{GUROBI_HOME} + PATHS ${GUROBI_DEFAULT_LOC} + PATH_SUFFIXES lib) + if(NOT "${GUROBI_LIBRARY}" STREQUAL "GUROBI_LIBRARY-NOTFOUND") + break() + endif() + endforeach(GUROBI_LIB) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set CBC_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(Gurobi DEFAULT_MSG + GUROBI_INCLUDE GUROBI_LIBRARY) + +if(GUROBI_PLUGIN AND HAS_WINDOWS_H AND NOT HAS_DLFCN_H) + unset(GUROBI_LIBRARY) +endif() + +mark_as_advanced(GUROBI_INCLUDE GUROBI_LIBRARY) +list(REMOVE_AT CMAKE_PREFIX_PATH 1 0) + +set(GUROBI_LIBRARIES ${GUROBI_LIBRARY}) +set(GUROBI_INCLUDE_DIRS ${GUROBI_INCLUDE}) diff --git a/cmake/modules/FindMPFR.cmake b/cmake/modules/FindMPFR.cmake new file mode 100644 index 0000000..a039b3c --- /dev/null +++ b/cmake/modules/FindMPFR.cmake @@ -0,0 +1,22 @@ +### Try to find MPFR +# Once done this will define +# MPFR_FOUND - System has MPFR +# MPFR_INCLUDE_DIRS - The MPFR include directories +# MPFR_LIBRARIES - The libraries needed to use MPFR + +find_path(MPFR_INCLUDE NAMES mpfr.h + PATHS $ENV{GMPDIR} $ENV{MPFRDIR} ${INCLUDE_INSTALL_DIR}) + +find_library(MPFR_LIBRARY mpfr + PATHS $ENV{GMPDIR} $ENV{MPFRDIR} ${LIB_INSTALL_DIR}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set MPFR_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(MPFR DEFAULT_MSG + MPFR_LIBRARY MPFR_INCLUDE) + +mark_as_advanced(MPFR_INCLUDE MPFR_LIBRARY) + +set(MPFR_INCLUDES ${MPFR_INCLUDE}) +set(MPFR_LIBRARIES ${MPFR_LIBRARY}) diff --git a/cmake/modules/FindOsiCBC.cmake b/cmake/modules/FindOsiCBC.cmake new file mode 100644 index 0000000..3d84746 --- /dev/null +++ b/cmake/modules/FindOsiCBC.cmake @@ -0,0 +1,81 @@ +# - Try to find CBC +# Once done this will define +# OSICBC_FOUND - System has CBC +# OSICBC_INCLUDE_DIRS - The CBC include directories +# OSICBC_LIBRARIES - The libraries needed to use CBC +# GOSICBC_TARGETS - The names of imported targets created for CBC +# User can set OSICBC_ROOT to the preferred installation prefix + +list(INSERT CMAKE_PREFIX_PATH 0 "${OSICBC_ROOT}" "$ENV{OSICBC_ROOT}") + +set(OSICBC_FIND_FILES coin/CbcSolver.hpp coin/CglPreProcess.hpp coin/ClpConfig.h coin/CoinSignal.hpp coin/OsiClpSolverInterface.hpp coin/OsiSolverInterface.hpp) + +foreach(OSICBC_FILE ${OSICBC_FIND_FILES}) + set(OSICBC_FILE_LOC "OSICBC_LIB_LOC-NOTFOUND") + find_path(OSICBC_FILE_LOC ${OSICBC_FILE} + PATH_SUFFIXES cbc cgl clp coinutils osi include) + if("${OSICBC_FILE_LOC}" STREQUAL "OSICBC_FILE_LOC-NOTFOUND") +# message(STATUS "OsiCBC: Could not find file `${OSICBC_FILE}`") + set(OSICBC_INCLUDE "") + break() + endif() + list(APPEND OSICBC_INCLUDE ${OSICBC_FILE_LOC}) + # Add "/coin" for CBC internal dependencies + list(APPEND OSICBC_INCLUDE ${OSICBC_FILE_LOC}/coin) +endforeach(OSICBC_FILE) + +list(REMOVE_DUPLICATES OSICBC_INCLUDE) +unset(OSICBC_FIND_FILES) +unset(OSICBC_FILE_LOC) + +if(WIN32 AND NOT UNIX) + set(OSICBC_REQ_LIBS Osi OsiClp OsiCbc Clp Cgl Cbc CbcSolver CoinUtils) +else() + set(OSICBC_REQ_LIBS CbcSolver Cbc Cgl OsiClp Clp Osi CoinUtils) +endif() + +foreach(OSICBC_LIB ${OSICBC_REQ_LIBS}) + set(OSICBC_LIB_LOC "OSICBC_LIB_LOC-NOTFOUND") + find_library(OSICBC_LIB_LOC NAMES ${OSICBC_LIB} lib${OSICBC_LIB} + PATH_SUFFIXES lib) + if("${OSICBC_LIB_LOC}" STREQUAL "OSICBC_LIB_LOC-NOTFOUND") +# message(STATUS "OsiCBC: Could not find library `${OSICBC_LIB}`") + set(OSICBC_LIBRARY "") + break() + endif() + list(APPEND OSICBC_LIBRARY ${OSICBC_LIB_LOC}) + add_library(${OSICBC_LIB} UNKNOWN IMPORTED) + set_target_properties(${OSICBC_LIB} PROPERTIES + IMPORTED_LOCATION ${OSICBC_LIB_LOC} + INTERFACE_INCLUDE_DIRECTORIES "${OSICBC_INCLUDE}") + list(APPEND OSICBC_TARGETS ${OSICBC_LIB}) +endforeach(OSICBC_LIB) + +unset(OSICBC_REQ_LIBS) +unset(OSICBC_LIB_LOC) + +if(UNIX AND NOT WIN32 AND NOT DEFINED EMSCRIPTEN) + find_package(ZLIB) + if(NOT ZLIB_FOUND) + message(STATUS "OsiCBC: Missing dependency `Zlib`") + set(OSICBC_LIBRARY "") + else() + list(APPEND OSICBC_LIBRARY ${ZLIB_LIBRARIES}) + list(APPEND OSICBC_TARGETS ${ZLIB_LIBRARIES}) + endif() +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set CBC_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(OsiCBC + FOUND_VAR OSICBC_FOUND + REQUIRED_VARS OSICBC_INCLUDE OSICBC_LIBRARY + FAIL_MESSAGE "Could NOT find OsiCBC, use OSICBC_ROOT to hint its location" +) + +mark_as_advanced(OSICBC_INCLUDE OSICBC_LIBRARY) +list(REMOVE_AT CMAKE_PREFIX_PATH 1 0) + +set(OSICBC_LIBRARIES ${OSICBC_LIBRARY}) +set(OSICBC_INCLUDE_DIRS ${OSICBC_INCLUDE}) diff --git a/cmake/modules/FindXpress.cmake b/cmake/modules/FindXpress.cmake new file mode 100644 index 0000000..441f051 --- /dev/null +++ b/cmake/modules/FindXpress.cmake @@ -0,0 +1,43 @@ +# - Try to find FICO Xpress +# Once done this will define +# XPRESS_FOUND - System has FICO Xpress +# XPRESS_INCLUDE_DIRS - The FICO Xpress include directories +# XPRESS_LIBRARIES - The libraries needed to use FICO Xpress +# User can set XPRESS_ROOT to the preferred installation prefix + +list(INSERT CMAKE_PREFIX_PATH 0 "${XPRESS_ROOT}" "$ENV{XPRESS_ROOT}") + +#TODO: Check default installation locations +find_path(XPRESS_INCLUDE xprs.h + PATHS $ENV{XPRESSDIR} $ENV{XPRESS} $ENV{XPRESS_DIR} + HINTS /opt/xpressmp C:/xpressmp + PATH_SUFFIXES include) + +foreach(XPRESS_LIB xprb xprs) + set(XPRESS_LIB_LOC "XPRESS_LIB_LOC-NOTFOUND") + find_library(XPRESS_LIB_LOC NAMES ${XPRESS_LIB} lib${XPRESS_LIB} + PATHS $ENV{XPRESSDIR} $ENV{XPRESS} $ENV{XPRESS_DIR} + HINTS /opt/xpressmp C:/xpressmp + PATH_SUFFIXES lib) + if("${XPRESS_LIB_LOC}" STREQUAL "XPRESS_LIB_LOC-NOTFOUND") +# message(STATUS "FICO Xpres: Could not find library `${XPRESS_LIB}`") + set(XPRESS_LIBRARY "") + break() + endif() + list(APPEND XPRESS_LIBRARY ${XPRESS_LIB_LOC}) +endforeach(XPRESS_LIB) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set XPRESS_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(Xpress + FOUND_VAR XPRESS_FOUND + REQUIRED_VARS XPRESS_INCLUDE XPRESS_LIBRARY + FAIL_MESSAGE "Could NOT find Xpress, use XPRESS_ROOT to hint its location" +) + +mark_as_advanced(XPRESS_INCLUDE XPRESS_LIBRARY) +list(REMOVE_AT CMAKE_PREFIX_PATH 1 0) + +set(XPRESS_LIBRARIES ${XPRESS_LIBRARY}) +set(XPRESS_INCLUDE_DIRS ${XPRESS_INCLUDE}) diff --git a/cmake/scripts/md5_gen.cmake b/cmake/scripts/md5_gen.cmake new file mode 100644 index 0000000..8907b64 --- /dev/null +++ b/cmake/scripts/md5_gen.cmake @@ -0,0 +1,56 @@ +# Script to update the cached lexer and parser +# Run after modifying lexer.lxx and/or parser.yxx +# +# 1. generate lexer and parser by building libminizinc +# 2. run cmake -DPROJECT_SOURCE_DIR=. -DPROJECT_BINARY_DIR=build -P cmake/scripts/md5_gen.cmake +# replacing . and build with the source and binary directory +# add -DFORCE=ON to force the renewing of generated files when md5's still match. + +option(FORCE "Force the renewing of generated files" OFF) + +macro(MD5 filename md5sum) + file(READ "${filename}" RAW_MD5_FILE) + string(REGEX REPLACE "\r" "" STRIPPED_MD5_FILE "${RAW_MD5_FILE}") + string(MD5 ${md5sum} "${STRIPPED_MD5_FILE}") +endmacro(MD5) + +# When updating the cached files, update MD5 sums defined in this file +include(${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake) + +MD5("${PROJECT_SOURCE_DIR}/lib/lexer.lxx" lexer_lxx_md5) +MD5("${PROJECT_SOURCE_DIR}/lib/parser.yxx" parser_yxx_md5) +MD5("${PROJECT_SOURCE_DIR}/lib/support/regex/lexer.lxx" regex_lexer_lxx_md5) +MD5("${PROJECT_SOURCE_DIR}/lib/support/regex/parser.yxx" regex_parser_yxx_md5) +MD5("${PROJECT_SOURCE_DIR}/lib/support/assembly/lexer.lxx" mza_lexer_lxx_md5) +MD5("${PROJECT_SOURCE_DIR}/lib/support/assembly/parser.yxx" mza_parser_yxx_md5) + + +if(FORCE OR (NOT "${lexer_lxx_md5}" STREQUAL "${lexer_lxx_md5_cached}")) + file(COPY "${PROJECT_BINARY_DIR}/lexer.yy.cpp" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached") +endif() +if(FORCE OR (NOT "${parser_yxx_md5}" STREQUAL "${parser_yxx_md5_cached}")) + file(COPY "${PROJECT_BINARY_DIR}/parser.tab.cpp" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached") + file(COPY "${PROJECT_BINARY_DIR}/include/minizinc/parser.tab.hh" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached/minizinc") +endif() +if(FORCE OR (NOT "${regex_lexer_lxx_md5}" STREQUAL "${regex_lexer_lxx_md5_cached}")) + file(COPY "${PROJECT_BINARY_DIR}/regex_lexer.yy.cpp" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached") +endif() +if(FORCE OR (NOT "${regex_parser_yxx_md5}" STREQUAL "${regex_parser_yxx_md5_cached}")) + file(COPY "${PROJECT_BINARY_DIR}/regex_parser.tab.cpp" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached") + file(COPY "${PROJECT_BINARY_DIR}/include/minizinc/support/regex_parser.tab.hh" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached/minizinc/support") +endif() +if(FORCE OR (NOT "${mza_lexer_lxx_md5}" STREQUAL "${mza_lexer_lxx_md5_cached}")) + file(COPY "${PROJECT_BINARY_DIR}/mza_lexer.yy.cpp" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached") +endif() +if(FORCE OR (NOT "${mza_parser_yxx_md5}" STREQUAL "${mza_parser_yxx_md5_cached}")) + file(COPY "${PROJECT_BINARY_DIR}/mza_parser.tab.cpp" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached") + file(COPY "${PROJECT_BINARY_DIR}/include/minizinc/support/mza_parser.tab.hh" DESTINATION "${PROJECT_SOURCE_DIR}/lib/cached/minizinc/support") +endif() + +set(MD5_TEMPLATE "set(lexer_lxx_md5_cached \"${lexer_lxx_md5}\")") +set(MD5_TEMPLATE "${MD5_TEMPLATE}\nset(parser_yxx_md5_cached \"${parser_yxx_md5}\")") +set(MD5_TEMPLATE "${MD5_TEMPLATE}\nset(regex_lexer_lxx_md5_cached \"${regex_lexer_lxx_md5}\")") +set(MD5_TEMPLATE "${MD5_TEMPLATE}\nset(regex_parser_yxx_md5_cached \"${regex_parser_yxx_md5}\")") +set(MD5_TEMPLATE "${MD5_TEMPLATE}\nset(mza_lexer_lxx_md5_cached \"${mza_lexer_lxx_md5}\")") +set(MD5_TEMPLATE "${MD5_TEMPLATE}\nset(mza_parser_yxx_md5_cached \"${mza_parser_yxx_md5}\")") +file(WRITE "${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" ${MD5_TEMPLATE}) diff --git a/cmake/support/ccache_setup.cmake b/cmake/support/ccache_setup.cmake new file mode 100644 index 0000000..fac062a --- /dev/null +++ b/cmake/support/ccache_setup.cmake @@ -0,0 +1,25 @@ +option(USE_CCACHE "Use ccache to speed up compilation when found on the system" TRUE) + +find_program(CCACHE_PROGRAM ccache) +if(USE_CCACHE AND CCACHE_PROGRAM) + # Support Unix Makefiles and Ninja + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") + + # Support for Xcode + get_property(RULE_LAUNCH_COMPILE GLOBAL PROPERTY RULE_LAUNCH_COMPILE) + if(RULE_LAUNCH_COMPILE AND CMAKE_GENERATOR STREQUAL "Xcode") + # Set up wrapper scripts + configure_file(cmake/templates/launch-c.in launch-c) + configure_file(cmake/templates/launch-cxx.in launch-cxx) + execute_process(COMMAND chmod a+rx + "${CMAKE_BINARY_DIR}/launch-c" + "${CMAKE_BINARY_DIR}/launch-cxx") + + # Set Xcode project attributes to route compilation through our scripts + set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_BINARY_DIR}/launch-c") + set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_BINARY_DIR}/launch-cxx") + set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_BINARY_DIR}/launch-c") + set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_BINARY_DIR}/launch-cxx") + endif() +endif() + diff --git a/cmake/support/compiler_setup.cmake b/cmake/support/compiler_setup.cmake new file mode 100644 index 0000000..6b1adb0 --- /dev/null +++ b/cmake/support/compiler_setup.cmake @@ -0,0 +1,72 @@ +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +option(USE_ADDRESS_SANITIZER "Use GCC Address Sanitizer" OFF) +if(USE_ADDRESS_SANITIZER) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") +endif() + +if(APPLE) + execute_process(COMMAND xcrun --show-sdk-path OUTPUT_VARIABLE OSX_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_OSX_SYSROOT ${OSX_SYSROOT}) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") +endif(APPLE) + +set(CMAKE_REQUIRED_QUIET $) + +include(CheckCXXCompilerFlag) + +set(SAFE_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") +check_cxx_compiler_flag(-Werror HAS_WERROR) + +if(HAS_WERROR) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") +endif() + +check_cxx_source_compiles("int main(void) { static __thread int x; (void)x; return 0;}" HAS_ATTR_THREAD) + +if(NOT HAS_ATTR_THREAD) + check_cxx_source_compiles("int main(void) { __declspec(thread) static int x; (void)x; return 0;}" HAS_DECLSPEC_THREAD) +endif() + +check_cxx_source_compiles("#include +int main(void) { long long int x = atoll(\"123\"); (void)x; }" HAS_ATOLL) +check_cxx_source_compiles(" +#include +#include +#include +#include +#include +#include + +int main (int argc, char* argv[]) +{ + pid_t pid = getpid(); + char path[PROC_PIDPATHINFO_MAXSIZE]; + (void) proc_pidpath (pid, path, sizeof(path)); + return 0; +} +" HAS_PIDPATH) + +check_cxx_source_compiles(" +#include +int main (int argc, char* argv[]) { + char path[MAX_PATH]; + (void) GetModuleFileName(NULL, path, MAX_PATH); + return 0; +}" HAS_GETMODULEFILENAME) + +check_cxx_source_compiles(" +#include +int main (int argc, char* argv[]) { + (void) GetFileAttributes(NULL); + return 0; +}" HAS_GETFILEATTRIBUTES) + +check_cxx_source_compiles(" +#include +int main (int argc, char* argv[]) { + (void) memcpy_s(NULL,0,NULL,0); + return 0; +}" HAS_MEMCPY_S) \ No newline at end of file diff --git a/cmake/support/config_emscripten.cmake b/cmake/support/config_emscripten.cmake new file mode 100644 index 0000000..36d33ed --- /dev/null +++ b/cmake/support/config_emscripten.cmake @@ -0,0 +1,58 @@ +if (DEFINED EMSCRIPTEN) + add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/CMakeFiles/file_packager.js + COMMAND python ${EMSCRIPTEN_ROOT_PATH}/tools/file_packager.py minizinc.data --lz4 --preload ${PROJECT_SOURCE_DIR}/share@/minizinc --from-emcc --js-output=${PROJECT_BINARY_DIR}/CMakeFiles/file_packager.js + COMMENT "building data store minizinc.data") + + set(EMSCRIPTEN_CXX_FLAGS "-s MINIZ_NO_ARCHIVE_APIS -s MINIZ_NO_ZLIB_APIS") + set(EMSCRIPTEN_LINK_FLAGS " -s FORCE_FILESYSTEM=1 -s LZ4=1 -s MODULARIZE=1 -s EXTRA_EXPORTED_RUNTIME_METHODS=\"['cwrap', 'FS', 'ENV']\" -s DISABLE_EXCEPTION_CATCHING=0 -s BINARYEN_TRAP_MODE=\"clamp\" -s ALLOW_MEMORY_GROWTH=1 --no-heap-copy") + + # ------------------------------------------------------------------------------------------------------------------- + # -- Web Assembly Configuration. + + # MiniZinc main executable + em_link_pre_js(minizinc ${PROJECT_SOURCE_DIR}/cmake/support/emscripten_file_packager_patch.js) + em_link_pre_js(minizinc ${PROJECT_BINARY_DIR}/CMakeFiles/file_packager.js) + + set_target_properties(minizinc PROPERTIES CXX_FLAGS ${EMSCRIPTEN_CXX_FLAGS}) + set_target_properties(minizinc PROPERTIES LINK_FLAGS "-s WASM=1 -s EXPORT_NAME=\"'MINIZINC'\" ${EMSCRIPTEN_LINK_FLAGS}") + + install(FILES ${PROJECT_BINARY_DIR}/minizinc.wasm DESTINATION bin) + install(FILES ${PROJECT_BINARY_DIR}/minizinc.data DESTINATION bin) + + # mzn2doc executable + em_link_pre_js(mzn2doc ${PROJECT_SOURCE_DIR}/cmake/support/emscripten_file_packager_patch.js) + em_link_pre_js(mzn2doc ${PROJECT_BINARY_DIR}/CMakeFiles/file_packager.js) + + set_target_properties(mzn2doc PROPERTIES CXX_FLAGS ${EMSCRIPTEN_CXX_FLAGS}) + set_target_properties(mzn2doc PROPERTIES LINK_FLAGS "-s WASM=1 -s EXPORT_NAME=\"'MZN2DOC'\" ${EMSCRIPTEN_LINK_FLAGS}") + install(FILES ${PROJECT_BINARY_DIR}/mzn2doc.wasm DESTINATION bin) + + # ------------------------------------------------------------------------------------------------------------------- + # -- ASM.js (JavaScript) Configuration. + + # MiniZinc main executable + add_executable(minizinc_asm minizinc.cpp) + target_link_libraries(minizinc_asm mzn) + + em_link_pre_js(minizinc_asm ${PROJECT_SOURCE_DIR}/cmake/support/emscripten_file_packager_patch.js) + em_link_pre_js(minizinc_asm ${PROJECT_BINARY_DIR}/CMakeFiles/file_packager.js) + + set_target_properties(minizinc_asm PROPERTIES CXX_FLAGS ${EMSCRIPTEN_CXX_FLAGS}) + set_target_properties(minizinc_asm PROPERTIES LINK_FLAGS "-s WASM=0 -s EXPORT_NAME=\"'MINIZINC'\" ${EMSCRIPTEN_LINK_FLAGS}") + + install(TARGETS minizinc_asm RUNTIME DESTINATION bin) + install(FILES $.mem DESTINATION bin) + + # mzn2doc executable + add_executable(mzn2doc_asm mzn2doc.cpp) + target_link_libraries(mzn2doc_asm mzn) + + em_link_pre_js(mzn2doc_asm ${PROJECT_SOURCE_DIR}/cmake/support/emscripten_file_packager_patch.js) + em_link_pre_js(mzn2doc_asm ${PROJECT_BINARY_DIR}/CMakeFiles/file_packager.js) + + set_target_properties(mzn2doc_asm PROPERTIES CXX_FLAGS ${EMSCRIPTEN_CXX_FLAGS}) + set_target_properties(mzn2doc_asm PROPERTIES LINK_FLAGS "-s WASM=0 -s EXPORT_NAME=\"'MZN2DOC'\" ${EMSCRIPTEN_LINK_FLAGS}") + + install(TARGETS mzn2doc_asm RUNTIME DESTINATION bin) + install(FILES $.mem DESTINATION bin) +endif() diff --git a/cmake/support/config_export.cmake b/cmake/support/config_export.cmake new file mode 100644 index 0000000..2721c3e --- /dev/null +++ b/cmake/support/config_export.cmake @@ -0,0 +1,91 @@ +### Export of a CMake configuration for MiniZinc +## This allows for "find_package(minizinc)" + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR CMake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/cmake) +endif() +set(INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") + +if(NOT IS_ABSOLUTE "${INSTALL_CMAKE_DIR}") + set(INSTALL_CMAKE_DIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_CMAKE_DIR}") +endif() + +file(RELATIVE_PATH REL_CMAKE_DIR "${CMAKE_INSTALL_PREFIX}" + "${INSTALL_CMAKE_DIR}") +file(RELATIVE_PATH REL_INCLUDE_DIR "${INSTALL_CMAKE_DIR}" + "${CMAKE_INSTALL_PREFIX}/include") + +# Add external (static) dependencies +if(TARGET minizinc_geas) + install( + FILES cmake/modules/FindGeas.cmake + DESTINATION ${REL_CMAKE_DIR} + COMPONENT dev + ) + set(CONF_DEPENDENCIES "${CONF_DEPENDENCIES}find_dependency(Geas)\n") +endif() +if(TARGET minizinc_gecode) + install( + FILES cmake/modules/FindGecode.cmake cmake/modules/FindMPFR.cmake + DESTINATION ${REL_CMAKE_DIR} + COMPONENT dev + ) + if(GECODE_HAS_GIST) + set(_CONF_GIST " Gist") + endif() + set(CONF_DEPENDENCIES "${CONF_DEPENDENCIES}find_dependency(Gecode 6.0 COMPONENTS Driver Float Int Kernel Minimodel Search Set Support${_CONF_GIST})\n") +endif() +if(TARGET minizinc_osicbc) + install( + FILES cmake/modules/FindOsiCBC.cmake + DESTINATION ${REL_CMAKE_DIR} + COMPONENT dev + ) + set(CONF_DEPENDENCIES "${CONF_DEPENDENCIES}find_dependency(OsiCBC)\n") +endif() + +# Add all targets to the build-tree export set +export(TARGETS mzn + FILE "${PROJECT_BINARY_DIR}/libminizincTargets.cmake") + +# Export the package for use from the build-tree +# (this registers the build-tree with a global CMake-registry) +export(PACKAGE libminizinc) + +# Create the libminizincConfig.cmake and libminizincConfigVersion files +# ... for the build tree +set(CONF_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}" "${PROJECT_BINARY_DIR}") +configure_file( + cmake/templates/libminizincConfig.cmake.in + "${PROJECT_BINARY_DIR}/libminizincConfig.cmake" + @ONLY +) +# ... for the install tree +set(CONF_INCLUDE_DIRS "\${libminizinc_CMAKE_DIR}/${REL_INCLUDE_DIR}") +configure_file( + cmake/templates/libminizincConfig.cmake.in + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/libminizincConfig.cmake" + @ONLY +) +# ... for both +configure_file( + cmake/templates/libminizincConfigVersion.cmake.in + "${PROJECT_BINARY_DIR}/libminizincConfigVersion.cmake" + @ONLY +) + +# Install the libminizincConfig.cmake and libminizincConfigVersion.cmake +install( + FILES "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/libminizincConfig.cmake" "${PROJECT_BINARY_DIR}/libminizincConfigVersion.cmake" + DESTINATION ${REL_CMAKE_DIR} + COMPONENT dev +) + +# Install the export set for use with the install-tree +install( + EXPORT libminizincTargets + DESTINATION ${REL_CMAKE_DIR} + COMPONENT dev +) diff --git a/cmake/support/config_output.cmake b/cmake/support/config_output.cmake new file mode 100644 index 0000000..310352d --- /dev/null +++ b/cmake/support/config_output.cmake @@ -0,0 +1,35 @@ +message("\n----- MiniZinc build configuration ----") +if(BUILD_REF) + set(STR_BUILD_REF "build ${BUILD_REF}") +endif() +message("MiniZinc version: ${libminizinc_VERSION} ${STR_BUILD_REF}") +message("Enabled drivers:") + +if(TARGET minizinc_cplex) + if(CPLEX_PLUGIN) + set(STR_CPLEX_PLUGIN " PLUGIN") + endif() + message("\tCPLEX${STR_CPLEX_PLUGIN}: ${CPLEX_INCLUDE_DIRS}") +endif() +if(TARGET minizinc_geas) + message("\tGeas: ${GEAS_INCLUDE_DIRS}") +endif() +if(TARGET minizinc_gecode) + message("\tGecode ${GECODE_VERSION}: ${GECODE_INCLUDE_DIRS}") +endif() +if(TARGET minizinc_gurobi) + if(GUROBI_PLUGIN) + set(STR_GUROBI_PLUGIN " PLUGIN") + endif() + message("\tGurobi${STR_GUROBI_PLUGIN}: ${GUROBI_INCLUDE_DIRS}") +endif() +if(TARGET minizinc_osicbc) + message("\tOSICBC: ${OSICBC_INCLUDE_DIRS}") +endif() +if(TARGET minizinc_scip) + message("\tSCIP: ${SCIP_INCLUDE_DIRS}") +endif() +if(TARGET minizinc_xpress) + message("\tXPress: ${XPRESS_INCLUDE_DIRS}") +endif() +message("---------------------------------------\n") \ No newline at end of file diff --git a/cmake/support/emscripten_file_packager_patch.js b/cmake/support/emscripten_file_packager_patch.js new file mode 100644 index 0000000..a673ba3 --- /dev/null +++ b/cmake/support/emscripten_file_packager_patch.js @@ -0,0 +1,44 @@ + +function filePackagerPatch_getPreloadedPackageNode(filename) { + var fs = require('fs'); + var path = require('path'); + filename = path.normalize(path.resolve(__dirname, filename)); + const buf = fs.readFileSync(filename); + // convert to ArrayBuffer + return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); +} + +function filePackagerPatch_isNodeOrShell() { + var ENVIRONMENT_IS_WEB = typeof window === 'object'; + var ENVIRONMENT_IS_WORKER = typeof importScripts === 'function'; + var ENVIRONMENT_IS_NODE = typeof process === 'object' && typeof require === 'function' && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER; + var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER; + return ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL; +} + +if (typeof location === 'undefined') { + // create a fake location to overrule the file_packager + var location = { + pathname: '/' + }; +} + +if (filePackagerPatch_isNodeOrShell()) { + Module.getPreloadedPackage = Module.getPreloadedPackage || filePackagerPatch_getPreloadedPackageNode; +} else { + // need a hack to locate relative file in browser settings for the file packager + var wrappee = Module.locateFile || function (path, prefix) {return prefix + path}; + Module.locateFile = function (path, prefix) { + if (prefix || !path.endsWith('.data')) { + return wrappee(path, prefix); + } + // file packager is called before a proper script location decection + var base = _scriptDir ? _scriptDir : (typeof importScripts === 'function' ? self.location.href : (typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : '')); + if (base.indexOf('blob:') !== 0) { + base = base.substr(0, base.lastIndexOf('/') + 1); + } else { + base = ''; + } + return wrappee(path, base); + }; +} diff --git a/cmake/support/emscripten_setup.cmake b/cmake/support/emscripten_setup.cmake new file mode 100644 index 0000000..4c97c8d --- /dev/null +++ b/cmake/support/emscripten_setup.cmake @@ -0,0 +1,8 @@ +# Workaround for bug in emscripten cmake: +# add .bc (and on macOS .dylib) as library suffixes +if (DEFINED EMSCRIPTEN) + list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".bc") + if (CMAKE_HOST_APPLE) + list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES ".dylib") + endif() +endif() diff --git a/cmake/support/format.cmake b/cmake/support/format.cmake new file mode 100644 index 0000000..bc77c2d --- /dev/null +++ b/cmake/support/format.cmake @@ -0,0 +1,27 @@ + +if (NOT CLANG_FORMAT_EXECUTABLE) + find_program(CLANG_FORMAT_EXECUTABLE NAMES clang-format clang-format-11 clang-format-10 clang-format-9) +endif() + +set(CLANG_FORMAT_FLAGS "-i" CACHE STRING "Flags passed to the clang-format executable") + +if (CLANG_FORMAT_EXECUTABLE) + file(GLOB_RECURSE FORMAT_FILES + ${PROJECT_SOURCE_DIR}/*.c + ${PROJECT_SOURCE_DIR}/*.cpp + ${PROJECT_SOURCE_DIR}/*.h + ${PROJECT_SOURCE_DIR}/*.hh + ${PROJECT_SOURCE_DIR}/*.hpp + ) + list(FILTER FORMAT_FILES EXCLUDE REGEX ${PROJECT_BINARY_DIR}/*) + list(FILTER FORMAT_FILES EXCLUDE REGEX ${PROJECT_SOURCE_DIR}/lib/cached/*) + list(FILTER FORMAT_FILES EXCLUDE REGEX ${PROJECT_SOURCE_DIR}/lib/thirdparty/*) + list(FILTER FORMAT_FILES EXCLUDE REGEX ${PROJECT_SOURCE_DIR}/include/minizinc/thirdparty/*) + + separate_arguments(CLANG_FORMAT_FLAGS_LIST NATIVE_COMMAND ${CLANG_FORMAT_FLAGS}) + add_custom_target(format + COMMAND ${CLANG_FORMAT_EXECUTABLE} ${CLANG_FORMAT_FLAGS_LIST} ${FORMAT_FILES} + COMMENT "Running ${CLANG_FORMAT_EXECUTABLE} on all source files" + ) +endif() + diff --git a/cmake/targets/libminizinc_cplex.cmake b/cmake/targets/libminizinc_cplex.cmake new file mode 100644 index 0000000..7082ed0 --- /dev/null +++ b/cmake/targets/libminizinc_cplex.cmake @@ -0,0 +1,26 @@ +### MiniZinc CPLEX Solver Target + +if(CPLEX_FOUND AND USE_CPLEX) + + ### Compile target for the CPlex interface + add_library(minizinc_cplex OBJECT + lib/algorithms/min_cut.cpp + + solvers/MIP/MIP_cplex_solverfactory.cpp + solvers/MIP/MIP_cplex_wrap.cpp + solvers/MIP/MIP_solverinstance.cpp + + include/minizinc/solvers/MIP/MIP_cplex_solverfactory.hh + include/minizinc/solvers/MIP/MIP_cplex_wrap.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hh + ) + set_target_properties(minizinc_cplex PROPERTIES COMPILE_FLAGS ${CPLEX_COMPILE_FLAGS}) + target_include_directories(minizinc_cplex PRIVATE ${CPLEX_INCLUDE_DIRS}) + add_dependencies(minizinc_cplex minizinc_parser) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_CPLEX) + target_sources(mzn PRIVATE $) + set_target_properties(mzn PROPERTIES COMPILE_FLAGS ${CPLEX_COMPILE_FLAGS}) + target_link_libraries(mzn ${CPLEX_LIBRARIES}) +endif() diff --git a/cmake/targets/libminizinc_dtrace.cmake b/cmake/targets/libminizinc_dtrace.cmake new file mode 100644 index 0000000..c348701 --- /dev/null +++ b/cmake/targets/libminizinc_dtrace.cmake @@ -0,0 +1,32 @@ + FIND_PROGRAM(DTRACE dtrace) + MARK_AS_ADVANCED(DTRACE) + +set(DTRACE_HEADER "") +set(DTRACE_OBJECT "") + +if (DTRACE) + ADD_CUSTOM_COMMAND( + OUTPUT ${PROJECT_BINARY_DIR}/include/minizinc/support/dtrace_probes.h + COMMAND ${DTRACE} -h -s ${CMAKE_SOURCE_DIR}/include/minizinc/support/dtrace_probes.d -o ${PROJECT_BINARY_DIR}/include/minizinc/support/dtrace_probes.h + DEPENDS ${PROJECT_SOURCE_DIR}/include/minizinc/support/dtrace_probes.d + ) + set(DTRACE_HEADER ${PROJECT_BINARY_DIR}/include/minizinc/support/dtrace_probes.h) + + if (CMAKE_SYSTEM_NAME MATCHES "Linux") + ADD_CUSTOM_COMMAND( + OUTPUT ${PROJECT_BINARY_DIR}/dtrace_probes.o + COMMAND ${DTRACE} -G -s ${CMAKE_SOURCE_DIR}/include/minizinc/support/dtrace_probes.d -o ${PROJECT_BINARY_DIR}/dtrace_probes.o + DEPENDS ${PROJECT_SOURCE_DIR}/include/minizinc/support/dtrace_probes.d + ) + add_library(minizinc_dtrace_object OBJECT IMPORTED GLOBAL) + set_target_properties(minizinc_dtrace_object PROPERTIES IMPORTED_OBJECTS ${PROJECT_BINARY_DIR}/dtrace_probes.o) + + set(DTRACE_OBJECT $) + endif() +endif() + +set(DTRACE_SOURCES + include/minizinc/support/dtrace.h + ${DTRACE_HEADER} + ${DTRACE_OBJECT} +) diff --git a/cmake/targets/libminizinc_fzn.cmake b/cmake/targets/libminizinc_fzn.cmake new file mode 100644 index 0000000..795b2e2 --- /dev/null +++ b/cmake/targets/libminizinc_fzn.cmake @@ -0,0 +1,14 @@ +### MiniZinc FlatZinc Executable Solver Target + +add_library(minizinc_fzn OBJECT + solvers/fzn/fzn_solverfactory.cpp + solvers/fzn/fzn_solverinstance.cpp + solvers/mzn/mzn_solverfactory.cpp + solvers/mzn/mzn_solverinstance.cpp + + include/minizinc/solvers/fzn_solverfactory.hh + include/minizinc/solvers/fzn_solverinstance.hh + include/minizinc/solvers/mzn_solverfactory.hh + include/minizinc/solvers/mzn_solverinstance.hh +) +add_dependencies(minizinc_fzn minizinc_parser) diff --git a/cmake/targets/libminizinc_geas.cmake b/cmake/targets/libminizinc_geas.cmake new file mode 100644 index 0000000..388f1dc --- /dev/null +++ b/cmake/targets/libminizinc_geas.cmake @@ -0,0 +1,23 @@ +### MiniZinc Geas Solver Target + +if(GEAS_FOUND AND USE_GEAS) + + ### Compile target for the Geas interface + add_library(minizinc_geas OBJECT + solvers/geas/geas_constraints.cpp + solvers/geas/geas_solverfactory.cpp + solvers/geas/geas_solverinstance.cpp + + include/minizinc/solvers/geas/geas_constraints.hh + include/minizinc/solvers/geas_solverfactory.hh + include/minizinc/solvers/geas_solverinstance.hh + ) + target_include_directories(minizinc_geas PRIVATE "${GEAS_INCLUDE_DIRS}") + add_dependencies(minizinc_geas minizinc_parser) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_GEAS) + target_sources(mzn PRIVATE $) + target_link_libraries(mzn Geas) + +endif() diff --git a/cmake/targets/libminizinc_gecode.cmake b/cmake/targets/libminizinc_gecode.cmake new file mode 100644 index 0000000..e92a33d --- /dev/null +++ b/cmake/targets/libminizinc_gecode.cmake @@ -0,0 +1,44 @@ +### MiniZinc Gecode Solver Target + +if(GECODE_FOUND AND USE_GECODE) + + ### Compile target for the Gecode interface + add_library(minizinc_gecode OBJECT + lib/passes/gecode_pass.cpp + + solvers/gecode/aux_brancher.hh + solvers/gecode/fzn_space.cpp + solvers/gecode/gecode_constraints.cpp + solvers/gecode/gecode_solverfactory.cpp + solvers/gecode/gecode_solverinstance.cpp + + include/minizinc/passes/gecode_pass.hh + include/minizinc/solvers/gecode/fzn_space.hh + include/minizinc/solvers/gecode/gecode_constraints.hh + include/minizinc/solvers/gecode_solverfactory.hh + include/minizinc/solvers/gecode_solverinstance.hh + ) + target_include_directories(minizinc_gecode PRIVATE "${GECODE_INCLUDE_DIRS}") + add_dependencies(minizinc_gecode minizinc_parser) + + # Workaround to force CMake to link in the correct order + target_link_libraries(Gecode::Support INTERFACE ${CMAKE_THREAD_LIBS_INIT}) + target_link_libraries(Gecode::Kernel INTERFACE Gecode::Support) + target_link_libraries(Gecode::Search INTERFACE Gecode::Kernel) + target_link_libraries(Gecode::Int INTERFACE Gecode::Kernel) + target_link_libraries(Gecode::Set INTERFACE Gecode::Int) + target_link_libraries(Gecode::Float INTERFACE Gecode::Int) + target_link_libraries(Gecode::Minimodel INTERFACE Gecode::Int Gecode::Set Gecode::Float Gecode::Search) + target_link_libraries(Gecode::Driver INTERFACE Gecode::Int) + target_link_libraries(Gecode::Flatzinc INTERFACE Gecode::Minimodel Gecode::Driver) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_GECODE) + target_sources(mzn PRIVATE $) + + target_link_libraries(mzn Gecode::Driver Gecode::Float Gecode::Int Gecode::Kernel Gecode::Search Gecode::Set) + if(WIN32 AND GECODE_HAS_GIST) + target_link_libraries(mzn Gecode::Gist) + endif() + +endif() diff --git a/cmake/targets/libminizinc_gurobi.cmake b/cmake/targets/libminizinc_gurobi.cmake new file mode 100644 index 0000000..6e72d2d --- /dev/null +++ b/cmake/targets/libminizinc_gurobi.cmake @@ -0,0 +1,27 @@ +### MiniZinc Gurobi Solver Target + +if(GUROBI_FOUND AND USE_GUROBI) + + ### Compile target for the Gurobi interface + add_library(minizinc_gurobi OBJECT + lib/algorithms/min_cut.cpp + lib/utils_savestream.cpp + + solvers/MIP/MIP_gurobi_solverfactory.cpp + solvers/MIP/MIP_gurobi_wrap.cpp + solvers/MIP/MIP_solverinstance.cpp + + include/minizinc/solvers/MIP/MIP_gurobi_solverfactory.hh + include/minizinc/solvers/MIP/MIP_gurobi_wrap.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hpp + ) + target_include_directories(minizinc_gurobi PRIVATE ${GUROBI_INCLUDE_DIRS}) + add_dependencies(minizinc_gurobi minizinc_parser) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_GUROBI) + target_sources(mzn PRIVATE $) + target_link_libraries(mzn ${GUROBI_LIBRARIES}) + +endif() diff --git a/cmake/targets/libminizinc_nl.cmake b/cmake/targets/libminizinc_nl.cmake new file mode 100644 index 0000000..87f1eb5 --- /dev/null +++ b/cmake/targets/libminizinc_nl.cmake @@ -0,0 +1,16 @@ +### MiniZinc NonLinear Executable Solver Target + +add_library(minizinc_nl OBJECT + solvers/nl/nl_components.cpp + solvers/nl/nl_file.cpp + solvers/nl/nl_solreader.cpp + solvers/nl/nl_solverfactory.cpp + solvers/nl/nl_solverinstance.cpp + + include/minizinc/solvers/nl/nl_components.hh + include/minizinc/solvers/nl/nl_file.hh + include/minizinc/solvers/nl/nl_solreader.hh + include/minizinc/solvers/nl/nl_solverfactory.hh + include/minizinc/solvers/nl/nl_solverinstance.hh +) +add_dependencies(minizinc_nl minizinc_parser) diff --git a/cmake/targets/libminizinc_osicbc.cmake b/cmake/targets/libminizinc_osicbc.cmake new file mode 100644 index 0000000..a0cb807 --- /dev/null +++ b/cmake/targets/libminizinc_osicbc.cmake @@ -0,0 +1,26 @@ +### MiniZinc OsiCBC Solver Target + +if(OSICBC_FOUND AND USE_OSICBC) + + ### Compile target for the OsiCBC interface + add_library(minizinc_osicbc OBJECT + lib/algorithms/min_cut.cpp + + solvers/MIP/MIP_osicbc_solverfactory.cpp + solvers/MIP/MIP_osicbc_wrap.cpp + solvers/MIP/MIP_solverinstance.cpp + + include/minizinc/solvers/MIP/MIP_osicbc_solverfactory.hh + include/minizinc/solvers/MIP/MIP_osicbc_wrap.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hpp + ) + target_include_directories(minizinc_osicbc PRIVATE ${OSICBC_INCLUDE_DIRS}) + add_dependencies(minizinc_osicbc minizinc_parser) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_OSICBC) + target_sources(mzn PRIVATE $) + target_link_libraries(mzn ${OSICBC_TARGETS}) + +endif() diff --git a/cmake/targets/libminizinc_parser.cmake b/cmake/targets/libminizinc_parser.cmake new file mode 100644 index 0000000..7aad31b --- /dev/null +++ b/cmake/targets/libminizinc_parser.cmake @@ -0,0 +1,155 @@ +# ------------------------------------------------------------------------------------------------------------------- +## Parser Generation Targets + +# When updating the cached files, update MD5 sums defined in this file +include(${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake) + +macro(MD5 filename md5sum) + file(READ "${filename}" RAW_MD5_FILE) + string(REGEX REPLACE "\r" "" STRIPPED_MD5_FILE "${RAW_MD5_FILE}") + string(MD5 ${md5sum} "${STRIPPED_MD5_FILE}") +endmacro(MD5) + +find_package(BISON 3.4) +find_package(FLEX 2.5) + +if(BISON_FOUND AND FLEX_FOUND) + BISON_TARGET(MZNParser + ${PROJECT_SOURCE_DIR}/lib/parser.yxx + ${PROJECT_BINARY_DIR}/parser.tab.cpp + DEFINES_FILE ${PROJECT_BINARY_DIR}/include/minizinc/parser.tab.hh + COMPILE_FLAGS "-p mzn_yy -l" + ) + + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/minizinc/support/) + BISON_TARGET(MZAParser + ${PROJECT_SOURCE_DIR}/lib/support/assembly/parser.yxx + ${PROJECT_BINARY_DIR}/mza_parser.tab.cpp + DEFINES_FILE ${PROJECT_BINARY_DIR}/include/minizinc/support/mza_parser.tab.hh + COMPILE_FLAGS "-p mza_yy -l" + ) + + BISON_TARGET(RegExParser + ${PROJECT_SOURCE_DIR}/lib/support/regex/parser.yxx + ${PROJECT_BINARY_DIR}/regex_parser.tab.cpp + DEFINES_FILE ${PROJECT_BINARY_DIR}/include/minizinc/support/regex_parser.tab.hh + COMPILE_FLAGS "-p regex_yy -l" + ) +else() + MD5(${PROJECT_SOURCE_DIR}/lib/parser.yxx parser_yxx_md5) + if(NOT "${parser_yxx_md5}" STREQUAL "${parser_yxx_md5_cached}") + message(FATAL_ERROR + "The file parser.yxx has been modified but bison cannot be run.\n" + "If you are sure parser.tab.cpp and minizinc/parser.tab.hh in ${PROJECT_SOURCE_DIR}/lib/cached/ are correct " + "then copy parser.yxx's md5 ${parser_yxx_md5} into ${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" + ) + endif() + + MD5(${PROJECT_SOURCE_DIR}/lib/support/regex/parser.yxx regex_parser_yxx_md5) + if(NOT "${regex_parser_yxx_md5}" STREQUAL "${regex_parser_yxx_md5_cached}") + message(FATAL_ERROR + "The file regex/parser.yxx has been modified but bison cannot be run.\n" + "If you are sure regex_parser.tab.cpp and minizinc/support/regex_parser.tab.hh in " + "${PROJECT_SOURCE_DIR}/lib/cached/ are correct then copy regex_parser.yxx's md5 ${regex_parser_yxx_md5} into " + "${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" + ) + endif() + + MD5(${PROJECT_SOURCE_DIR}/lib/support/assembly/parser.yxx mza_parser_yxx_md5) + if(NOT "${mza_parser_yxx_md5}" STREQUAL "${mza_parser_yxx_md5_cached}") + message(FATAL_ERROR + "The file assembly/parser.yxx has been modified but bison cannot be run.\n" + "If you are sure mza_parser.tab.cpp and minizinc/support/mza_parser.tab.hh in " + "${PROJECT_SOURCE_DIR}/lib/cached/ are correct then copy mza_parser.yxx's md5 ${mza_parser_yxx_md5} into " + "${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" + ) + endif() + + include_directories(${PROJECT_SOURCE_DIR}/lib/cached) + set(BISON_MZNParser_OUTPUTS + ${PROJECT_SOURCE_DIR}/lib/cached/parser.tab.cpp + ${PROJECT_SOURCE_DIR}/lib/cached/minizinc/parser.tab.hh + ) + set(BISON_RegExParser_OUTPUTS + ${PROJECT_SOURCE_DIR}/lib/cached/regex_parser.tab.cpp + ${PROJECT_SOURCE_DIR}/lib/cached/minizinc/support/regex_parser.tab.hh + ) + set(BISON_MZAParser_OUTPUTS + ${PROJECT_SOURCE_DIR}/lib/cached/mza_parser.tab.cpp + ${PROJECT_SOURCE_DIR}/lib/cached/minizinc/support/mza_parser.tab.hh + ) +endif() + +find_package(FLEX) +if(FLEX_FOUND) + FLEX_TARGET(MZNLexer + ${PROJECT_SOURCE_DIR}/lib/lexer.lxx + ${PROJECT_BINARY_DIR}/lexer.yy.cpp + COMPILE_FLAGS "-P mzn_yy -L" + ) + ADD_FLEX_BISON_DEPENDENCY(MZNLexer MZNParser) + + FLEX_TARGET(MZALexer + ${PROJECT_SOURCE_DIR}/lib/support/assembly/lexer.lxx + ${PROJECT_BINARY_DIR}/mza_lexer.yy.cpp + COMPILE_FLAGS "-P mza_yy -L" + ) + ADD_FLEX_BISON_DEPENDENCY(MZALexer MZAParser) + + FLEX_TARGET(RegExLexer + ${PROJECT_SOURCE_DIR}/lib/support/regex/lexer.lxx + ${PROJECT_BINARY_DIR}/regex_lexer.yy.cpp + COMPILE_FLAGS "-P regex_yy -L" + ) + ADD_FLEX_BISON_DEPENDENCY(RegExLexer RegExParser) +else() + MD5(${PROJECT_SOURCE_DIR}/lib/lexer.lxx lexer_lxx_md5) + if(NOT "${lexer_lxx_md5}" STREQUAL "${lexer_lxx_md5_cached}") + message(FATAL_ERROR + "The file lexer.lxx has been modified but flex cannot be run.\n" + "If you are sure ${PROJECT_SOURCE_DIR}/lib/cached/lexer.yy.cpp is correct then " + "copy lexer.lxx's md5 ${lexer_lxx_md5} into ${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" + ) + endif() + + MD5(${PROJECT_SOURCE_DIR}/lib/support/regex/lexer.lxx regex_lexer_lxx_md5) + if(NOT "${regex_lexer_lxx_md5}" STREQUAL "${regex_lexer_lxx_md5_cached}") + message(FATAL_ERROR + "The file regex/lexer.lxx has been modified but flex cannot be run.\n" + "If you are sure ${PROJECT_SOURCE_DIR}/lib/cached/regex_lexer.yy.cpp is correct then " + "copy regex/lexer.lxx's md5 ${regex_lexer_lxx_md5} into ${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" + ) + endif() + + MD5(${PROJECT_SOURCE_DIR}/lib/support/assembly/lexer.lxx mza_lexer_lxx_md5) + if(NOT "${mza_lexer_lxx_md5}" STREQUAL "${mza_lexer_lxx_md5_cached}") + message(FATAL_ERROR + "The file assembly/lexer.lxx has been modified but flex cannot be run.\n" + "If you are sure ${PROJECT_SOURCE_DIR}/lib/cached/mza_lexer.yy.cpp is correct then " + "copy mza/lexer.lxx's md5 ${mza_lexer_lxx_md5} into ${PROJECT_SOURCE_DIR}/lib/cached/md5_cached.cmake" + ) + endif() + + set(FLEX_MZNLexer_OUTPUTS ${PROJECT_SOURCE_DIR}/lib/cached/lexer.yy.cpp) + set(FLEX_RegExLexer_OUTPUTS ${PROJECT_SOURCE_DIR}/lib/cached/regex_lexer.yy.cpp) + set(FLEX_MZALexer_OUTPUTS ${PROJECT_SOURCE_DIR}/lib/cached/mza_lexer.yy.cpp) +endif() + +if(NOT (GECODE_FOUND AND USE_GECODE)) + set(FLEX_RegExLexer_OUTPUTS "") + set(BISON_RegExParser_OUTPUTS "") +endif() + +add_library(minizinc_parser OBJECT + ${BISON_MZNParser_OUTPUTS} + ${FLEX_MZNLexer_OUTPUTS} + ${BISON_MZAParser_OUTPUTS} + ${FLEX_MZALexer_OUTPUTS} + ${BISON_RegExParser_OUTPUTS} + ${FLEX_RegExLexer_OUTPUTS} +) + +if(GECODE_FOUND AND USE_GECODE) + target_include_directories(minizinc_parser PRIVATE "${GECODE_INCLUDE_DIRS}") + target_compile_definitions(minizinc_parser PRIVATE HAS_GECODE) +endif() diff --git a/cmake/targets/libminizinc_scip.cmake b/cmake/targets/libminizinc_scip.cmake new file mode 100644 index 0000000..b4e8fb1 --- /dev/null +++ b/cmake/targets/libminizinc_scip.cmake @@ -0,0 +1,26 @@ +### MiniZinc SCIP Solver Target + +if(SCIP_FOUND AND USE_SCIP) + + ### Compile target for the SCIP interface + add_library(minizinc_scip OBJECT + lib/algorithms/min_cut.cpp + + solvers/MIP/MIP_scip_solverfactory.cpp + solvers/MIP/MIP_scip_wrap.cpp + solvers/MIP/MIP_solverinstance.cpp + + include/minizinc/solvers/MIP/MIP_scip_solverfactory.hh + include/minizinc/solvers/MIP/MIP_scip_wrap.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hpp + ) + target_include_directories(minizinc_scip PRIVATE ${SCIP_INCLUDE_DIRS}) + add_dependencies(minizinc_scip minizinc_parser) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_SCIP) + target_sources(mzn PRIVATE $) + target_link_libraries(mzn ${SCIP_LIBRARIES}) + +endif() diff --git a/cmake/targets/libminizinc_solver.cmake b/cmake/targets/libminizinc_solver.cmake new file mode 100644 index 0000000..e69de29 diff --git a/cmake/targets/libminizinc_xpress.cmake b/cmake/targets/libminizinc_xpress.cmake new file mode 100644 index 0000000..ad3f79e --- /dev/null +++ b/cmake/targets/libminizinc_xpress.cmake @@ -0,0 +1,28 @@ +### MiniZinc FICO Xpress Solver Target + +if(XPRESS_FOUND AND USE_XPRESS) + + ### Compile target for the Xpress interface + add_library(minizinc_xpress OBJECT + lib/algorithms/min_cut.cpp + + solvers/MIP/MIP_solverinstance.cpp + solvers/MIP/MIP_xpress_solverfactory.cpp + solvers/MIP/MIP_xpress_wrap.cpp + + include/minizinc/solvers/MIP/MIP_solverinstance.hh + include/minizinc/solvers/MIP/MIP_solverinstance.hpp + include/minizinc/solvers/MIP/MIP_xpress_solverfactory.hh + include/minizinc/solvers/MIP/MIP_xpress_wrap.hh + ) + + target_include_directories(minizinc_xpress PRIVATE ${XPRESS_INCLUDE_DIRS}) + target_link_libraries(minizinc_xpress minizinc_core ${XPRESS_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + add_dependencies(minizinc_xpress minizinc_parser) + + ### Setup correct compilation into the MiniZinc library + target_compile_definitions(mzn PRIVATE HAS_XPRESS) + target_sources(mzn PRIVATE $) + target_link_libraries(mzn ${XPRESS_LIBRARIES}) + +endif() diff --git a/cmake/targets/libmza.cmake b/cmake/targets/libmza.cmake new file mode 100644 index 0000000..bd0dd27 --- /dev/null +++ b/cmake/targets/libmza.cmake @@ -0,0 +1,15 @@ +if(GECODE_FOUND AND USE_GECODE) + add_library(mza SHARED + lib/c_interface.cpp + include/minizinc/c_interface.h + ) + target_link_libraries(mza mzn) + + install( + TARGETS mza + EXPORT libminizincTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + ) +endif() diff --git a/cmake/targets/libmzn.cmake b/cmake/targets/libmzn.cmake new file mode 100644 index 0000000..1679bfa --- /dev/null +++ b/cmake/targets/libmzn.cmake @@ -0,0 +1,183 @@ +### MiniZinc Library Target +# Combined definition of the MiniZinc core and all solvers compiled + +include(cmake/targets/libminizinc_dtrace.cmake) +include(cmake/targets/libminizinc_parser.cmake) +include(cmake/targets/libminizinc_fzn.cmake) +include(cmake/targets/libminizinc_nl.cmake) + +add_library(mzn + lib/MIPdomains.cpp + lib/ast.cpp + lib/astexception.cpp + lib/aststring.cpp + lib/astvec.cpp + lib/builtins.cpp + lib/cdecode.c + lib/cencode.c + lib/chain_compressor.cpp + lib/copy.cpp + lib/eval_par.cpp + lib/file_utils.cpp + lib/flatten.cpp + lib/flatten/flat_exp.cpp + lib/flatten/flatten_anon.cpp + lib/flatten/flatten_arrayaccess.cpp + lib/flatten/flatten_arraylit.cpp + lib/flatten/flatten_binop.cpp + lib/flatten/flatten_call.cpp + lib/flatten/flatten_comp.cpp + lib/flatten/flatten_id.cpp + lib/flatten/flatten_ite.cpp + lib/flatten/flatten_let.cpp + lib/flatten/flatten_par.cpp + lib/flatten/flatten_setlit.cpp + lib/flatten/flatten_unop.cpp + lib/flatten/flatten_vardecl.cpp + lib/flattener.cpp + lib/gc.cpp + lib/htmlprinter.cpp + lib/interpreter.cpp + lib/interpreter/bytecode.cpp + lib/interpreter/constraint.cpp + lib/interpreter/primitives.cpp + lib/interpreter/values.cpp + lib/interpreter/variable.cpp + lib/interpreter/vector.cpp + lib/json_parser.cpp + lib/lexer.lxx + lib/miniz.c + lib/model.cpp + lib/optimize.cpp + lib/optimize_constraints.cpp + lib/options.cpp + lib/output.cpp + lib/parser.cpp + lib/parser.yxx + lib/passes/compile_pass.cpp + lib/pathfileprinter.cpp + lib/prettyprinter.cpp + lib/process.cpp + lib/reader.cpp + lib/solns2out.cpp + lib/solver.cpp + lib/solver_config.cpp + lib/solver_instance.cpp + lib/solver_instance_base.cpp + lib/statistics.cpp + lib/type.cpp + lib/typecheck.cpp + lib/values.cpp + lib/support/assembly/parser.yxx + lib/support/assembly/lexer.lxx + lib/support/regex/parser.yxx + lib/support/regex/lexer.lxx + + include/minizinc/ast.hh + include/minizinc/ast.hpp + include/minizinc/astexception.hh + include/minizinc/astiterator.hh + include/minizinc/aststring.hh + include/minizinc/astvec.hh + include/minizinc/builtins.hh + include/minizinc/interpreter.hh + include/minizinc/interpreter/_val_decl.hh + include/minizinc/interpreter/bytecode.hh + include/minizinc/interpreter/constraint.hh + include/minizinc/interpreter/cse.hh + include/minizinc/interpreter/cse.hpp + include/minizinc/interpreter/primitives.hh + include/minizinc/interpreter/rco.hh + include/minizinc/interpreter/values.hh + include/minizinc/interpreter/variable.hh + include/minizinc/interpreter/vector.hh + include/minizinc/chain_compressor.hh + include/minizinc/config.hh.in + include/minizinc/copy.hh + include/minizinc/eval_par.hh + include/minizinc/exception.hh + include/minizinc/file_utils.hh + include/minizinc/flat_exp.hh + include/minizinc/flatten.hh + include/minizinc/flatten_internal.hh + include/minizinc/flattener.hh + include/minizinc/gc.hh + include/minizinc/hash.hh + include/minizinc/htmlprinter.hh + include/minizinc/iter.hh + include/minizinc/json_parser.hh + include/minizinc/model.hh + include/minizinc/optimize.hh + include/minizinc/optimize_constraints.hh + include/minizinc/options.hh + include/minizinc/output.hh + include/minizinc/parser.hh + include/minizinc/passes/compile_pass.hh + include/minizinc/pathfileprinter.hh + include/minizinc/prettyprinter.hh + include/minizinc/process.hh + include/minizinc/reader.hh + include/minizinc/solns2out.hh + include/minizinc/solver.hh + include/minizinc/solver_config.hh + include/minizinc/solver_instance.hh + include/minizinc/solver_instance_base.hh + include/minizinc/statistics.hh + include/minizinc/support/regex.hh + include/minizinc/thirdparty/b64/cdecode.h + include/minizinc/thirdparty/b64/cencode.h + include/minizinc/thirdparty/b64/decode.h + include/minizinc/thirdparty/b64/encode.h + include/minizinc/thirdparty/miniz.h + include/minizinc/timer.hh + include/minizinc/type.hh + include/minizinc/typecheck.hh + include/minizinc/utils.hh + include/minizinc/values.hh + + ${DTRACE_SOURCES} + $ + $ + $ +) +target_link_libraries(mzn ${CMAKE_THREAD_LIBS_INIT}) +if(DTRACE) + target_compile_definitions(mzn PRIVATE USE_DYNAMIC_TRACE) +endif() + +### Add Solver Interfaces to the MiniZinc library when available +include(cmake/targets/libminizinc_cplex.cmake) +include(cmake/targets/libminizinc_geas.cmake) +include(cmake/targets/libminizinc_gecode.cmake) +include(cmake/targets/libminizinc_gurobi.cmake) +include(cmake/targets/libminizinc_osicbc.cmake) +include(cmake/targets/libminizinc_scip.cmake) +include(cmake/targets/libminizinc_xpress.cmake) + +if(GECODE_FOUND AND USE_GECODE) + target_link_libraries(mzn Gecode::Minimodel Gecode::Support) +endif() + + + +### Add all necessary files to the install target +install( + TARGETS mzn + EXPORT libminizincTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) +install( + DIRECTORY share/minizinc + DESTINATION share +) +install( + DIRECTORY include/minizinc + DESTINATION include + PATTERN config.hh.in EXCLUDE +) +install( + DIRECTORY lib/cached/minizinc + DESTINATION include +) diff --git a/cmake/targets/minizinc.cmake b/cmake/targets/minizinc.cmake new file mode 100644 index 0000000..731cf4d --- /dev/null +++ b/cmake/targets/minizinc.cmake @@ -0,0 +1,12 @@ +#### MiniZinc Executable Target + +add_executable(minizinc minizinc.cpp) +target_link_libraries(minizinc mzn) + +install( + TARGETS minizinc + EXPORT libminizincTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/cmake/targets/mzn2doc.cmake b/cmake/targets/mzn2doc.cmake new file mode 100644 index 0000000..d7c429d --- /dev/null +++ b/cmake/targets/mzn2doc.cmake @@ -0,0 +1,11 @@ +#### Binary target for MiniZinc documentation generator +add_executable(mzn2doc mzn2doc.cpp) +target_link_libraries(mzn2doc mzn) + +install( + TARGETS mzn2doc + EXPORT libminizincTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/cmake/targets/mznasm.cmake b/cmake/targets/mznasm.cmake new file mode 100644 index 0000000..818bf19 --- /dev/null +++ b/cmake/targets/mznasm.cmake @@ -0,0 +1,12 @@ +#### MiniZinc assembler / interpreter Binary Target + +add_executable(mznasm mznasm.cpp) +target_link_libraries(mznasm mzn) + +install( + TARGETS mznasm + EXPORT libminizincTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/cmake/targets/mzncc.cmake b/cmake/targets/mzncc.cmake new file mode 100644 index 0000000..3f36cf9 --- /dev/null +++ b/cmake/targets/mzncc.cmake @@ -0,0 +1,18 @@ +#### Minizinc bytecode compiler +add_executable(mzncc + mzncc.cpp + lib/codegen.cpp + lib/codegen/codegen_internal.hpp + lib/codegen/analysis.hpp + lib/codegen/analysis.cpp + include/minizinc/codegen.hh + include/minizinc/codegen_support.hh) +target_link_libraries(mzncc mzn) + +install( + TARGETS mzncc + EXPORT libminizincTargets + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/cmake/templates/launch-c.in b/cmake/templates/launch-c.in new file mode 100644 index 0000000..3d476af --- /dev/null +++ b/cmake/templates/launch-c.in @@ -0,0 +1,3 @@ +#!/bin/sh +export CCACHE_CPP2=true +exec "${RULE_LAUNCH_COMPILE}" "${CMAKE_C_COMPILER}" "$@" \ No newline at end of file diff --git a/cmake/templates/launch-cxx.in b/cmake/templates/launch-cxx.in new file mode 100644 index 0000000..039edb0 --- /dev/null +++ b/cmake/templates/launch-cxx.in @@ -0,0 +1,3 @@ +#!/bin/sh +export CCACHE_CPP2=true +exec "${RULE_LAUNCH_COMPILE}" "${CMAKE_CXX_COMPILER}" "$@" \ No newline at end of file diff --git a/cmake/templates/libminizincConfig.cmake.in b/cmake/templates/libminizincConfig.cmake.in new file mode 100644 index 0000000..730a4d7 --- /dev/null +++ b/cmake/templates/libminizincConfig.cmake.in @@ -0,0 +1,18 @@ +# - Config file for the libminizinc package +# It defines the following variables +# libminizinc_INCLUDE_DIRS - include directories for libminizinc +# libminizinc_LIBRARIES - libraries to link against +# libminizinc_EXECUTABLE - the bar executable + +# Compute paths +get_filename_component(libminizinc_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +set(libminizinc_INCLUDE_DIRS "@CONF_INCLUDE_DIRS@") + +# Our library dependencies +list(APPEND CMAKE_MODULE_PATH ${libminizinc_CMAKE_DIR}) +include(CMakeFindDependencyMacro) +@CONF_DEPENDENCIES@ + +# Our library targets (contains definitions for IMPORTED targets) +include("${libminizinc_CMAKE_DIR}/libminizincTargets.cmake") + diff --git a/cmake/templates/libminizincConfigVersion.cmake.in b/cmake/templates/libminizincConfigVersion.cmake.in new file mode 100644 index 0000000..00ae7c9 --- /dev/null +++ b/cmake/templates/libminizincConfigVersion.cmake.in @@ -0,0 +1,12 @@ +set(PACKAGE_VERSION "@libminizinc_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() + diff --git a/include/minizinc/MIPdomains.hh b/include/minizinc/MIPdomains.hh new file mode 100644 index 0000000..854544e --- /dev/null +++ b/include/minizinc/MIPdomains.hh @@ -0,0 +1,57 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_mipDOMAINS_HH__ +#define __MINIZINC_mipDOMAINS_HH__ + +#include +#include +#include + +#include +#include + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#undef ERROR // MICROsoft. +#undef min +#undef max +#endif + +#define MZN_MIPD__assert_soft(c, e) \ + do { \ + static int nn = 0; \ + if (!(c)) \ + if (++nn <= 1) std::cerr << e << std::endl; \ + } while (0) +#define MZN_MIPD__assert_hard(c) MZN_ASSERT_HARD(c) +#define MZN_MIPD__assert_hard_msg(c, e) MZN_ASSERT_HARD_MSG(c, e) +struct MIPD_Infeasibility_Exception { + std::string msg; + MIPD_Infeasibility_Exception(const std::string& s) : msg(s) {} +}; +#define MZN_MIPD__assert_for_feas(c, e) \ + do { \ + if (!(c)) { \ + std::ostringstream oss; \ + oss << e; \ + throw MIPD_Infeasibility_Exception(oss.str()); \ + } \ + } while (0) + +namespace MiniZinc { + +/// Linearize domain constraints in \a env +void MIPdomains(Env& env, bool fVerbose = false, int = 0, double = 3.0); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/algorithms/min_cut.h b/include/minizinc/algorithms/min_cut.h new file mode 100644 index 0000000..2a36890 --- /dev/null +++ b/include/minizinc/algorithms/min_cut.h @@ -0,0 +1,25 @@ +#ifndef __MIN_CUT_H +#define __MIN_CUT_H + +#include +#include + +namespace Algorithms { + +/// An interface to some min-cut algorithm for undirected graphs +class MinCut { +public: + /// INPUT + int nNodes = 0; + std::vector > edges; + std::vector weights; + /// OUTPUT + std::vector parities; + double wMinCut = 1e100; + /// Invocation + void solve(); +}; + +} // namespace Algorithms + +#endif // __MIN_CUT_H diff --git a/include/minizinc/ast.hh b/include/minizinc/ast.hh new file mode 100644 index 0000000..02fc20a --- /dev/null +++ b/include/minizinc/ast.hh @@ -0,0 +1,1950 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_AST_HH__ +#define __MINIZINC_AST_HH__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +class IntLit; +class FloatLit; +class SetLit; +class BoolLit; +class StringLit; +class Id; +class AnonVar; +class ArrayLit; +class ArrayAccess; +class Comprehension; +class ITE; +class BinOp; +class UnOp; +class Call; +class VarDecl; +class Let; +class TypeInst; + +class Item; +class FunctionI; + +class ExpressionSet; +class ExpressionSetIter; + +/// %Location of an expression used during parsing +class ParserLocation { +protected: + /// Source code file name + ASTString _filename; + /// Line where expression starts + unsigned int _first_line; + /// Line where expression ends + unsigned int _last_line; + /// Column where expression starts + unsigned int _first_column; + /// Column where expression ends + unsigned int _last_column; + +public: + /// Construct empty location + ParserLocation(void) : _first_line(1), _last_line(1), _first_column(0), _last_column(0) {} + + /// Construct location + ParserLocation(const ASTString& filename, unsigned int first_line, unsigned int first_column, + unsigned int last_line, unsigned int last_column) + : _filename(filename), + _first_line(first_line), + _last_line(last_line), + _first_column(first_column), + _last_column(last_column) {} + + ASTString filename(void) const { return _filename; } + void filename(const ASTString& f) { _filename = f; } + + unsigned int first_line(void) const { return _first_line; } + void first_line(unsigned int l) { _first_line = l; } + + unsigned int last_line(void) const { return _last_line; } + void last_line(unsigned int l) { _last_line = l; } + + unsigned int first_column(void) const { return _first_column; } + void first_column(unsigned int c) { _first_column = c; } + + unsigned int last_column(void) const { return _last_column; } + void last_column(unsigned int c) { _last_column = c; } + + std::string toString(void) const { + std::ostringstream oss; + oss << _filename << ":" << _first_line << "." << _first_column; + if (_first_line != _last_line) { + oss << "-" << _last_line << "." << _last_column; + } else if (_first_column != _last_column) { + oss << "-" << _last_column; + } + return oss.str(); + } +}; + +/// %Location of an expression in the source code +class Location { +protected: + /// Internal representation of a Location + /// Layout depends on sizeof pointer and arguments + /// 32 bit pointers: + /// Layout 1: filename (32 bit), first line (8 bit), last line-first line (7 bit), first column + /// (6 bit), last column (7 bit) Layout 2: filename (32 bit), 4 IntLit for the lines/columns + /// 64 bit pointers: + /// Layout 1: filename (64 bit), first line (20 bit), last line-first line (20 bit), first + /// column (10 bit), last column (10 bit) Layout 2: filename (64 bit), 4 IntLit for the + /// lines/columns + class LocVec : public ASTVec { + protected: + LocVec(const ASTString& filename, unsigned int first_line, unsigned int first_column, + unsigned int last_line, unsigned int last_column); + LocVec(const ASTString& filename, IntVal combined); + + public: + static LocVec* a(const ASTString& filename, unsigned int first_line, unsigned int first_column, + unsigned int last_line, unsigned int last_column); + void mark(void) { + _gc_mark = 1; + if (_data[0]) static_cast(_data[0])->mark(); + } + + ASTString filename(void) const; + unsigned int first_line(void) const; + unsigned int last_line(void) const; + unsigned int first_column(void) const; + unsigned int last_column(void) const; + }; + + union LI { + LocVec* lv; + ptrdiff_t t; + } _loc_info; + + LocVec* lv(void) const { + LI li = _loc_info; + li.t &= ~static_cast(1); + return li.lv; + } + +public: + /// Construct empty location + Location(void) { _loc_info.lv = NULL; } + + /// Construct location + Location(const ASTString& filename, unsigned int first_line, unsigned int first_column, + unsigned int last_line, unsigned int last_column) { + if (last_line < first_line) throw InternalError("invalid location"); + _loc_info.lv = LocVec::a(filename, first_line, first_column, last_line, last_column); + } + + Location(const ParserLocation& loc) { + _loc_info.lv = LocVec::a(loc.filename(), loc.first_line(), loc.first_column(), loc.last_line(), + loc.last_column()); + } + + /// Return string representation + std::string toString(void) const; + + /// Return filename + ASTString filename(void) const { return lv() ? lv()->filename() : ASTString(); } + + /// Return first line number + unsigned int first_line(void) const { return lv() ? lv()->first_line() : 0; } + + /// Return last line number + unsigned int last_line(void) const { return lv() ? lv()->last_line() : 0; } + + /// Return first column number + unsigned int first_column(void) const { return lv() ? lv()->first_column() : 0; } + + /// Return last column number + unsigned int last_column(void) const { return lv() ? lv()->last_column() : 0; } + + /// Return whether location is introduced by the compiler + bool is_introduced(void) const { return _loc_info.lv == NULL || ((_loc_info.t & 1) != 0); } + + /// Mark as alive for garbage collection + void mark(void) const; + + /// Return location with introduced flag set + Location introduce(void) const; + + /// Location used for un-allocated expressions + static Location nonalloc; + + ParserLocation parserLocation(void) const { + return ParserLocation(filename(), first_line(), first_column(), last_line(), last_column()); + } +}; + +/// Output operator for locations +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const Location& loc) { + std::basic_ostringstream s; + s.copyfmt(os); + s.width(0); + if (loc.filename() == "") { + s << "unknown file"; + } else { + s << loc.filename(); + } + s << ":" << loc.first_line() << "." << loc.first_column(); + if (loc.first_line() != loc.last_line()) { + s << "-" << loc.last_line() << "." << loc.last_column(); + } else if (loc.first_column() != loc.last_column()) { + s << "-" << loc.last_column(); + } + + return os << s.str(); +} + +/** + * \brief Annotations + */ +class Annotation { +private: + ExpressionSet* _s; + + /// Delete + Annotation(const Annotation&); + /// Delete + Annotation& operator=(const Annotation&); + +public: + Annotation(void) : _s(NULL) {} + ~Annotation(void); + bool contains(Expression* e) const; + bool containsCall(const ASTString& id) const; + bool isEmpty(void) const; + ExpressionSetIter begin(void) const; + ExpressionSetIter end(void) const; + void add(Expression* e); + void add(std::vector e); + void remove(Expression* e); + void removeCall(const ASTString& id); + void clear(void); + void merge(const Annotation& ann); + Call* getCall(const ASTString& id) const; + + static Annotation empty; +}; + +/// returns the Annotation specified by the string; returns NULL if not exists +Expression* getAnnotation(const Annotation& ann, std::string str); + +/// returns the Annotation specified by the string; returns NULL if not exists +Expression* getAnnotation(const Annotation& ann, const ASTString& str); + +/** + * \brief Base class for expressions + */ +class Expression : public ASTNode { +protected: + /// The %MiniZinc type of the expression + Type _type; + /// The annotations + Annotation _ann; + /// The location of the expression + Location _loc; + /// The hash value of the expression + size_t _hash; + +public: + /// Identifier of the concrete expression type + enum ExpressionId { + E_INTLIT = ASTNode::NID_END + 1, + E_FLOATLIT, + E_SETLIT, + E_BOOLLIT, + E_STRINGLIT, + E_ID, + E_ANON, + E_ARRAYLIT, + E_ARRAYACCESS, + E_COMP, + E_ITE, + E_BINOP, + E_UNOP, + E_CALL, + E_VARDECL, + E_LET, + E_TI, + E_TIID, + EID_END = E_TIID + }; + + bool isUnboxedVal(void) const { + if (sizeof(double) <= sizeof(FloatLit*)) { + // bit 1 or bit 0 is set + return (reinterpret_cast(this) & static_cast(3)) != 0; + } else { + // bit 0 is set + return (reinterpret_cast(this) & static_cast(1)) != 0; + } + } + bool isUnboxedInt(void) const { + if (sizeof(double) <= sizeof(FloatLit*)) { + // bit 1 is set, bit 0 is not set + return (reinterpret_cast(this) & static_cast(3)) == 2; + } else { + // bit 0 is set + return (reinterpret_cast(this) & static_cast(1)) == 1; + } + } + bool isUnboxedFloatVal(void) const { + // bit 0 is set (and doubles fit inside pointers) + return (sizeof(double) <= sizeof(FloatLit*)) && + (reinterpret_cast(this) & static_cast(1)) == 1; + } + + ExpressionId eid(void) const { + return isUnboxedInt() ? E_INTLIT + : isUnboxedFloatVal() ? E_FLOATLIT + : static_cast(_id); + } + + const Location& loc(void) const { return isUnboxedVal() ? Location::nonalloc : _loc; } + void loc(const Location& l) { + if (!isUnboxedVal()) _loc = l; + } + const Type& type(void) const { + return isUnboxedInt() ? Type::unboxedint : isUnboxedFloatVal() ? Type::unboxedfloat : _type; + } + void type(const Type& t); + size_t hash(void) const { + return isUnboxedInt() ? unboxedIntToIntVal().hash() + : isUnboxedFloatVal() ? unboxedFloatToFloatVal().hash() + : _hash; + } + +protected: + /// Combination function for hash values + void cmb_hash(size_t h) { _hash ^= h + 0x9e3779b9 + (_hash << 6) + (_hash >> 2); } + /// Combination function for hash values + size_t cmb_hash(size_t seed, size_t h) { + seed ^= h + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } + + /// Compute base hash value + void init_hash(void) { _hash = cmb_hash(0, _id); } + + /// Check if \a e0 and \a e1 are equal + static bool equal_internal(const Expression* e0, const Expression* e1); + + /// Constructor + Expression(const Location& loc, const ExpressionId& eid, const Type& t) + : ASTNode(eid), _type(t), _loc(loc) {} + +public: + IntVal unboxedIntToIntVal(void) const { + assert(isUnboxedInt()); + if (sizeof(double) <= sizeof(FloatVal*)) { + unsigned long long int i = reinterpret_cast(this) & ~static_cast(7); + bool pos = ((reinterpret_cast(this) & static_cast(4)) == 0); + if (pos) { + return i >> 3; + } else { + return -(static_cast(i >> 3)); + } + } else { + unsigned long long int i = reinterpret_cast(this) & ~static_cast(3); + bool pos = ((reinterpret_cast(this) & static_cast(2)) == 0); + if (pos) { + return i >> 2; + } else { + return -(static_cast(i >> 2)); + } + } + } + static IntLit* intToUnboxedInt(long long int i) { + static const unsigned int pointerBits = sizeof(IntLit*) * 8; + if (sizeof(double) <= sizeof(FloatVal*)) { + static const long long int maxUnboxedVal = + (static_cast(1) << (pointerBits - 3)) - static_cast(1); + if (i < -maxUnboxedVal || i > maxUnboxedVal) return NULL; + long long int j = i < 0 ? -i : i; + ptrdiff_t ubi_p = (static_cast(j) << 3) | static_cast(2); + if (i < 0) ubi_p = ubi_p | static_cast(4); + return reinterpret_cast(ubi_p); + } else { + static const long long int maxUnboxedVal = + (static_cast(1) << (pointerBits - 2)) - static_cast(1); + if (i < -maxUnboxedVal || i > maxUnboxedVal) return NULL; + long long int j = i < 0 ? -i : i; + ptrdiff_t ubi_p = (static_cast(j) << 2) | static_cast(1); + if (i < 0) ubi_p = ubi_p | static_cast(2); + return reinterpret_cast(ubi_p); + } + } + FloatVal unboxedFloatToFloatVal(void) const { + assert(isUnboxedFloatVal()); + union { + double d; + uint64_t bits; + const Expression* p; + } _u; + _u.p = this; + _u.bits = _u.bits >> 1; + uint64_t exponent = (_u.bits & (static_cast(0x3FF) << 52)) >> 52; + if (exponent != 0) { + exponent += 512; // reconstruct original bias of 1023 + } + uint64_t sign = (_u.bits & (static_cast(1) << 62) ? 1 : 0); + _u.bits = (sign << 63) | (exponent << 52) | (_u.bits & static_cast(0xFFFFFFFFFFFFF)); + return _u.d; + } + static FloatLit* doubleToUnboxedFloatVal(double d) { + if (sizeof(double) > sizeof(FloatLit*)) return NULL; + union { + double d; + uint64_t bits; + FloatLit* p; + } _u; + _u.d = d; + + uint64_t exponent = (_u.bits & (static_cast(0x7FF) << 52)) >> 52; + if (exponent != 0) { + if (exponent < 513 || exponent > 1534) return NULL; // exponent doesn't fit in 10 bits + exponent -= 512; // make exponent fit in 10 bits, with bias 511 + } + bool sign = (_u.bits & (static_cast(1) << 63)) != 0; + + _u.bits = _u.bits & + ~(static_cast(0x7FF) << 52); // mask out top 11 bits (previously exponent) + _u.bits = (_u.bits << 1) | 1u; // shift by one bit and add tag for double + _u.bits = + _u.bits | (static_cast(sign) << 63) | (static_cast(exponent) << 53); + return _u.p; + } + + bool isTagged(void) const { + // only bit 2 is set + if (isUnboxedVal()) return false; + if (sizeof(double) <= sizeof(FloatVal*)) + return (reinterpret_cast(this) & static_cast(7)) == 4; + else + return (reinterpret_cast(this) & static_cast(3)) == 2; + } + + Expression* tag(void) const { + assert(!isUnboxedVal()); + if (sizeof(double) <= sizeof(FloatVal*)) + return reinterpret_cast(reinterpret_cast(this) | + static_cast(4)); + else + return reinterpret_cast(reinterpret_cast(this) | + static_cast(2)); + } + Expression* untag(void) { + if (isUnboxedVal()) return this; + if (sizeof(double) <= sizeof(FloatVal*)) + return reinterpret_cast(reinterpret_cast(this) & + ~static_cast(4)); + else + return reinterpret_cast(reinterpret_cast(this) & + ~static_cast(2)); + } + + /// Test if expression is of type \a T + template + bool isa(void) const { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-undefined-compare" +#endif + if (nullptr == this) throw InternalError("isa: nullptr"); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return isUnboxedInt() ? T::eid == E_INTLIT + : isUnboxedFloatVal() ? T::eid == E_FLOATLIT + : _id == T::eid; + } + /// Cast expression to type \a T* + template + T* cast(void) { + assert(isa()); + return static_cast(this); + } + /// Cast expression to type \a const T* + template + const T* cast(void) const { + assert(isa()); + return static_cast(this); + } + /// Cast expression to type \a T* or NULL if types do not match + template + T* dyn_cast(void) { + return isa() ? static_cast(this) : NULL; + } + /// Cast expression to type \a const T* or NULL if types do not match + template + const T* dyn_cast(void) const { + return isa() ? static_cast(this) : NULL; + } + + /// Cast expression to type \a T* + template + static T* cast(Expression* e) { + return e == NULL ? NULL : e->cast(); + } + /// Cast expression to type \a const T* + template + static const T* cast(const Expression* e) { + return e == NULL ? NULL : e->cast(); + } + /// Cast expression to type \a T* or NULL if types do not match + template + static T* dyn_cast(Expression* e) { + return e == NULL ? NULL : e->dyn_cast(); + } + /// Cast expression to type \a const T* or NULL if types do not match + template + static const T* dyn_cast(const Expression* e) { + return e == NULL ? NULL : e->dyn_cast(); + } + + /// Add annotation \a ann to the expression + void addAnnotation(Expression* ann); + + /// Add annotation \a ann to the expression + void addAnnotations(std::vector ann); + + const Annotation& ann(void) const { return isUnboxedVal() ? Annotation::empty : _ann; } + Annotation& ann(void) { return isUnboxedVal() ? Annotation::empty : _ann; } + + /// Return hash value of \a e + static size_t hash(const Expression* e) { return e == NULL ? 0 : e->hash(); } + + /// Check if \a e0 and \a e1 are equal + static bool equal(const Expression* e0, const Expression* e1); + + /// Mark \a e as alive for garbage collection + static void mark(Expression* e); +}; + +/// \brief Integer literal expression +class IntLit : public Expression { +protected: + /// The value of this expression + IntVal _v; + /// Constructor + IntLit(const Location& loc, IntVal v); + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_INTLIT; + /// Access value + IntVal v(void) const { return isUnboxedInt() ? unboxedIntToIntVal() : _v; } + /// Recompute hash value + void rehash(void); + /// Allocate literal + static IntLit* a(IntVal v); + /// Allocate literal for enumerated type (only used internally for generators) + static IntLit* aEnum(IntVal v, unsigned int enumId); +}; +/// \brief Float literal expression +class FloatLit : public Expression { +protected: + /// The value of this expression + FloatVal _v; + /// Constructor + FloatLit(const Location& loc, FloatVal v); + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_FLOATLIT; + /// Access value + FloatVal v(void) const { return isUnboxedFloatVal() ? unboxedFloatToFloatVal() : _v; } + /// Recompute hash value + void rehash(void); + /// Allocate literal + static FloatLit* a(FloatVal v); +}; +/// \brief Set literal expression +class SetLit : public Expression { +protected: + /// The value of this expression + ASTExprVec _v; + union { + /// A range-list based representation for an integer set, or NULL + IntSetVal* isv; + /// A range-list based representation for an float set, or NULL + FloatSetVal* fsv; + } _u; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_SETLIT; + /// Construct set \$f\{v1,\dots,vn\}\$f + SetLit(const Location& loc, const std::vector& v); + /// Construct set \$f\{v1,\dots,vn\}\$f + SetLit(const Location& loc, ASTExprVec v); + /// Construct set + SetLit(const Location& loc, IntSetVal* isv); + /// Construct set + SetLit(const Location& loc, FloatSetVal* fsv); + /// Access value + ASTExprVec v(void) const { return _v; } + /// Set value + void v(const ASTExprVec& val) { _v = val; } + /// Access integer set value if present + IntSetVal* isv(void) const { return type().bt() == Type::BT_INT ? _u.isv : NULL; } + /// Set integer set value + void isv(IntSetVal* val) { _u.isv = val; } + /// Access float set value if present + FloatSetVal* fsv(void) const { return type().bt() == Type::BT_FLOAT ? _u.fsv : NULL; } + /// Set integer set value + void fsv(FloatSetVal* val) { _u.fsv = val; } + /// Recompute hash value + void rehash(void); +}; +/// \brief Boolean literal expression +class BoolLit : public Expression { +protected: + /// The value of this expression + bool _v; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_BOOLLIT; + /// Constructor + BoolLit(const Location& loc, bool v); + /// Access value + bool v(void) const { return _v; } + /// Recompute hash value + void rehash(void); +}; +/// \brief String literal expression +class StringLit : public Expression { +protected: + /// The value of this expression + ASTString _v; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_STRINGLIT; + /// Constructor + StringLit(const Location& loc, const std::string& v); + /// Constructor + StringLit(const Location& loc, const ASTString& v); + /// Access value + ASTString v(void) const { return _v; } + /// Set value + void v(const ASTString& val) { _v = val; } + /// Recompute hash value + void rehash(void); +}; +/// \brief Identifier expression +class Id : public Expression { +protected: + /// The string identifier + void* _v_or_idn; + /// The declaration corresponding to this identifier (may be NULL) + Expression* _decl; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_ID; + /// Constructor (\a decl may be NULL) + Id(const Location& loc, const std::string& v, VarDecl* decl); + /// Constructor (\a decl may be NULL) + Id(const Location& loc, const ASTString& v, VarDecl* decl); + /// Constructor (\a decl may be NULL) + Id(const Location& loc, long long int idn, VarDecl* decl); + /// Access identifier + ASTString v(void) const; + /// Set identifier + void v(const ASTString& val) { _v_or_idn = val.aststr(); } + /// Access identifier number + long long int idn(void) const; + /// Set identifier number + void idn(long long int n) { + _v_or_idn = + reinterpret_cast((static_cast(n) << 1) | static_cast(1)); + rehash(); + } + /// Return identifier or X_INTRODUCED plus identifier number + ASTString str(void) const; + /// Access declaration + VarDecl* decl(void) const { + Expression* d = _decl; + while (d && d->isa()) d = d->cast()->_decl; + return Expression::cast(d); + } + /// Set declaration + void decl(VarDecl* d); + /// Redirect to another Id \a id + void redirect(Id* id) { + assert(_decl == NULL || _decl->isa()); + _decl = id; + } + /// Recompute hash value + void rehash(void); +}; +/// \brief Type-inst identifier expression +class TIId : public Expression { +protected: + /// The string identifier + ASTString _v; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_TIID; + /// Constructor + TIId(const Location& loc, const std::string& v); + /// Access identifier + ASTString v(void) const { return _v; } + /// Set identifier + void v(const ASTString& val) { _v = val; } + /// Check whether it is an enum identifier (starting with two $ signs) + bool isEnum(void) const { return _v.c_str()[0] == '$'; } + /// Recompute hash value + void rehash(void); +}; +/// \brief Anonymous variable expression +class AnonVar : public Expression { +public: + /// The identifier of this expression type + static const ExpressionId eid = E_ANON; + /// Constructor + AnonVar(const Location& loc); + /// Recompute hash value + void rehash(void); +}; +/// \brief Array literal expression +class ArrayLit : public Expression { + friend class Expression; + +protected: + /// The array + union { + /// An expression vector (if _flag_2==false) + ASTExprVecO* _v; + /// Another array literal (if _flag_2==true) + ArrayLit* _al; + } _u; + /// The declared array dimensions + // If _flag_2 is true, then this is an array view. In that case, + // the _dims array holds the sliced dimensions + ASTIntVec _dims; + /// Set compressed vector (initial repetitions are removed) + void compress(const std::vector& v, const std::vector& dims); + +public: + /// Index conversion from slice to original + int origIdx(int i) const; + /// Get element \a i of a sliced array + Expression* slice_get(int i) const; + /// Set element \a i of a sliced array + void slice_set(int i, Expression* e); + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_ARRAYLIT; + /// Constructor + ArrayLit(const Location& loc, const std::vector& v, + const std::vector >& dims); + /// Constructor (existing content) + ArrayLit(const Location& loc, ArrayLit& v, const std::vector >& dims); + /// Constructor (one-dimensional, existing content) + ArrayLit(const Location& loc, ArrayLit& v); + /// Constructor (one-dimensional) + ArrayLit(const Location& loc, const std::vector& v); + /// Constructor (two-dimensional) + ArrayLit(const Location& loc, const std::vector >& v); + /// Constructor for slices + ArrayLit(const Location& loc, ArrayLit* v, const std::vector >& dims, + const std::vector >& slice); + /// Constructor (one-dimensional) + ArrayLit(const Location& loc, const std::vector& v); + /// Recompute hash value + void rehash(void); + + // The following methods are only used for copying + + /// Access value + ASTExprVec getVec(void) const { + assert(!_flag_2); + return _u._v; + } + /// Set value + void setVec(const ASTExprVec& val) { + assert(!_flag_2); + _u._v = val.vec(); + } + /// Get underlying array (if this is an array slice) or NULL + ArrayLit* getSliceLiteral(void) const { return _flag_2 ? _u._al : NULL; } + /// Get underlying _dims vector + ASTIntVec dimsInternal(void) const { return _dims; } + + /// Return number of dimensions + int dims(void) const; + /// Return minimum index of dimension \a i + int min(int i) const; + /// Return maximum index of dimension \a i + int max(int i) const; + /// Return the length of the array + int length(void) const; + /// Turn into 1d array (only used at the end of flattening) + void make1d(void); + /// Check if this array was produced by flattening + bool flat(void) const { return _flag_1; } + /// Set whether this array was produced by flattening + void flat(bool b) { _flag_1 = b; } + /// Return size of underlying array + unsigned int size(void) const { return (_flag_2 || _u._v->flag()) ? length() : _u._v->size(); } + /// Access element \a i + Expression* operator[](int i) const { + return (_flag_2 || _u._v->flag()) ? slice_get(i) : (*_u._v)[i]; + } + /// Set element \a i + void set(int i, Expression* e) { + if (_flag_2 || _u._v->flag()) { + slice_set(i, e); + } else { + (*_u._v)[i] = e; + } + } +}; +/// \brief Array access expression +class ArrayAccess : public Expression { +protected: + /// The array to access + Expression* _v; + /// The indexes (for all array dimensions) + ASTExprVec _idx; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_ARRAYACCESS; + /// Constructor + ArrayAccess(const Location& loc, Expression* v, const std::vector& idx); + /// Constructor + ArrayAccess(const Location& loc, Expression* v, ASTExprVec idx); + /// Access value + Expression* v(void) const { return _v; } + /// Set value + void v(Expression* val) { _v = val; } + /// Access index sets + ASTExprVec idx(void) const { return _idx; } + /// Set index sets + void idx(const ASTExprVec& idx) { _idx = idx; } + /// Recompute hash value + void rehash(void); +}; +/** + * \brief Generators for comprehensions + * + * A generator consists of a list of variable declarations, one for + * each generated variable, and the expression to generate. E.g., + * the Zinc expression [ x[i,j,k] | i,j in 1..10, k in 1..5] contains + * two generators. The first one has variable declarations for i and j + * and the expression 1..10, and the second one has a variable declaration + * for k and the expression 1..5. + * + */ +class Generator { + friend class Comprehension; + +protected: + /// Variable declarations + std::vector _v; + /// in-expression + Expression* _in; + /// where-expression + Expression* _where; + +public: + /// Allocate + Generator(const std::vector& v, Expression* in, Expression* where); + /// Allocate + Generator(const std::vector& v, Expression* in, Expression* where); + /// Allocate + Generator(const std::vector& v, Expression* in, Expression* where); + /// Allocate + Generator(const std::vector& v, Expression* in, Expression* where); + /// Allocate single where clause (without generator) at position \a pos + Generator(int pos, Expression* where); +}; +/// \brief A list of generators with one where-expression +struct Generators { + /// %Generators + std::vector _g; + /// Constructor + Generators(void) {} +}; +/// \brief An expression representing an array- or set-comprehension +class Comprehension : public Expression { + friend class Expression; + +protected: + /// The expression to generate + Expression* _e; + /// A list of generator expressions + ASTExprVec _g; + /// A list of indices where generators start + ASTIntVec _g_idx; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_COMP; + /// Constructor + Comprehension(const Location& loc, Expression* e, Generators& g, bool set); + /// Recompute hash value + void rehash(void); + /// Whether comprehension is a set + bool set(void) const; + + /// Return number of generators + int n_generators(void) const; + /// Return "in" expression for generator \a i + Expression* in(int i); + /// Return "in" expression for generator \a i + const Expression* in(int i) const; + /// Return number of declarations for generator \a i + int n_decls(int i) const; + /// Return declaration \a i for generator \a gen + VarDecl* decl(int gen, int i); + /// Return declaration \a i for generator \a gen + const VarDecl* decl(int gen, int i) const; + /// Return where clause for generator \a i + Expression* where(int i); + /// Return where clause for generator \a i + const Expression* where(int i) const; + /// Return generator body + Expression* e(void) const { return _e; } + /// Set generator body + void e(Expression* e0) { _e = e0; } + /// Re-construct (used for copying) + void init(Expression* e, Generators& g); +}; +/// \brief If-then-else expression +class ITE : public Expression { + friend class Expression; + +protected: + /// List of if-then-pairs + ASTExprVec _e_if_then; + /// Else-expression + Expression* _e_else; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_ITE; + /// Constructor + ITE(const Location& loc, const std::vector& e_if_then, Expression* e_else); + int size(void) const { return _e_if_then.size() / 2; } + Expression* e_if(int i) { return _e_if_then[2 * i]; } + Expression* e_then(int i) { return _e_if_then[2 * i + 1]; } + Expression* e_else(void) { return _e_else; } + const Expression* e_if(int i) const { return _e_if_then[2 * i]; } + const Expression* e_then(int i) const { return _e_if_then[2 * i + 1]; } + const Expression* e_else(void) const { return _e_else; } + void e_then(int i, Expression* e) { _e_if_then[2 * i + 1] = e; } + void e_else(Expression* e) { _e_else = e; } + /// Recompute hash value + void rehash(void); + /// Re-construct (used for copying) + void init(const std::vector& e_if_then, Expression* e_else); +}; + +/// Type of binary operators +enum BinOpType { + BOT_PLUS, + BOT_MINUS, + BOT_MULT, + BOT_DIV, + BOT_IDIV, + BOT_MOD, + BOT_POW, + BOT_LE, + BOT_LQ, + BOT_GR, + BOT_GQ, + BOT_EQ, + BOT_NQ, + BOT_IN, + BOT_SUBSET, + BOT_SUPERSET, + BOT_UNION, + BOT_DIFF, + BOT_SYMDIFF, + BOT_INTERSECT, + BOT_PLUSPLUS, + BOT_EQUIV, + BOT_IMPL, + BOT_RIMPL, + BOT_OR, + BOT_AND, + BOT_XOR, + BOT_DOTDOT +}; +/// \brief Binary-operator expression +class BinOp : public Expression { +protected: + /// Left hand side expression + Expression* _e0; + /// Right hand side expression + Expression* _e1; + /// The predicate or function declaration (or NULL) + FunctionI* _decl; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_BINOP; + /// Constructor + BinOp(const Location& loc, Expression* e0, BinOpType op, Expression* e1); + /// Access left hand side + Expression* lhs(void) const { return _e0; } + /// Set left hand side + void lhs(Expression* e) { _e0 = e; } + /// Access right hand side + Expression* rhs(void) const { return _e1; } + /// Set right hand side + void rhs(Expression* e) { _e1 = e; } + /// Access argument \a i + Expression* arg(int i) { + assert(i == 0 || i == 1); + return i == 0 ? _e0 : _e1; + } + /// Return number of arguments + unsigned int n_args(void) const { return 2; } + /// Access declaration + FunctionI* decl(void) const { return _decl; } + /// Set declaration + void decl(FunctionI* f) { _decl = f; } + /// Return string representation of the operator + ASTString opToString(void) const; + /// Recompute hash value + void rehash(void); + /// Return operator type + BinOpType op(void) const; +}; + +/// Type of unary operators +enum UnOpType { UOT_NOT, UOT_PLUS, UOT_MINUS }; +/// \brief Unary-operator expressions +class UnOp : public Expression { +protected: + /// %Expression + Expression* _e0; + /// The predicate or function declaration (or NULL) + FunctionI* _decl; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_UNOP; + /// Constructor + UnOp(const Location& loc, UnOpType op, Expression* e); + /// Access expression + Expression* e(void) const { return _e0; } + /// Set expression + void e(Expression* e0) { _e0 = e0; } + /// Access argument \a i + Expression* arg(int i) { + assert(i == 0); + return _e0; + } + /// Return number of arguments + unsigned int n_args(void) const { return 1; } + /// Access declaration + FunctionI* decl(void) const { return _decl; } + /// Set declaration + void decl(FunctionI* f) { _decl = f; } + ASTString opToString(void) const; + /// Recompute hash value + void rehash(void); + /// Return operator type + UnOpType op(void) const; +}; + +/// \brief A predicate or function call expression +class Call : public Expression { + friend class Expression; + +protected: + union { + /// Identifier of called predicate or function + ASTStringO* _id; + /// The predicate or function declaration (or NULL) + FunctionI* _decl; + } _u_id; + union { + /// Single-argument call (tagged pointer) + Expression* _oneArg; + /// Arguments to the call + ASTExprVecO* _args; + } _u; + /// Check if _u_id contains an id or a decl + bool hasId(void) const; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_CALL; + /// Constructor + Call(const Location& loc, const std::string& id, const std::vector& args); + /// Constructor + Call(const Location& loc, const ASTString& id, const std::vector& args); + /// Access identifier + ASTString id(void) const; + /// Set identifier (overwrites decl) + void id(const ASTString& i); + /// Number of arguments + unsigned int n_args(void) const { + return _u._oneArg->isUnboxedVal() || _u._oneArg->isTagged() ? 1 : _u._args->size(); + } + /// Access argument \a i + Expression* arg(int i) const { + assert(i < n_args()); + if (_u._oneArg->isUnboxedVal() || _u._oneArg->isTagged()) { + assert(i == 0); + return _u._oneArg->isUnboxedVal() ? _u._oneArg : _u._oneArg->untag(); + } else { + return (*_u._args)[i]; + } + } + /// Set argument \a i + void arg(int i, Expression* e) { + assert(i < n_args()); + if (_u._oneArg->isUnboxedVal() || _u._oneArg->isTagged()) { + assert(i == 0); + _u._oneArg = e->isUnboxedVal() ? e : e->tag(); + } else { + (*_u._args)[i] = e; + } + } + /// Set arguments + void args(const ASTExprVec& a) { + if (a.size() == 1) { + _u._oneArg = a[0]->isUnboxedVal() ? a[0] : a[0]->tag(); + } else { + _u._args = a.vec(); + assert(!_u._oneArg->isTagged()); + } + } + /// Access declaration + FunctionI* decl(void) const; + /// Set declaration (overwrites id) + void decl(FunctionI* f); + /// Recompute hash value + void rehash(void); +}; +/// \brief A variable declaration expression +class VarDecl : public Expression { +protected: + /// Type-inst of the declared variable + TypeInst* _ti; + /// Identifier + Id* _id; + /// Initialisation expression (can be NULL) + Expression* _e; + /// Flattened version of the VarDecl + WeakRef _flat; + /// Integer payload + int _payload; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_VARDECL; + /// Constructor + VarDecl(const Location& loc, TypeInst* ti, const std::string& id, Expression* e = NULL); + /// Constructor + VarDecl(const Location& loc, TypeInst* ti, const ASTString& id, Expression* e = NULL); + // Constructor + VarDecl(const Location& loc, TypeInst* ti, long long int idn, Expression* e = NULL); + /// Constructor + VarDecl(const Location& loc, TypeInst* ti, Id* id, Expression* e = NULL); + + /// Access TypeInst + TypeInst* ti(void) const { return _ti; } + /// Set TypeInst + void ti(TypeInst* t) { _ti = t; } + /// Access identifier + Id* id(void) const { return _id; } + /// Access initialisation expression + Expression* e(void) const; + /// Set initialisation expression + void e(Expression* rhs); + /// Access flattened version + VarDecl* flat(void) { return _flat() ? _flat()->cast() : NULL; } + /// Set flattened version + void flat(VarDecl* vd); + + /// Recompute hash value + void rehash(void); + /// Whether variable is toplevel + bool toplevel(void) const; + /// Whether variable is toplevel + void toplevel(bool t); + /// Whether variable is introduced + bool introduced(void) const; + /// Whether variable is introduced + void introduced(bool t); + /// Whether variable has been evaluated + bool evaluated(void) const; + /// Whether variable has been evaluated + void evaluated(bool t); + /// Access payload + int payload(void) const { return _payload; } + /// Set payload + void payload(int i) { _payload = i; } + /// Put current value on trail + void trail(void); +}; + +class EnvI; +class CopyMap; + +/// \brief %Let expression +class Let : public Expression { + friend Expression* copy(EnvI& env, CopyMap& m, Expression* e, bool followIds, bool copyFundecls, + bool isFlatModel); + friend class Expression; + +protected: + /// List of local declarations + ASTExprVec _let; + /// Copy of original local declarations + ASTExprVec _let_orig; + /// Body of the let + Expression* _in; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_LET; + /// Constructor + Let(const Location& loc, const std::vector& let, Expression* in); + /// Recompute hash value + void rehash(void); + + /// Access local declarations + ASTExprVec let(void) const { return _let; } + /// Access local declarations + ASTExprVec let_orig(void) const { return _let_orig; } + /// Access body + Expression* in(void) const { return _in; } + + /// Remember current let bindings + void pushbindings(void); + /// Restore previous let bindings + void popbindings(void); +}; + +/// \brief Type-inst expression +class TypeInst : public Expression { +protected: + /// Ranges of an array expression + ASTExprVec _ranges; + /// Declared domain (or NULL) + Expression* _domain; + +public: + /// The identifier of this expression type + static const ExpressionId eid = E_TI; + /// Constructor + TypeInst(const Location& loc, const Type& t, ASTExprVec ranges, + Expression* domain = NULL); + /// Constructor + TypeInst(const Location& loc, const Type& t, Expression* domain = NULL); + + /// Access ranges + ASTExprVec ranges(void) const { return _ranges; } + /// Access domain + Expression* domain(void) const { return _domain; } + //// Set domain + void domain(Expression* d) { _domain = d; } + + /// Set ranges to \a ranges + void setRanges(const std::vector& ranges); + bool isarray(void) const { return _ranges.size() > 0; } + bool hasTiVariable(void) const; + /// Recompute hash value + void rehash(void); + /// Check if domain is computed from right hand side of variable + bool computedDomain(void) const { return _flag_1; } + /// Set if domain is computed from right hand side of variable + void setComputedDomain(bool b) { _flag_1 = b; } + /// Check if this TypeInst represents an enum + bool isEnum(void) const { return _flag_2; } + /// Set if this TypeInst represents an enum + void setIsEnum(bool b) { _flag_2 = b; } +}; + +/** + * \brief Base-class for items + */ +class Item : public ASTNode { +protected: + /// Location of the item + Location _loc; + +public: + /// Identifier of the concrete item type + enum ItemId { + II_INC = Expression::EID_END + 1, + II_VD, + II_ASN, + II_CON, + II_SOL, + II_OUT, + II_FUN, + II_END = II_FUN + }; + ItemId iid(void) const { return static_cast(_id); } + + const Location& loc(void) const { return _loc; } + +protected: + /// Constructor + Item(const Location& loc, const ItemId& iid) : ASTNode(iid), _loc(loc) { _flag_1 = false; } + +public: + /// Test if item is of type \a T + template + bool isa(void) const { + return _id == T::iid; + } + /// Cast item to type \a T* + template + T* cast(void) { + assert(isa()); + return static_cast(this); + } + /// Cast expression to type \a const T* + template + const T* cast(void) const { + assert(isa()); + return static_cast(this); + } + /// Cast item to type \a T* or NULL if types do not match + template + T* dyn_cast(void) { + return isa() ? static_cast(this) : NULL; + } + /// Cast item to type \a const T* or NULL if types do not match + template + const T* dyn_cast(void) const { + return isa() ? static_cast(this) : NULL; + } + + /// Cast item to type \a T* + template + static T* cast(Item* i) { + return i == NULL ? NULL : i->cast(); + } + /// Cast item to type \a const T* + template + static const T* cast(const Item* i) { + return i == NULL ? NULL : i->cast(); + } + /// Cast item to type \a T* or NULL if types do not match + template + static T* dyn_cast(Item* i) { + return i == NULL ? NULL : i->dyn_cast(); + } + /// Cast item to type \a const T* or NULL if types do not match + template + static const T* dyn_cast(const Item* i) { + return i == NULL ? NULL : i->dyn_cast(); + } + + /// Check if item should be removed + bool removed(void) const { return _flag_1; } + /// Set flag to remove item + void remove(void) { _flag_1 = true; } + /// Unset remove item flag (only possible if not already removed by compact()) + void unremove(void) { _flag_1 = false; } +}; + +class Model; + +/// \brief Include item +class IncludeI : public Item { +protected: + /// Filename to include + ASTString _f; + /// Model for that file + Model* _m; + +public: + /// The identifier of this item type + static const ItemId iid = II_INC; + /// Constructor + IncludeI(const Location& loc, const ASTString& f); + /// Access filename + ASTString f(void) const { return _f; } + /// Set filename + void f(const ASTString& nf) { _f = nf; } + /// Access model + Model* m(void) const { return _m; } + /// Set the model + void m(Model* m0, bool own = true) { + assert(_m == NULL || m0 == NULL); + _m = m0; + _flag_2 = own; + } + bool own(void) const { return _flag_2; } +}; + +/// \brief Variable declaration item +class VarDeclI : public Item { +protected: + /// The declaration expression + VarDecl* _e; + +public: + /// The identifier of this item type + static const ItemId iid = II_VD; + /// Constructor + VarDeclI(const Location& loc, VarDecl* e); + /// Access expression + VarDecl* e(void) const { return _e; } + /// Set expression + void e(VarDecl* vd) { _e = vd; } + /// Flag used during compilation + bool flag(void) const { return _flag_2; } + /// Set flag used during compilation + void flag(bool b) { _flag_2 = b; } +}; + +/// \brief Assign item +class AssignI : public Item { +protected: + /// Identifier of variable to assign to + ASTString _id; + /// Expression to assign to the variable + Expression* _e; + /// Declaration of the variable to assign to + VarDecl* _decl; + +public: + /// The identifier of this item type + static const ItemId iid = II_ASN; + /// Constructor + AssignI(const Location& loc, const std::string& id, Expression* e); + /// Access identifier + ASTString id(void) const { return _id; } + /// Access expression + Expression* e(void) const { return _e; } + /// Set expression + void e(Expression* e0) { _e = e0; } + /// Access declaration + VarDecl* decl(void) const { return _decl; } + /// Set declaration + void decl(VarDecl* d) { _decl = d; } +}; + +/// \brief Constraint item +class ConstraintI : public Item { +protected: + /// Constraint expression + Expression* _e; + +public: + /// The identifier of this item type + static const ItemId iid = II_CON; + /// Constructor + ConstraintI(const Location& loc, Expression* e); + /// Access expression + Expression* e(void) const { return _e; } + /// Set expression + void e(Expression* e0) { _e = e0; } + /// Flag used during compilation + bool flag(void) const { return _flag_2; } + /// Set flag used during compilation + void flag(bool b) { _flag_2 = b; } +}; + +/// \brief Solve item +class SolveI : public Item { +protected: + /// Solve item annotation + Annotation _ann; + /// Expression for minimisation/maximisation (or NULL) + Expression* _e; + /// Constructor + SolveI(const Location& loc, Expression* e); + +public: + /// The identifier of this item type + static const ItemId iid = II_SOL; + /// Type of solving + enum SolveType { ST_SAT, ST_MIN, ST_MAX }; + /// Allocate solve satisfy item + static SolveI* sat(const Location& loc); + /// Allocate solve minimize item + static SolveI* min(const Location& loc, Expression* e); + /// Allocate solve maximize item + static SolveI* max(const Location& loc, Expression* e); + /// Access solve annotation + const Annotation& ann(void) const { return _ann; } + /// Access solve annotation + Annotation& ann(void) { return _ann; } + /// Access expression for optimisation + Expression* e(void) const { return _e; } + /// Set expression for optimisation + void e(Expression* e0) { _e = e0; } + /// Return type of solving + SolveType st(void) const; + /// Set type of solving + void st(SolveType s); +}; + +/// \brief Output item +class OutputI : public Item { +protected: + /// Expression to output + Expression* _e; + +public: + /// The identifier of this item type + static const ItemId iid = II_OUT; + /// Constructor + OutputI(const Location& loc, Expression* e); + /// Access expression + Expression* e(void) const { return _e; } + /// Update expression + void e(Expression* e) { _e = e; } +}; + +class EnvI; + +/// \brief Function declaration item +class FunctionI : public Item { +protected: + /// Identifier of this function + ASTString _id; + /// Type-inst of the return value + TypeInst* _ti; + /// List of parameter declarations + ASTExprVec _params; + /// Annotation + Annotation _ann; + /// Function body (or NULL) + Expression* _e; + /// Whether function is defined in the standard library + bool _from_stdlib; + +public: + /// The identifier of this item type + static const ItemId iid = II_FUN; + + /// Type of builtin expression-valued functions + typedef Expression* (*builtin_e)(EnvI&, Call*); + /// Type of builtin int-valued functions + typedef IntVal (*builtin_i)(EnvI&, Call*); + /// Type of builtin bool-valued functions + typedef bool (*builtin_b)(EnvI&, Call*); + /// Type of builtin float-valued functions + typedef FloatVal (*builtin_f)(EnvI&, Call*); + /// Type of builtin set-valued functions + typedef IntSetVal* (*builtin_s)(EnvI&, Call*); + /// Type of builtin string-valued functions + typedef std::string (*builtin_str)(EnvI&, Call*); + + /// Builtin functions (or NULL) + struct { + builtin_e e; + builtin_i i; + builtin_f f; + builtin_b b; + builtin_s s; + builtin_str str; + } _builtins; + + /// Constructor + FunctionI(const Location& loc, const std::string& id, TypeInst* ti, + const std::vector& params, Expression* e = NULL); + /// Constructor + FunctionI(const Location& loc, const ASTString& id, TypeInst* ti, + const ASTExprVec& params, Expression* e = NULL); + + /// Access identifier + ASTString id(void) const { return _id; } + /// Access TypeInst + TypeInst* ti(void) const { return _ti; } + /// Access parameters + ASTExprVec params(void) const { return _params; } + /// Access annotation + const Annotation& ann(void) const { return _ann; } + /// Access annotation + Annotation& ann(void) { return _ann; } + /// Access body + Expression* e(void) const { return _e; } + /// Set body + void e(Expression* b) { _e = b; } + + /** \brief Compute return type given argument types \a ta + */ + Type rtype(EnvI& env, const std::vector& ta, bool strictEnums); + /** \brief Compute return type given argument types \a ta + */ + Type rtype(EnvI& env, const std::vector& ta, bool strictEnums); + /** \brief Compute expected type of argument \a n given argument types \a ta + */ + Type argtype(EnvI& env, const std::vector& ta, int n); + + /// Return whether function is defined in the standard library + bool from_stdlib(void) const { return _from_stdlib; }; + + /// Mark for GC + void mark(void) { + _gc_mark = 1; + loc().mark(); + } +}; + +/** + * \brief Visitor for expressions + * + * This class implements no-ops for all expression types. + * Override the methods to implement custom behaviour. + */ +class EVisitor { +public: + /// Visit integer literal + void vIntLit(const IntLit&) {} + /// Visit floating point literal + void vFloatLit(const FloatLit&) {} + /// Visit Boolean literal + void vBoolLit(const BoolLit&) {} + /// Visit set literal + void vSetLit(const SetLit&) {} + /// Visit string literal + void vStringLit(const StringLit&) {} + /// Visit identifier + void vId(const Id&) {} + /// Visit anonymous variable + void vAnonVar(const AnonVar&) {} + /// Visit array literal + void vArrayLit(const ArrayLit&) {} + /// Visit array access + void vArrayAccess(const ArrayAccess&) {} + /// Visit array comprehension + void vComprehension(const Comprehension&) {} + /// Visit array comprehension (only generator \a gen_i) + void vComprehensionGenerator(const Comprehension&, int gen_i) { (void)gen_i; } + /// Visit if-then-else + void vITE(const ITE&) {} + /// Visit binary operator + void vBinOp(const BinOp&) {} + /// Visit unary operator + void vUnOp(const UnOp&) {} + /// Visit call + void vCall(const Call&) {} + /// Visit let + void vLet(const Let&) {} + /// Visit variable declaration + void vVarDecl(const VarDecl&) {} + /// Visit type inst + void vTypeInst(const TypeInst&) {} + /// Visit TIId + void vTIId(const TIId&) {} + /// Determine whether to enter node + bool enter(Expression* e) { return true; } + /// Exit node after processing has finished + void exit(Expression* e) {} +}; + +/// Statically allocated constants +class Constants { +private: + /// Garbage collection root set for constants + Model* m; + +public: + /// Literal true + BoolLit* lit_true; + /// Variable bound to true + VarDecl* var_true; + /// Literal false + BoolLit* lit_false; + /// Variable bound to false + VarDecl* var_false; + /// Special variable to signal compiler to ignore result + VarDecl* var_ignore; + /// Infinite set + SetLit* infinity; + /// Function item used to keep track of redefined variables + FunctionI* var_redef; + /// Literal absent value + Expression* absent; + /// Identifiers for builtins + struct { + ASTString forall; + ASTString forall_reif; + ASTString exists; + ASTString clause; + ASTString bool2int; + ASTString int2float; + ASTString bool2float; + ASTString assert; + ASTString trace; + + ASTString sum; + ASTString lin_exp; + ASTString element; + + ASTString show; + ASTString fix; + ASTString output; + + struct { + ASTString lin_eq; + ASTString lin_le; + ASTString lin_ne; + ASTString plus; + ASTString minus; + ASTString times; + ASTString div; + ASTString mod; + ASTString lt; + ASTString le; + ASTString gt; + ASTString ge; + ASTString eq; + ASTString ne; + } int_; + + struct { + ASTString lin_eq; + ASTString lin_le; + ASTString lin_ne; + ASTString plus; + ASTString minus; + ASTString times; + ASTString div; + ASTString mod; + ASTString lt; + ASTString le; + ASTString gt; + ASTString ge; + ASTString eq; + ASTString ne; + } int_reif; + + struct { + ASTString lin_eq; + ASTString lin_le; + ASTString lin_lt; + ASTString lin_ne; + ASTString plus; + ASTString minus; + ASTString times; + ASTString div; + ASTString mod; + ASTString lt; + ASTString le; + ASTString gt; + ASTString ge; + ASTString eq; + ASTString ne; + ASTString in; + ASTString dom; + } float_; + + struct { + ASTString lin_eq; + ASTString lin_le; + ASTString lin_lt; + ASTString lin_ne; + ASTString plus; + ASTString minus; + ASTString times; + ASTString div; + ASTString mod; + ASTString lt; + ASTString le; + ASTString gt; + ASTString ge; + ASTString eq; + ASTString ne; + ASTString in; + } float_reif; + + ASTString bool_eq; + ASTString bool_eq_reif; + ASTString array_bool_or; + ASTString array_bool_and; + ASTString bool_clause; + ASTString bool_clause_reif; + ASTString bool_xor; + ASTString set_eq; + ASTString set_in; + ASTString set_card; + ASTString pow; + + ASTString introduced_var; + ASTString anonEnumFromStrings; + } ids; + + /// Identifiers for Boolean contexts + struct { + Id* root; + Id* pos; + Id* neg; + Id* mix; + } ctx; + /// Common annotations + struct { + Id* output_var; + ASTString output_array; + Id* add_to_output; + Id* output_only; + Id* mzn_check_var; + ASTString mzn_check_enum_var; + Id* is_defined_var; + ASTString defines_var; + Id* is_reverse_map; + Id* promise_total; + Id* maybe_partial; + Id* _export; + ASTString doc_comment; + ASTString mzn_path; + ASTString is_introduced; + Id* user_cut; // MIP + Id* lazy_constraint; // MIP + Id* mzn_break_here; + Id* rhs_from_assignment; + Id* domain_change_constraint; + ASTString global_register; + } ann; + + /// Command line options + struct { /// basic MiniZinc command line options + ASTString cmdlineData_str; + ASTString cmdlineData_short_str; + ASTString datafile_str; + ASTString datafile_short_str; + ASTString globalsDir_str; + ASTString globalsDir_alt_str; + ASTString globalsDir_short_str; + ASTString help_str; + ASTString help_short_str; + ASTString ignoreStdlib_str; + ASTString include_str; + ASTString inputFromStdin_str; + ASTString instanceCheckOnly_str; + ASTString no_optimize_str; + ASTString no_optimize_alt_str; + ASTString no_outputOzn_str; + ASTString no_outputOzn_short_str; + ASTString no_typecheck_str; + ASTString newfzn_str; + ASTString outputBase_str; + ASTString outputFznToStdout_str; + ASTString outputFznToStdout_alt_str; + ASTString outputOznToFile_str; + ASTString outputOznToStdout_str; + ASTString outputFznToFile_str; + ASTString outputFznToFile_alt_str; + ASTString outputFznToFile_short_str; + ASTString rangeDomainsOnly_str; + ASTString statistics_str; + ASTString statistics_short_str; + ASTString stdlib_str; + ASTString verbose_str; + ASTString verbose_short_str; + ASTString version_str; + ASTString werror_str; + + struct { + ASTString all_sols_str; + ASTString fzn_solver_str; + } solver; + + } cli; + + /// options strings to find setting in Options map + struct { + ASTString cmdlineData; + ASTString datafile; + ASTString datafiles; + ASTString fznToStdout; + ASTString fznToFile; + ASTString globalsDir; + ASTString ignoreStdlib; + ASTString includeDir; + ASTString includePaths; + ASTString instanceCheckOnly; + ASTString inputFromStdin; + ASTString model; + ASTString newfzn; + ASTString noOznOutput; + ASTString optimize; + ASTString outputBase; + ASTString oznToFile; + ASTString oznToStdout; + ASTString rangeDomainsOnly; + ASTString statistics; + ASTString stdlib; + ASTString typecheck; + ASTString verbose; + ASTString werror; + + struct { + ASTString allSols; + ASTString numSols; + ASTString threads; + ASTString fzn_solver; + ASTString fzn_flags; + ASTString fzn_flag; + ASTString fzn_time_limit_ms; + ASTString fzn_sigint; + } solver; + + } opts; + + /// categories of the command line interface options + struct { + ASTString general; + ASTString io; + ASTString solver; + ASTString translation; + } cli_cat; + + /// Keep track of allocated integer literals + std::unordered_map integerMap; + /// Keep track of allocated float literals + std::unordered_map floatMap; + /// Constructor + Constants(void); + /// Return shared BoolLit + BoolLit* boollit(bool b) { return b ? lit_true : lit_false; } + static const int max_array_size = std::numeric_limits::max() / 2; +}; + +/// Return static instance +Constants& constants(void); + +} // namespace MiniZinc + +#include + +#endif diff --git a/include/minizinc/ast.hpp b/include/minizinc/ast.hpp new file mode 100644 index 0000000..d57a1aa --- /dev/null +++ b/include/minizinc/ast.hpp @@ -0,0 +1,643 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +namespace MiniZinc { + +inline bool Expression::equal(const Expression* e0, const Expression* e1) { + if (e0 == e1) return true; + if (e0 == NULL || e1 == NULL) return false; + if (e0->isUnboxedInt() || e1->isUnboxedInt()) return false; + if (e0->isUnboxedFloatVal() || e1->isUnboxedFloatVal()) { + if (e0->isUnboxedFloatVal() && e1->isUnboxedFloatVal()) { + return e0->unboxedFloatToFloatVal() == e1->unboxedFloatToFloatVal(); + } + return false; + } + if (e0->_id != e1->_id) return false; + if (e0->type() != e1->type()) return false; + if (e0->hash() != e1->hash()) return false; + return equal_internal(e0, e1); +} + +inline void Expression::type(const Type& t) { + if (isUnboxedVal()) { + assert(!isUnboxedInt() || t == Type::parint()); + assert(!isUnboxedFloatVal() || t == Type::parfloat()); + return; + } + if (eid() == E_VARDECL) { + this->cast()->id()->_type = t; + } else if (eid() == E_ID && this->cast()->decl()) { + assert(_type.bt() == Type::BT_UNKNOWN || _type.dim() == t.dim() || t.dim() != -1); + this->cast()->decl()->_type = t; + } + _type = t; +} + +inline IntLit::IntLit(const Location& loc, IntVal v) + : Expression(loc, E_INTLIT, Type::parint()), _v(v) { + rehash(); +} + +inline IntLit* IntLit::a(MiniZinc::IntVal v) { + if (v.isFinite()) { + IntLit* ret = intToUnboxedInt(v.toInt()); + if (ret) { + return ret; + } + } + + std::unordered_map::iterator it = constants().integerMap.find(v); + if (it == constants().integerMap.end() || it->second() == NULL) { + IntLit* il = new IntLit(Location().introduce(), v); + if (it == constants().integerMap.end()) { + constants().integerMap.insert(std::make_pair(v, il)); + } else { + it->second = il; + } + return il; + } else { + return it->second()->cast(); + } +} + +inline IntLit* IntLit::aEnum(IntVal v, unsigned int enumId) { + if (enumId == 0) return a(v); + IntLit* il = new IntLit(Location().introduce(), v); + Type tt(il->type()); + tt.enumId(enumId); + il->type(tt); + return il; +} + +inline ASTString Location::LocVec::filename(void) const { + return static_cast(_data[0]); +} +inline unsigned int Location::LocVec::first_line(void) const { + if (_size == 2) { + static const unsigned int pointerBits = sizeof(IntLit*) * 8; + IntLit* il = static_cast(_data[1]); + long long unsigned int mask = pointerBits <= 32 ? 0xFF : 0xFFFFF; + union { + long long int i; + unsigned long long int u; + } ui; + ui.i = il->v().toInt(); + return static_cast(ui.u & mask); + } else { + IntLit* il = static_cast(_data[1]); + return il->v().toInt(); + } +} +inline unsigned int Location::LocVec::last_line(void) const { + if (_size == 2) { + static const unsigned int pointerBits = sizeof(IntLit*) * 8; + IntLit* il = static_cast(_data[1]); + long long unsigned int first_line_size = pointerBits <= 32 ? 8 : 20; + long long unsigned int mask = pointerBits <= 32 ? 0xFF : 0xFFFFF; + long long unsigned int offsetmask = pointerBits <= 32 ? 0x7F : 0xFFFFF; + union { + long long int i; + unsigned long long int u; + } ui; + ui.i = il->v().toInt(); + // return first line (8 bit) + offset (7 bit) + return static_cast((ui.u & mask) + ((ui.u >> first_line_size) & offsetmask)); + } else { + IntLit* il = static_cast(_data[2]); + return il->v().toInt(); + } +} +inline unsigned int Location::LocVec::first_column(void) const { + if (_size == 2) { + static const unsigned int pointerBits = sizeof(IntLit*) * 8; + IntLit* il = static_cast(_data[1]); + long long unsigned int first_col_offset = pointerBits <= 32 ? 8 + 7 : 20 + 20; + long long unsigned int mask = pointerBits <= 32 ? 0x3F : 0x3FF; + union { + long long int i; + unsigned long long int u; + } ui; + ui.i = il->v().toInt(); + // return first line (8 bit) + offset (7 bit) + return static_cast((ui.u >> first_col_offset) & mask); + } else { + IntLit* il = static_cast(_data[3]); + return il->v().toInt(); + } +} +inline unsigned int Location::LocVec::last_column(void) const { + if (_size == 2) { + static const unsigned int pointerBits = sizeof(IntLit*) * 8; + IntLit* il = static_cast(_data[1]); + long long unsigned int last_col_offset = pointerBits <= 32 ? 8 + 7 + 6 : 20 + 20 + 10; + long long unsigned int mask = pointerBits <= 32 ? 0x7F : 0x3FF; + union { + long long int i; + unsigned long long int u; + } ui; + ui.i = il->v().toInt(); + // return first line (8 bit) + offset (7 bit) + return static_cast((ui.u >> last_col_offset) & mask); + } else { + IntLit* il = static_cast(_data[4]); + return il->v().toInt(); + } +} + +inline FloatLit::FloatLit(const Location& loc, FloatVal v) + : Expression(loc, E_FLOATLIT, Type::parfloat()), _v(v) { + rehash(); +} + +inline FloatLit* FloatLit::a(MiniZinc::FloatVal v) { + if (sizeof(double) <= sizeof(FloatLit*) && v.isFinite()) { + FloatLit* ret = Expression::doubleToUnboxedFloatVal(v.toDouble()); + if (ret) { + return ret; + } + } + + std::unordered_map::iterator it = constants().floatMap.find(v); + if (it == constants().floatMap.end() || it->second() == NULL) { + FloatLit* fl = new FloatLit(Location().introduce(), v); + if (it == constants().floatMap.end()) { + constants().floatMap.insert(std::make_pair(v, fl)); + } else { + it->second = fl; + } + return fl; + } else { + return it->second()->cast(); + } +} + +inline SetLit::SetLit(const Location& loc, const std::vector& v) + : Expression(loc, E_SETLIT, Type()), _v(ASTExprVec(v)) { + _u.isv = NULL; + rehash(); +} + +inline SetLit::SetLit(const Location& loc, ASTExprVec v) + : Expression(loc, E_SETLIT, Type()), _v(v) { + _u.isv = NULL; + rehash(); +} + +inline SetLit::SetLit(const Location& loc, IntSetVal* isv) : Expression(loc, E_SETLIT, Type()) { + _type = Type::parsetint(); + _u.isv = isv; + rehash(); +} + +inline SetLit::SetLit(const Location& loc, FloatSetVal* fsv) : Expression(loc, E_SETLIT, Type()) { + _type = Type::parsetfloat(); + _u.fsv = fsv; + rehash(); +} + +inline BoolLit::BoolLit(const Location& loc, bool v) + : Expression(loc, E_BOOLLIT, Type::parbool()), _v(v) { + rehash(); +} + +inline StringLit::StringLit(const Location& loc, const std::string& v) + : Expression(loc, E_STRINGLIT, Type::parstring()), _v(ASTString(v)) { + rehash(); +} + +inline StringLit::StringLit(const Location& loc, const ASTString& v) + : Expression(loc, E_STRINGLIT, Type::parstring()), _v(v) { + rehash(); +} + +inline Id::Id(const Location& loc, const std::string& v0, VarDecl* decl) + : Expression(loc, E_ID, Type()), _decl(decl) { + v(v0); + rehash(); +} + +inline Id::Id(const Location& loc, const ASTString& v0, VarDecl* decl) + : Expression(loc, E_ID, Type()), _decl(decl) { + v(v0); + rehash(); +} + +inline Id::Id(const Location& loc, long long int idn0, VarDecl* decl) + : Expression(loc, E_ID, Type()), _decl(decl) { + idn(idn0); + rehash(); +} + +inline void Id::decl(VarDecl* d) { _decl = d; } + +inline ASTString Id::v(void) const { + if (_decl && _decl->isa()) { + Expression* d = _decl; + while (d && d->isa()) { + d = d->cast()->_decl; + } + return d->cast()->id()->v(); + } else { + assert((reinterpret_cast(_v_or_idn) & static_cast(1)) == 0); + return ASTString(reinterpret_cast(_v_or_idn)); + } +} + +inline long long int Id::idn(void) const { + if (_decl && _decl->isa()) { + Expression* d = _decl; + while (d && d->isa()) { + d = d->cast()->_decl; + } + return d->cast()->id()->idn(); + } else { + if ((reinterpret_cast(_v_or_idn) & static_cast(1)) == 0) return -1; + long long int i = reinterpret_cast(_v_or_idn) & ~static_cast(1); + return i >> 1; + } +} + +inline TIId::TIId(const Location& loc, const std::string& v) + : Expression(loc, E_TIID, Type()), _v(ASTString(v)) { + rehash(); +} + +inline AnonVar::AnonVar(const Location& loc) : Expression(loc, E_ANON, Type()) { rehash(); } + +inline ArrayLit::ArrayLit(const Location& loc, ArrayLit& v, + const std::vector >& dims) + : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = v._flag_2; + if (_flag_2) { + _u._al = v._u._al; + std::vector d(dims.size() * 2 + v._dims.size() - v.dims() * 2); + for (unsigned int i = static_cast(dims.size()); i--;) { + d[i * 2] = dims[i].first; + d[i * 2 + 1] = dims[i].second; + } + int sliceOffset = static_cast(dims.size()) * 2; + int origSliceOffset = v.dims() * 2; + for (int i = 0; i < _u._al->dims() * 2; i++) { + d[sliceOffset + i] = v._dims[origSliceOffset + i]; + } + _dims = ASTIntVec(d); + } else { + std::vector d(dims.size() * 2); + for (unsigned int i = static_cast(dims.size()); i--;) { + d[i * 2] = dims[i].first; + d[i * 2 + 1] = dims[i].second; + } + if (v._u._v->flag() || d.size() != 2 || d[0] != 1) { + // only allocate dims vector if it is not a 1d array indexed from 1 + _dims = ASTIntVec(d); + } + _u._v = v._u._v; + } + rehash(); +} + +inline ArrayLit::ArrayLit(const Location& loc, ArrayLit& v) : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = v._flag_2; + if (_flag_2) { + _u._al = v._u._al; + std::vector d(2 + v._dims.size() - v.dims() * 2); + d[0] = 1; + d[1] = v.size(); + int sliceOffset = 2; + int origSliceOffset = v.dims() * 2; + for (int i = 0; i < _u._al->dims() * 2; i++) { + d[sliceOffset + i] = v._dims[origSliceOffset + i]; + } + _dims = ASTIntVec(d); + } else { + _u._v = v._u._v; + if (_u._v->flag()) { + std::vector d(2); + d[0] = 1; + d[1] = v.length(); + _dims = ASTIntVec(d); + } else { + // don't allocate dims vector since this is a 1d array indexed from 1 + } + } + rehash(); +} + +inline ArrayLit::ArrayLit(const Location& loc, const std::vector& v) + : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = false; + std::vector d(2); + d[0] = 1; + d[1] = static_cast(v.size()); + compress(v, d); + rehash(); +} + +inline ArrayLit::ArrayLit(const Location& loc, const std::vector& v) + : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = false; + std::vector d(2); + d[0] = 1; + d[1] = static_cast(v.size()); + std::vector vv(v.size()); + for (unsigned int i = 0; i < v.size(); i++) { + vv[i] = v[i](); + } + compress(vv, d); + rehash(); +} + +inline ArrayLit::ArrayLit(const Location& loc, const std::vector >& v) + : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = false; + std::vector dims(4); + dims[0] = 1; + dims[1] = static_cast(v.size()); + dims[2] = 1; + dims[3] = v.size() > 0 ? static_cast(v[0].size()) : 0; + std::vector vv; + for (unsigned int i = 0; i < v.size(); i++) + for (unsigned int j = 0; j < v[i].size(); j++) vv.push_back(v[i][j]); + compress(vv, dims); + rehash(); +} + +inline ArrayAccess::ArrayAccess(const Location& loc, Expression* v, + const std::vector& idx) + : Expression(loc, E_ARRAYACCESS, Type()) { + _v = v; + _idx = ASTExprVec(idx); + rehash(); +} + +inline ArrayAccess::ArrayAccess(const Location& loc, Expression* v, ASTExprVec idx) + : Expression(loc, E_ARRAYACCESS, Type()) { + _v = v; + _idx = idx; + rehash(); +} + +inline void Comprehension::init(Expression* e, Generators& g) { + _e = e; + std::vector es; + std::vector idx; + for (unsigned int i = 0; i < g._g.size(); i++) { + idx.push_back(static_cast(es.size())); + es.push_back(g._g[i]._in); + es.push_back(g._g[i]._where); + for (unsigned int j = 0; j < g._g[i]._v.size(); j++) { + es.push_back(g._g[i]._v[j]); + } + } + idx.push_back(static_cast(es.size())); + _g = ASTExprVec(es); + _g_idx = ASTIntVec(idx); + rehash(); +} +inline Comprehension::Comprehension(const Location& loc, Expression* e, Generators& g, bool set) + : Expression(loc, E_COMP, Type()) { + _flag_1 = set; + init(e, g); +} +inline void ITE::init(const std::vector& e_if_then, Expression* e_else) { + _e_if_then = ASTExprVec(e_if_then); + _e_else = e_else; + rehash(); +} +inline ITE::ITE(const Location& loc, const std::vector& e_if_then, Expression* e_else) + : Expression(loc, E_ITE, Type()) { + init(e_if_then, e_else); +} + +inline BinOp::BinOp(const Location& loc, Expression* e0, BinOpType op, Expression* e1) + : Expression(loc, E_BINOP, Type()), _e0(e0), _e1(e1), _decl(NULL) { + _sec_id = op; + rehash(); +} + +inline UnOp::UnOp(const Location& loc, UnOpType op, Expression* e) + : Expression(loc, E_UNOP, Type()), _e0(e), _decl(NULL) { + _sec_id = op; + rehash(); +} + +inline bool Call::hasId(void) const { + return (reinterpret_cast(_u_id._decl) & static_cast(1)) == 0; +} + +inline ASTString Call::id(void) const { return hasId() ? _u_id._id : decl()->id(); } + +inline void Call::id(const ASTString& i) { + _u_id._id = i.aststr(); + assert(hasId()); + assert(decl() == NULL); +} + +inline FunctionI* Call::decl(void) const { + return hasId() ? NULL + : reinterpret_cast(reinterpret_cast(_u_id._decl) & + ~static_cast(1)); +} + +inline void Call::decl(FunctionI* f) { + assert(f != NULL); + _u_id._decl = + reinterpret_cast(reinterpret_cast(f) | static_cast(1)); +} + +inline Call::Call(const Location& loc, const std::string& id0, const std::vector& args) + : Expression(loc, E_CALL, Type()) { + id(ASTString(id0)); + if (args.size() == 1) { + _u._oneArg = args[0]->isUnboxedVal() ? args[0] : args[0]->tag(); + } else { + _u._args = ASTExprVec(args).vec(); + } + rehash(); + assert(hasId()); + assert(decl() == NULL); +} + +inline Call::Call(const Location& loc, const ASTString& id0, const std::vector& args) + : Expression(loc, E_CALL, Type()) { + id(ASTString(id0)); + if (args.size() == 1) { + _u._oneArg = args[0]->isUnboxedVal() ? args[0] : args[0]->tag(); + } else { + _u._args = ASTExprVec(args).vec(); + } + rehash(); + assert(hasId()); + assert(decl() == NULL); +} + +inline VarDecl::VarDecl(const Location& loc, TypeInst* ti, const ASTString& id, Expression* e) + : Expression(loc, E_VARDECL, ti ? ti->type() : Type()), _id(NULL), _flat(NULL) { + _id = new Id(loc, id, this); + _flag_1 = true; + _flag_2 = false; + _ti = ti; + _e = e; + _id->type(type()); + _payload = 0; + rehash(); +} + +inline VarDecl::VarDecl(const Location& loc, TypeInst* ti, long long int idn, Expression* e) + : Expression(loc, E_VARDECL, ti ? ti->type() : Type()), _id(NULL), _flat(NULL) { + _id = new Id(loc, idn, this); + _flag_1 = true; + _flag_2 = false; + _ti = ti; + _e = e; + _id->type(type()); + _payload = 0; + rehash(); +} + +inline VarDecl::VarDecl(const Location& loc, TypeInst* ti, const std::string& id, Expression* e) + : Expression(loc, E_VARDECL, ti->type()), _id(NULL), _flat(NULL) { + _id = new Id(loc, ASTString(id), this); + _flag_1 = true; + _flag_2 = false; + _ti = ti; + _e = e; + _id->type(type()); + _payload = 0; + rehash(); +} + +inline VarDecl::VarDecl(const Location& loc, TypeInst* ti, Id* id, Expression* e) + : Expression(loc, E_VARDECL, ti->type()), _id(NULL), _flat(NULL) { + if (id->idn() == -1) + _id = new Id(loc, id->v(), this); + else + _id = new Id(loc, id->idn(), this); + _flag_1 = true; + _flag_2 = false; + _ti = ti; + _e = e; + _id->type(type()); + _payload = 0; + rehash(); +} + +inline Expression* VarDecl::e(void) const { + return (_e == nullptr || _e->isUnboxedVal()) ? _e : _e->untag(); +} + +inline void VarDecl::e(Expression* rhs) { + assert(rhs == NULL || !rhs->isa() || rhs->cast() != _id); + _e = rhs; +} + +inline bool VarDecl::toplevel(void) const { return _flag_1; } +inline void VarDecl::toplevel(bool t) { _flag_1 = t; } +inline bool VarDecl::introduced(void) const { return _flag_2; } +inline void VarDecl::introduced(bool t) { _flag_2 = t; } +inline bool VarDecl::evaluated(void) const { return _e->isUnboxedVal() || _e->isTagged(); } +inline void VarDecl::evaluated(bool t) { + if (!_e->isUnboxedVal()) { + if (t) + _e = _e->tag(); + else + _e = _e->untag(); + } +} +inline void VarDecl::flat(VarDecl* vd) { _flat = WeakRef(vd); } + +inline TypeInst::TypeInst(const Location& loc, const Type& type, ASTExprVec ranges, + Expression* domain) + : Expression(loc, E_TI, type), _ranges(ranges), _domain(domain) { + _flag_1 = false; + _flag_2 = false; + rehash(); +} + +inline TypeInst::TypeInst(const Location& loc, const Type& type, Expression* domain) + : Expression(loc, E_TI, type), _domain(domain) { + _flag_1 = false; + _flag_2 = false; + rehash(); +} + +inline IncludeI::IncludeI(const Location& loc, const ASTString& f) + : Item(loc, II_INC), _f(f), _m(NULL) {} + +inline VarDeclI::VarDeclI(const Location& loc, VarDecl* e) : Item(loc, II_VD), _e(e) {} + +inline AssignI::AssignI(const Location& loc, const std::string& id, Expression* e) + : Item(loc, II_ASN), _id(ASTString(id)), _e(e), _decl(NULL) {} + +inline ConstraintI::ConstraintI(const Location& loc, Expression* e) : Item(loc, II_CON), _e(e) {} + +inline SolveI::SolveI(const Location& loc, Expression* e) : Item(loc, II_SOL), _e(e) {} +inline SolveI* SolveI::sat(const Location& loc) { + SolveI* si = new SolveI(loc, NULL); + si->_sec_id = ST_SAT; + return si; +} +inline SolveI* SolveI::min(const Location& loc, Expression* e) { + SolveI* si = new SolveI(loc, e); + si->_sec_id = ST_MIN; + return si; +} +inline SolveI* SolveI::max(const Location& loc, Expression* e) { + SolveI* si = new SolveI(loc, e); + si->_sec_id = ST_MAX; + return si; +} +inline SolveI::SolveType SolveI::st(void) const { return static_cast(_sec_id); } +inline void SolveI::st(SolveI::SolveType s) { _sec_id = s; } + +inline OutputI::OutputI(const Location& loc, Expression* e) : Item(loc, II_OUT), _e(e) {} + +inline FunctionI::FunctionI(const Location& loc, const std::string& id, TypeInst* ti, + const std::vector& params, Expression* e) + : Item(loc, II_FUN), _id(ASTString(id)), _ti(ti), _params(ASTExprVec(params)), _e(e) { + _builtins.e = NULL; + _builtins.b = NULL; + _builtins.f = NULL; + _builtins.i = NULL; + _builtins.s = NULL; + _builtins.str = NULL; + _from_stdlib = + (loc.filename() == "builtins.mzn" || loc.filename().endsWith("/builtins.mzn") || + loc.filename() == "stdlib.mzn" || loc.filename().endsWith("/stdlib.mzn") || + loc.filename() == "mzncc_builtins.mzn" || loc.filename().endsWith("/mzncc_builtins.mzn") || + loc.filename() == "flatzinc_builtins.mzn" || + loc.filename().endsWith("/flatzinc_builtins.mzn")); +} + +inline FunctionI::FunctionI(const Location& loc, const ASTString& id, TypeInst* ti, + const ASTExprVec& params, Expression* e) + : Item(loc, II_FUN), _id(id), _ti(ti), _params(params), _e(e) { + _builtins.e = NULL; + _builtins.b = NULL; + _builtins.f = NULL; + _builtins.i = NULL; + _builtins.s = NULL; + _builtins.str = NULL; + _from_stdlib = + (loc.filename() == "builtins.mzn" || loc.filename().endsWith("/builtins.mzn") || + loc.filename() == "stdlib.mzn" || loc.filename().endsWith("/stdlib.mzn") || + loc.filename() == "mzncc_builtins.mzn" || loc.filename().endsWith("/mzncc_builtins.mzn") || + loc.filename() == "flatzinc_builtins.mzn" || + loc.filename().endsWith("/flatzinc_builtins.mzn")); +} + +} // namespace MiniZinc diff --git a/include/minizinc/astexception.hh b/include/minizinc/astexception.hh new file mode 100644 index 0000000..e893cf3 --- /dev/null +++ b/include/minizinc/astexception.hh @@ -0,0 +1,81 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_ASTEXCEPTION_HH__ +#define __MINIZINC_ASTEXCEPTION_HH__ + +#include +#include +#include + +#include + +namespace MiniZinc { + +class SyntaxError : public Exception { +protected: + Location _loc; + +public: + SyntaxError(const Location& loc, const std::string& msg) : Exception(msg), _loc(loc) {} + virtual ~SyntaxError(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: syntax error"; } + const Location& loc(void) const { return _loc; } +}; + +class LocationException : public Exception { +protected: + Location _loc; + +public: + LocationException(EnvI& env, const Location& loc, const std::string& msg); + virtual ~LocationException(void) throw() {} + const Location& loc(void) const { return _loc; } +}; + +class TypeError : public LocationException { +public: + TypeError(EnvI& env, const Location& loc, const std::string& msg) + : LocationException(env, loc, msg) {} + ~TypeError(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: type error"; } +}; + +class EvalError : public LocationException { +public: + EvalError(EnvI& env, const Location& loc, const std::string& msg) + : LocationException(env, loc, msg) {} + EvalError(EnvI& env, const Location& loc, const std::string& msg, const ASTString& name) + : LocationException(env, loc, msg + " '" + name.str() + "'") {} + ~EvalError(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: evaluation error"; } +}; + +class ModelInconsistent : public LocationException { +public: + ModelInconsistent(EnvI& env, const Location& loc) + : LocationException(env, loc, "model inconsistency detected") {} + ~ModelInconsistent(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: warning"; } +}; + +class ResultUndefinedError : public LocationException { +public: + ResultUndefinedError(EnvI& env, const Location& loc, const std::string& msg); + ~ResultUndefinedError(void) throw() {} + virtual const char* what(void) const throw() { + return "MiniZinc: result of evaluation is undefined"; + } +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/astiterator.hh b/include/minizinc/astiterator.hh new file mode 100644 index 0000000..6d5abf7 --- /dev/null +++ b/include/minizinc/astiterator.hh @@ -0,0 +1,356 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_ASTITERATOR_HH__ +#define __MINIZINC_ASTITERATOR_HH__ + +#include +#include + +namespace MiniZinc { + +/** + * \brief Bottom-up iterator for expressions + */ +template +class BottomUpIterator { +protected: + /// The visitor to call back during iteration + T& _t; + + /// Stack item + struct C { + /// Expression on the stack + Expression* _e; + /// Whether this expression has been visited before + bool _done; + /// If part of a generator expression, which one it is + int _gen_i; + /// Constructor + C(Expression* e) : _e(e), _done(false), _gen_i(-1) {} + /// Constructor for generator expression + C(Expression* e, int gen_i) : _e(e), _done(true), _gen_i(gen_i) {} + }; + + /// Push all elements of \a v onto \a stack + template + void pushVec(std::vector& stack, ASTExprVec v) { + for (unsigned int i = 0; i < v.size(); i++) stack.push_back(C(v[i])); + } + +public: + /// Constructor + BottomUpIterator(T& t) : _t(t) {} + /// Run iterator on expression \a e + void run(Expression* e); +}; + +template +void bottomUp(T& t, Expression* e) { + BottomUpIterator(t).run(e); +} + +/** + * \brief Leaf iterator for expressions + */ +template +class TopDownIterator { +protected: + /// The visitor to call back during iteration + T& _t; + + /// Push all elements of \a v onto \a stack + template + static void pushVec(std::vector& stack, ASTExprVec v) { + for (unsigned int i = 0; i < v.size(); i++) stack.push_back(v[i]); + } + +public: + /// Constructor + TopDownIterator(T& t) : _t(t) {} + /// Run iterator on expression \a e + void run(Expression* e); +}; + +template +void topDown(T& t, Expression* e) { + TopDownIterator(t).run(e); +} + +/* IMPLEMENTATION */ + +template +void BottomUpIterator::run(Expression* root) { + std::vector stack; + if (_t.enter(root)) stack.push_back(C(root)); + while (!stack.empty()) { + C& c = stack.back(); + if (c._e == NULL) { + stack.pop_back(); + continue; + } + if (c._done) { + switch (c._e->eid()) { + case Expression::E_INTLIT: + _t.vIntLit(*c._e->template cast()); + break; + case Expression::E_FLOATLIT: + _t.vFloatLit(*c._e->template cast()); + break; + case Expression::E_SETLIT: + _t.vSetLit(*c._e->template cast()); + break; + case Expression::E_BOOLLIT: + _t.vBoolLit(*c._e->template cast()); + break; + case Expression::E_STRINGLIT: + _t.vStringLit(*c._e->template cast()); + break; + case Expression::E_ID: + _t.vId(*c._e->template cast()); + break; + case Expression::E_ANON: + _t.vAnonVar(*c._e->template cast()); + break; + case Expression::E_ARRAYLIT: + _t.vArrayLit(*c._e->template cast()); + break; + case Expression::E_ARRAYACCESS: + _t.vArrayAccess(*c._e->template cast()); + break; + case Expression::E_COMP: + if (c._gen_i >= 0) { + _t.vComprehensionGenerator(*c._e->template cast(), c._gen_i); + } else { + _t.vComprehension(*c._e->template cast()); + } + break; + case Expression::E_ITE: + _t.vITE(*c._e->template cast()); + break; + case Expression::E_BINOP: + _t.vBinOp(*c._e->template cast()); + break; + case Expression::E_UNOP: + _t.vUnOp(*c._e->template cast()); + break; + case Expression::E_CALL: + _t.vCall(*c._e->template cast()); + break; + case Expression::E_VARDECL: + _t.vVarDecl(*c._e->template cast()); + break; + case Expression::E_LET: + _t.vLet(*c._e->template cast()); + break; + case Expression::E_TI: + _t.vTypeInst(*c._e->template cast()); + break; + case Expression::E_TIID: + _t.vTIId(*c._e->template cast()); + break; + } + _t.exit(c._e); + stack.pop_back(); + } else { + c._done = true; + Expression* ce = c._e; + for (ExpressionSetIter it = ce->ann().begin(); it != ce->ann().end(); ++it) { + if (_t.enter(*it)) stack.push_back(C(*it)); + } + if (_t.enter(ce)) { + switch (ce->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_ID: + case Expression::E_TIID: + break; + case Expression::E_SETLIT: + pushVec(stack, ce->template cast()->v()); + break; + case Expression::E_ARRAYLIT: { + for (unsigned int i = 0; i < ce->cast()->size(); i++) + stack.push_back((*ce->cast())[i]); + } break; + case Expression::E_ARRAYACCESS: + pushVec(stack, ce->template cast()->idx()); + stack.push_back(C(ce->template cast()->v())); + break; + case Expression::E_COMP: { + Comprehension* comp = ce->template cast(); + stack.push_back(C(comp->e())); + for (unsigned int i = comp->n_generators(); i--;) { + for (unsigned int j = comp->n_decls(i); j--;) { + stack.push_back(C(comp->decl(i, j))); + } + if (comp->in(i)) { + stack.push_back(C(comp->where(i))); + stack.push_back(C(comp, i)); + stack.push_back(C(comp->in(i))); + } else { + stack.push_back(C(comp, i)); + stack.push_back(C(comp->where(i))); + } + } + } break; + case Expression::E_ITE: { + ITE* ite = ce->template cast(); + stack.push_back(C(ite->e_else())); + for (int i = 0; i < ite->size(); i++) { + stack.push_back(C(ite->e_if(i))); + stack.push_back(C(ite->e_then(i))); + } + } break; + case Expression::E_BINOP: + stack.push_back(C(ce->template cast()->rhs())); + stack.push_back(C(ce->template cast()->lhs())); + break; + case Expression::E_UNOP: + stack.push_back(C(ce->template cast()->e())); + break; + case Expression::E_CALL: + for (unsigned int i = 0; i < ce->template cast()->n_args(); i++) + stack.push_back(ce->template cast()->arg(i)); + break; + case Expression::E_VARDECL: + stack.push_back(C(ce->template cast()->e())); + stack.push_back(C(ce->template cast()->ti())); + break; + case Expression::E_LET: + stack.push_back(C(ce->template cast()->in())); + pushVec(stack, ce->template cast()->let()); + break; + case Expression::E_TI: + stack.push_back(C(ce->template cast()->domain())); + pushVec(stack, ce->template cast()->ranges()); + break; + } + } else { + c._e = NULL; + } + } + } +} + +template +void TopDownIterator::run(Expression* root) { + std::vector stack; + if (_t.enter(root)) stack.push_back(root); + while (!stack.empty()) { + Expression* e = stack.back(); + stack.pop_back(); + if (e == NULL) { + continue; + } + if (!_t.enter(e)) continue; + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) { + stack.push_back(*it); + } + switch (e->eid()) { + case Expression::E_INTLIT: + _t.vIntLit(*e->template cast()); + break; + case Expression::E_FLOATLIT: + _t.vFloatLit(*e->template cast()); + break; + case Expression::E_SETLIT: + _t.vSetLit(*e->template cast()); + pushVec(stack, e->template cast()->v()); + break; + case Expression::E_BOOLLIT: + _t.vBoolLit(*e->template cast()); + break; + case Expression::E_STRINGLIT: + _t.vStringLit(*e->template cast()); + break; + case Expression::E_ID: + _t.vId(*e->template cast()); + break; + case Expression::E_ANON: + _t.vAnonVar(*e->template cast()); + break; + case Expression::E_ARRAYLIT: + _t.vArrayLit(*e->template cast()); + for (unsigned int i = 0; i < e->cast()->size(); i++) + stack.push_back((*e->cast())[i]); + break; + case Expression::E_ARRAYACCESS: + _t.vArrayAccess(*e->template cast()); + pushVec(stack, e->template cast()->idx()); + stack.push_back(e->template cast()->v()); + break; + case Expression::E_COMP: + _t.vComprehension(*e->template cast()); + { + Comprehension* comp = e->template cast(); + for (unsigned int i = comp->n_generators(); i--;) { + stack.push_back(comp->where(i)); + stack.push_back(comp->in(i)); + for (unsigned int j = comp->n_decls(i); j--;) { + stack.push_back(comp->decl(i, j)); + } + } + stack.push_back(comp->e()); + } + break; + case Expression::E_ITE: + _t.vITE(*e->template cast()); + { + ITE* ite = e->template cast(); + stack.push_back(ite->e_else()); + for (int i = 0; i < ite->size(); i++) { + stack.push_back(ite->e_if(i)); + stack.push_back(ite->e_then(i)); + } + } + break; + case Expression::E_BINOP: + _t.vBinOp(*e->template cast()); + stack.push_back(e->template cast()->rhs()); + stack.push_back(e->template cast()->lhs()); + break; + case Expression::E_UNOP: + _t.vUnOp(*e->template cast()); + stack.push_back(e->template cast()->e()); + break; + case Expression::E_CALL: + _t.vCall(*e->template cast()); + for (unsigned int i = 0; i < e->template cast()->n_args(); i++) + stack.push_back(e->template cast()->arg(i)); + break; + case Expression::E_VARDECL: + _t.vVarDecl(*e->template cast()); + stack.push_back(e->template cast()->e()); + stack.push_back(e->template cast()->ti()); + break; + case Expression::E_LET: + _t.vLet(*e->template cast()); + stack.push_back(e->template cast()->in()); + pushVec(stack, e->template cast()->let()); + break; + case Expression::E_TI: + _t.vTypeInst(*e->template cast()); + stack.push_back(e->template cast()->domain()); + pushVec(stack, e->template cast()->ranges()); + break; + case Expression::E_TIID: + _t.vTIId(*e->template cast()); + break; + } + } +} + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/aststring.hh b/include/minizinc/aststring.hh new file mode 100644 index 0000000..83b74dd --- /dev/null +++ b/include/minizinc/aststring.hh @@ -0,0 +1,191 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_ASTSTRING_HH__ +#define __MINIZINC_ASTSTRING_HH__ + +#include + +#include +#include +#include +#include + +namespace MiniZinc { + +class ASTStringO; + +/** + * \brief Handler for ASTStringO objects + */ +class ASTString { +protected: + /// String + ASTStringO* _s; + +public: + /// Default constructor + ASTString(void) : _s(NULL) {} + /// Constructor + ASTString(ASTStringO* s) : _s(s) {} + /// Constructor + ASTString(const std::string& s); + /// Copy constructor + ASTString(const ASTString& s); + /// Assignment operator + ASTString& operator=(const ASTString& s); + + /// Size of the string + unsigned int size(void) const; + /// Underlying C string object + const char* c_str(void) const; + /// Conversion to STL string + std::string str(void) const; + /// Underlying string implementation + ASTStringO* aststr(void) const { return _s; } + + /// Return if string is equal to \a s + bool operator==(const ASTString& s) const; + /// Return if string is not equal to \a s + bool operator!=(const ASTString& s) const; + + /// Return if string is equal to \a s + bool operator==(const std::string& s) const; + /// Return if string is not equal to \a s + bool operator!=(const std::string& s) const; + + /// Return if string ends with \a s + bool endsWith(const std::string& s) const; + + /// Return if string begins with \a s + bool beginsWith(const std::string& s) const; + + /// Compute hash value of string + size_t hash(void) const; + + /// Mark string during garbage collection + void mark(void) const; +}; + +/// Hash map from strings to \a T +template +struct ASTStringMap { + /// The map type specialised for ASTString + typedef std::unordered_map t; +}; + +/** + * \brief Print integer set \a s + * \relates Gecode::IntSet + */ +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const ASTString& s) { + return s.size() == 0 ? os : (os << s.c_str()); +} + +} // namespace MiniZinc + +namespace std { +template <> +struct hash { +public: + size_t operator()(const MiniZinc::ASTString& s) const; +}; +} // namespace std + +namespace std { +template <> +struct equal_to { +public: + bool operator()(const MiniZinc::ASTString& s0, const MiniZinc::ASTString& s1) const; +}; +} // namespace std + +namespace MiniZinc { + +/** + * \brief Garbage collected string + */ +class ASTStringO : public ASTChunk { +protected: + /// Constructor + ASTStringO(const std::string& s); + +public: + /// Allocate and initialise as \a s + static ASTStringO* a(const std::string& s); + /// Return underlying C-style string + const char* c_str(void) const { return _data + sizeof(size_t); } + /// Conversion to STL string + std::string str(void) const { return std::string(c_str()); } + /// Return size of string + unsigned int size(void) const { + return static_cast(_size) - static_cast(sizeof(size_t)) - 1; + } + /// Access character at position \a i + char operator[](unsigned int i) { + assert(i < size()); + return _data[sizeof(size_t) + i]; + } + /// Return hash value of string + size_t hash(void) const { return reinterpret_cast(_data)[0]; } + /// Mark for garbage collection + void mark(void) const { _gc_mark = 1; } +}; + +inline ASTString::ASTString(const std::string& s) : _s(ASTStringO::a(s)) {} +inline ASTString::ASTString(const ASTString& s) : _s(s._s) {} +inline ASTString& ASTString::operator=(const ASTString& s) { + _s = s._s; + return *this; +} +inline unsigned int ASTString::size(void) const { return _s ? _s->size() : 0; } +inline const char* ASTString::c_str(void) const { return _s ? _s->c_str() : NULL; } +inline std::string ASTString::str(void) const { return _s ? _s->str() : std::string(""); } +inline void ASTString::mark(void) const { + if (_s) _s->mark(); +} + +inline bool ASTString::operator==(const ASTString& s) const { + return size() == s.size() && (size() == 0 || strncmp(_s->c_str(), s._s->c_str(), size()) == 0); +} +inline bool ASTString::operator!=(const ASTString& s) const { return !(*this == s); } +inline bool ASTString::operator==(const std::string& s) const { + return size() == s.size() && (size() == 0 || strncmp(_s->c_str(), s.c_str(), size()) == 0); +} +inline bool ASTString::operator!=(const std::string& s) const { return !(*this == s); } +inline bool ASTString::endsWith(const std::string& s) const { + return size() >= s.size() && + (size() == 0 || strncmp(_s->c_str() + size() - s.size(), s.c_str(), s.size()) == 0); +} +inline bool ASTString::beginsWith(const std::string& s) const { + return size() >= s.size() && (size() == 0 || strncmp(_s->c_str(), s.c_str(), s.size()) == 0); +} + +inline size_t ASTString::hash(void) const { return _s ? _s->hash() : 0; } + +} // namespace MiniZinc + +namespace std { +inline size_t hash::operator()(const MiniZinc::ASTString& s) const { + return s.hash(); +} +} // namespace std + +namespace std { +inline bool equal_to::operator()(const MiniZinc::ASTString& s0, + const MiniZinc::ASTString& s1) const { + return s0 == s1; +} +} // namespace std + +#endif diff --git a/include/minizinc/astvec.hh b/include/minizinc/astvec.hh new file mode 100644 index 0000000..e12e379 --- /dev/null +++ b/include/minizinc/astvec.hh @@ -0,0 +1,227 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_ASTVEC_HH__ +#define __MINIZINC_ASTVEC_HH__ + +#include + +#include + +namespace MiniZinc { + +class ASTIntVecO; + +/** + * \brief Handler for ASTIntVecO objects + */ +class ASTIntVec { +protected: + /// Vector + ASTIntVecO* _v; + +public: + /// Default constructor + ASTIntVec(void) : _v(NULL) {} + /// Constructor + ASTIntVec(ASTIntVecO* v) : _v(v) {} + /// Constructor + ASTIntVec(const std::vector& v); + /// Copy constructor + ASTIntVec(const ASTIntVec& s); + /// Assignment operator + ASTIntVec& operator=(const ASTIntVec& s); + + /// Size of vector + unsigned int size(void) const; + /// Element access + int& operator[](unsigned int i); + /// Element access + int operator[](unsigned int i) const; + /// Iterator begin + int* begin(void); + /// Iterator end + int* end(void); + /// Mark as alive for garbage collection + void mark(void) const; +}; + +template +class ASTExprVecO; + +/** + * \brief Handler for ASTExprVecO objects + */ +template +class ASTExprVec { +protected: + /// Vector + ASTExprVecO* _v; + +public: + /// Default constructor + ASTExprVec(void) : _v(NULL) {} + /// Constructor + ASTExprVec(ASTExprVecO* v) : _v(v) {} + /// Constructor + ASTExprVec(const std::vector& v); + /// Copy constructor + ASTExprVec(const ASTExprVec& v); + /// Assignment operator + ASTExprVec& operator=(const ASTExprVec& v); + + /// Size of vector + unsigned int size(void) const; + /// Element access + T*& operator[](unsigned int i); + /// Element access + T* operator[](unsigned int i) const; + /// Iterator begin + T** begin(void); + /// Iterator end + T** end(void); + + /// Return vector object + ASTExprVecO* vec(void) const; + /// Mark as alive for garbage collection + void mark(void) const; +}; + +/// Garbage collected integer vector +class ASTIntVecO : public ASTChunk { +protected: + /// Constructor + ASTIntVecO(const std::vector& v); + +public: + /// Allocate and initialise from \a v + static ASTIntVecO* a(const std::vector& v); + /// Return size + unsigned int size(void) const { return static_cast(_size / sizeof(int)); } + /// Return element at position \a i + int& operator[](unsigned int i) { + assert(i < size()); + return reinterpret_cast(_data)[i]; + } + /// Return element at position \a i + int operator[](unsigned int i) const { + assert(i < size()); + return reinterpret_cast(_data)[i]; + } + /// Iterator begin + int* begin(void) { return reinterpret_cast(_data); } + /// Iterator end + int* end(void) { return begin() + size(); } + /// Mark as alive for garbage collection + void mark(void) const { _gc_mark = 1; } +}; + +/// Garbage collected vector of expressions +template +class ASTExprVecO : public ASTVec { +protected: + /// Constructor + ASTExprVecO(const std::vector& v); + +public: + /// Allocate and initialise from \a v + static ASTExprVecO* a(const std::vector& v); + unsigned int size(void) const { return static_cast(_size); } + bool empty(void) const { return size() == 0; } + T& operator[](int i) { + assert(i < static_cast(size())); + return reinterpret_cast(_data[i]); + } + const T operator[](int i) const { + assert(i < static_cast(size())); + return reinterpret_cast(_data[i]); + } + /// Iterator begin + T* begin(void) { return reinterpret_cast(_data); } + /// Iterator end + T* end(void) { return begin() + size(); } + /// Mark as alive for garbage collection + void mark(void) const { _gc_mark = 1; } + /// Check if flag is set + bool flag(void) const { return _flag_1; } + /// Set flag + void flag(bool f) { _flag_1 = f; } +}; + +template +ASTExprVecO::ASTExprVecO(const std::vector& v) : ASTVec(v.size()) { + _flag_1 = false; + for (unsigned int i = static_cast(v.size()); i--;) (*this)[i] = v[i]; +} +template +ASTExprVecO* ASTExprVecO::a(const std::vector& v) { + ASTExprVecO* ao = static_cast*>(alloc(v.size())); + new (ao) ASTExprVecO(v); + return ao; +} + +inline ASTIntVec::ASTIntVec(const std::vector& v) : _v(ASTIntVecO::a(v)) {} +inline ASTIntVec::ASTIntVec(const ASTIntVec& v) : _v(v._v) {} +inline ASTIntVec& ASTIntVec::operator=(const ASTIntVec& v) { + _v = v._v; + return *this; +} + +inline unsigned int ASTIntVec::size(void) const { return _v ? _v->size() : 0; } +inline int& ASTIntVec::operator[](unsigned int i) { return (*_v)[i]; } +inline int ASTIntVec::operator[](unsigned int i) const { return (*_v)[i]; } +inline int* ASTIntVec::begin(void) { return _v ? _v->begin() : NULL; } +inline int* ASTIntVec::end(void) { return _v ? _v->end() : NULL; } +inline void ASTIntVec::mark(void) const { + if (_v) _v->mark(); +} + +template +ASTExprVec::ASTExprVec(const std::vector& v) : _v(ASTExprVecO::a(v)) {} +template +inline ASTExprVec::ASTExprVec(const ASTExprVec& v) : _v(v._v) {} +template +inline ASTExprVec& ASTExprVec::operator=(const ASTExprVec& v) { + _v = v._v; + return *this; +} +template +inline unsigned int ASTExprVec::size(void) const { + return _v ? _v->size() : 0; +} +template +inline T*& ASTExprVec::operator[](unsigned int i) { + return (*_v)[i]; +} +template +inline T* ASTExprVec::operator[](unsigned int i) const { + return (*_v)[i]; +} +template +inline T** ASTExprVec::begin(void) { + return _v ? _v->begin() : NULL; +} +template +inline T** ASTExprVec::end(void) { + return _v ? _v->end() : NULL; +} +template +inline ASTExprVecO* ASTExprVec::vec(void) const { + return _v; +} +template +inline void ASTExprVec::mark(void) const { + if (_v) _v->mark(); +} + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/builtins.hh b/include/minizinc/builtins.hh new file mode 100644 index 0000000..ce48568 --- /dev/null +++ b/include/minizinc/builtins.hh @@ -0,0 +1,24 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_BUILTINS_HH__ +#define __MINIZINC_BUILTINS_HH__ + +#include + +namespace MiniZinc { + +/// Add builtins to the functions defined in \a env.model() +void registerBuiltins(Env& env); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/c_interface.h b/include/minizinc/c_interface.h new file mode 100644 index 0000000..797f33f --- /dev/null +++ b/include/minizinc/c_interface.h @@ -0,0 +1,39 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct _MZNInstance; +typedef struct _MZNInstance* MZNInstance; + +void set_rnd_seed(int seed); + +MZNInstance minizinc_instance_init(const char* mza_file, const char* data_file, const char* solver); +void minizinc_instance_destroy(MZNInstance); + +void minizinc_add_call(MZNInstance, const char* call, ...); +void minizinc_set_solution(MZNInstance, int def, int sol); +void minizinc_output_dict(MZNInstance, bool); +void minizinc_print_hedge(MZNInstance); +void minizinc_set_limit(MZNInstance, int limit); + +void minizinc_push_state(MZNInstance); +void minizinc_pop_state(MZNInstance); + +const char* minizinc_solve(MZNInstance); + +#ifdef __cplusplus +} +#endif diff --git a/include/minizinc/chain_compressor.hh b/include/minizinc/chain_compressor.hh new file mode 100644 index 0000000..0df3e58 --- /dev/null +++ b/include/minizinc/chain_compressor.hh @@ -0,0 +1,103 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_CHAIN_COMPRESSOR_H__ +#define __MINIZINC_CHAIN_COMPRESSOR_H__ + +#include + +#include + +namespace MiniZinc { + +class ChainCompressor { +public: + ChainCompressor(EnvI& env, Model& m, std::vector& deletedVarDecls) + : env(env), m(m), deletedVarDecls(deletedVarDecls){}; + + virtual bool trackItem(Item* i) = 0; + + virtual void compress() = 0; + +protected: + EnvI& env; + Model& m; + std::vector& deletedVarDecls; + + std::multimap items; + typedef std::multimap::iterator iterator; + + void storeItem(VarDecl* v, Item* i) { items.emplace(v, i); } + + void updateCount(); + + unsigned long count(VarDecl* v) { return items.count(v); } + + std::pair find(VarDecl* v) { return items.equal_range(v); }; + + void removeItem(Item* i); + int addItem(Item* i); + + // Replaces the Nth argument of a Call c by Expression e, c must be located on Item i + void replaceCallArgument(Item* i, Call* c, unsigned int n, Expression* e); +}; + +class ImpCompressor : public ChainCompressor { +public: + ImpCompressor(EnvI& env, Model& m, std::vector& deletedVarDecls, + std::vector& boolConstraints0) + : ChainCompressor(env, m, deletedVarDecls), boolConstraints(boolConstraints0){}; + + bool trackItem(Item* i) override; + + void compress() override; + +protected: + std::vector& boolConstraints; + + // Compress two implications. e.g. (x -> y) /\ (y -> z) => x -> z + // In this case i: (y -> z), newLHS: x + // Function returns true if compression was successful (and the implication that contains newLHS + // can be removed) Side effect: Item i might be removed. + bool compressItem(Item* i, VarDecl* newLHS); + + // Constructs a clause constraint item with pos and neg as parameters. + // if pos/neg are not ArrayLit then they will inserted into an ArrayLit. + ConstraintI* constructClause(Expression* pos, Expression* neg); + + ConstraintI* constructHalfReif(Call* call, Id* control); +}; + +class LECompressor : public ChainCompressor { +public: + LECompressor(EnvI& env, Model& m, std::vector& deletedVarDecls) + : ChainCompressor(env, m, deletedVarDecls){}; + + bool trackItem(Item* i) override; + + void compress() override; + +protected: + std::map aliasMap; + + /// Replace the use a variable within an inequality + /// e.g. i: int_lin_le([1,2,3], [a,b,c], 10), oldVar: a, newVar d -> int_lin_le([1,2,3], [d,b,c], + /// 10) Occurrence count is updated for variables involved. + template + void LEReplaceVar(Item* i, VarDecl* oldVar, VarDecl* newVar); + + /// Check if the bounds of two Variables are equal + bool eqBounds(Expression* a, Expression* b); +}; + +} // namespace MiniZinc + +#endif //__MINIZINC_CHAIN_COMPRESSOR_H__ diff --git a/include/minizinc/codegen.hh b/include/minizinc/codegen.hh new file mode 100644 index 0000000..9612d95 --- /dev/null +++ b/include/minizinc/codegen.hh @@ -0,0 +1,924 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Graeme Gange + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_CODEGEN_HH__ +#define __MINIZINC_CODEGEN_HH__ + +// Simple single-pass bytecode compiler for MiniZinc. + +#include +#include +#include + +#include +#include +#include + +namespace MiniZinc { + +struct CodeGen; + +// Location is either a register or global. +class Loc { +private: + Loc(int _x) : x(_x) {} + +public: + static Loc reg(int r) { return r << 1; } + static Loc global(int g) { return (g << 1) | 1; } + + inline bool is_reg(void) const { return !(x & 1); } + inline bool is_global(void) const { return x & 1; } + inline int index(void) const { return x >> 1; } + + int x; +}; + +// Code generators for structures. +// Expressions can be executed in three modes: +// - If Boolean, it is always just executed. +// - If non-Boolean but total, it is similarly just executed. +// - If non-Boolean and partial, we first evaluate its partiality +// constraints in the enclosing Boolean expression, _then_ +// evaluate its numeric component. +// For any function that is not total, we generate two procedures: +// one which is +// A slightly more structured representation for code generation. +class CG_Value { +public: + enum CG_ValueKind { V_Immi, V_Global, V_Reg, V_Proc, V_Label }; + +protected: + CG_Value(CG_ValueKind _kind, int _value) : kind(_kind), value(_value) {} + +public: + CG_Value(void) : kind(V_Immi), value(0) {} + static CG_Value reg(int r) { return CG_Value(V_Reg, r); } + static CG_Value global(int r) { return CG_Value(V_Global, r); } + static CG_Value immi(long long int r) { return CG_Value(V_Immi, r); } + static CG_Value proc(int p) { return CG_Value(V_Proc, p); } + static CG_Value label(int l) { return CG_Value(V_Label, l); } + + CG_ValueKind kind; + long long int value; +}; + +struct CG_Instr { +protected: + CG_Instr(unsigned int _tag) : tag(_tag) {} + +public: + static CG_Instr instr(BytecodeStream::Instr i) { return static_cast(i) << 1; } + static CG_Instr label(unsigned int l) { return (l << 1) + 1; } + + unsigned int tag; + std::vector params; +}; + +struct CG_ProcID { +protected: + CG_ProcID(int _p) : p(_p) {} + +public: + static CG_ProcID builtin(int b) { return CG_ProcID((b << 1) | 1); } + static CG_ProcID proc(int p) { return CG_ProcID(p << 1); } + bool is_builtin(void) const { return p & 1; } + unsigned int id(void) const { return p >> 1; } + + static CG_ProcID of_val(CG_Value v) { + assert(v.kind == CG_Value::V_Proc); + return CG_ProcID(v.value); + } + + unsigned int p; +}; + +struct CG_Builder { + std::vector instrs; + + void append(CG_Builder& o) { + instrs.insert(instrs.end(), o.instrs.begin(), o.instrs.end()); + o.instrs.clear(); + } + void clear(void) { instrs.clear(); } +}; + +// When we bind a non-Boolean expression, we also construct +// a set of conditions we need to insert into any use-contexts. + +// We use CG_Cond to track the conditionality of values. +struct CG_Cond { + // FIXME: Currently not GC'd, so this will leak a bunch of memory. + enum Kind { CC_Reg, CC_Call, CC_And }; + class C_Reg; + class C_Call; + class C_And; + + struct cond_reg { + cond_reg(void) : is_root(0), is_seen(0), is_par(0), reg(-1) {} + cond_reg(int _reg, bool _par) : is_root(0), is_seen(0), is_par(_par), reg(_reg) {} + + int operator*(void) const { + assert(reg >= 0); + return reg; + } + bool has_reg(void) const { return reg >= 0; } + + int is_root : 1; + int is_seen : 1; + int is_par : 1; + int reg : 29; + }; + + class _T { + public: + cond_reg reg[2]; + + _T(void) {} + _T(int r, bool is_par) { + reg[0].reg = r; + reg[0].is_par = is_par; + } + + virtual Kind kind(void) const = 0; + }; + + class T { + T(uintptr_t _p) : p(_p) {} + + public: + T(void) : p(0) {} + + static T ttt(void) { return T(0); } + static T fff(void) { return T(1); } + static T of_ptr(_T* p) { return T(reinterpret_cast(p)); } + + T operator~(void) const { return T(p ^ 1); } + T operator^(bool b) const { return T(p ^ b); } + + bool sign(void) const { return p & 1; } + _T* get(void) const { return reinterpret_cast<_T*>(p & ~((uintptr_t)1)); } + + uintptr_t p; + }; + + static T ttt(void) { return T::ttt(); } + static T fff(void) { return T::fff(); } + + class C_Reg : public _T { + public: + static const Kind _kind = CC_Reg; + Kind kind(void) const { return _kind; } + C_Reg(int reg, bool is_par) : _T(reg, is_par) {} + }; + class C_Call : public _T { + public: + static const Kind _kind = CC_Call; + Kind kind(void) const { return _kind; } + + C_Call(ASTString _ident, BytecodeProc::Mode _m, bool _cse, const std::vector& _ty, + const std::vector& _params) + : ident(_ident), m(std::move(_m)), ty(_ty), cse(_cse), params(_params) {} + + ASTString ident; + // Return Types + argument types + BytecodeProc::Mode m; + bool cse; + std::vector ty; + std::vector params; + }; + class C_And : public _T { + public: + static const Kind _kind = CC_And; + Kind kind(void) const { return _kind; } + + C_And(BytecodeProc::Mode _m, std::vector& _children) : m(_m), children(_children) { + assert(children.size() > 1); + } + + BytecodeProc::Mode m; + std::vector children; + }; + + static T reg(int r, bool is_par) { return T::of_ptr(new C_Reg(r, is_par)); } + + static T call(ASTString ident, BytecodeProc::Mode m, bool cse, const std::vector& ty, + const std::vector& params) { + return _call(ident, m, cse, ty, params); + } + + static T _call(ASTString ident, BytecodeProc::Mode m, bool cse, const std::vector& ty, + const std::vector& params) { + return T::of_ptr(new C_Call(ident, m, cse, ty, params)); + } + + template + static T _forall(BytecodeProc::Mode m, std::vector& args, T next, Args... rest) { + args.push_back(next); + return _forall(m, args, rest...); + } + template + static void clear_seen(It b, It e) { + for (; b != e; ++b) { + if (!b->get()) continue; + b->get()->reg[b->sign()].is_seen = false; + } + } + static T _forall(BytecodeProc::Mode m, std::vector& args) { + size_t count = 0; + for (T x : args) { + _T* p(x.get()); + if (!p) { + // Either true or false. + if (x.sign()) { + clear_seen(args.begin(), args.end()); + return T::fff(); + } + continue; + } + // Otherwise, check if we've already seen this or its negation. + if (p->reg[1 - x.sign()].is_seen || p->reg[1 - x.sign()].is_root) { + clear_seen(args.begin(), args.end()); + return T::fff(); + } + if (p->reg[x.sign()].is_seen || p->reg[x.sign()].is_root) continue; + // Haven't seen this yet, so save and mark it. + p->reg[x.sign()].is_seen = true; + args[count] = x; + count++; + } + clear_seen(args.begin(), args.end()); + args.resize(count); + if (count == 0) { + return T::ttt(); + } + if (count == 1) { + return args[0]; + } + return T::of_ptr(new C_And(m, args)); + } + + template + static T forall(BytecodeProc::Mode m, Args... args) { + std::vector vec; + return _forall(m, vec, args...); + } + static T forall(BytecodeProc::Mode m, std::vector& args) { return _forall(m, args); } + + template + static T _exists(BytecodeProc::Mode m, std::vector& args, T next, Args... rest) { + args.push_back(next); + return _exists(m, args, rest...); + } + static T _exists(BytecodeProc::Mode m, std::vector& args); + + template + static T exists(BytecodeProc::Mode m, Args... args) { + std::vector vec; + return _exists(m, vec, args...); + } + static T exists(BytecodeProc::Mode m, std::vector& args) { return _exists(m, args); } +}; + +// An environment should never outlive its parent. +template +class CG_Env { +private: + CG_Env(CG_Env* _p) : p(_p), sz(p ? p->sz : 0) {} + + // Forbid copy and assignment operators. + CG_Env(const CG_Env& _o) = delete; + CG_Env& operator=(const CG_Env& o) = delete; + +public: + CG_Env(void) : p(nullptr), sz(0) {} + CG_Env(CG_Env&& o) + : bindings(std::move(o.bindings)), + available(std::move(o.available)), + available_csts(std::move(o.available_csts)), + available_ranges(std::move(o.available_ranges)), + cached_conds(std::move(o.cached_conds)), + occurs(std::move(o.occurs)), + p(o.p), + sz(o.sz) {} + + void clear_cached_conds() { + for (CG_Cond::T c : cached_conds) { + CG_Cond::_T* p(c.get()); + bool sign(c.sign()); + p->reg[sign].reg = -1; + } + cached_conds.clear(); + } + void record_cached_cond(CG_Cond::T c) { cached_conds.push_back(c); } + + class NotFound : public std::exception { + public: + NotFound(void) {} + }; + + T lookup(const ASTString& s) const { + auto it(bindings.find(s)); + if (it != bindings.end()) return (*it).second; + if (!p) throw NotFound(); + + return p->lookup(s); + } + + void bind(const ASTString& s, T val) { + auto it(bindings.find(s)); + if (it != bindings.end()) + bindings.erase(it); + else + sz++; + bindings.insert(std::make_pair(s, val)); + + // Invalidate any cached values mentioning s. + auto o_it(occurs.find(s)); + if (o_it != occurs.end()) { + // We're lazy here, in that we don't remove e from + // other occurs lists. + for (Expression* e : (*o_it).second) available.erase(e); + occurs.erase(o_it); + } + } + + // Check whether e is already available in an enclosing environment. + T cache_lookup(Expression* e, ASTStSet e_scope) { + auto it(available.find(e)); + // Anything in the current table is hasn't been invalidated. + if (it != available.end()) { + return (*it).second; + } + if (!p) throw NotFound(); + + // If there's a parent table, check whether we've re-bound + // something in its scope. + for (auto p : bindings) { + if (e_scope.find(p.first) != e_scope.end()) throw NotFound(); + } + // If the scope hasn't been invalidated, check the parent. + return p->cache_lookup(e, e_scope); + } + + void cache_store(Expression* e, ASTStSet e_scope, T val) { + available.insert(std::make_pair(e, val)); + // Add e to the occurs lists for variables in its scope. + for (ASTString s : e_scope) occurs[s].push_back(e); + } + + bool cache_lookup_cst(int x, T& ret) { + auto it(available_csts.find(x)); + // Anything in the current table is hasn't been invalidated. + if (it != available_csts.end()) { + ret = (*it).second; + return true; + } + if (!p) return false; + return p->cache_lookup_cst(x, ret); + } + void cache_store_cst(int x, T val) { available_csts.insert(std::make_pair(x, val)); } + + static uint64_t range_key(int l, int u) { return (((uint64_t)u) << 32ull | (uint64_t)l); } + bool cache_lookup_range(int l, int u, T& ret) { + auto it(available_ranges.find(range_key(l, u))); + // Anything in the current table is hasn't been invalidated. + if (it != available_ranges.end()) { + ret = (*it).second; + return true; + } + if (!p) return false; + return p->cache_lookup_range(l, u, ret); + } + void cache_store_range(int l, int u, T val) { + available_ranges.insert(std::make_pair(range_key(l, u), val)); + } + + static CG_Env* spawn(CG_Env* p) { return new CG_Env(p); } + + unsigned int size(void) const { return sz; } + + typename ASTStringMap::t bindings; + + typename ExprMap::t available; + std::unordered_map available_csts; + std::unordered_map available_ranges; + // std::unordered_map, T> available_ranges; + typename ASTStringMap>::t occurs; + + std::vector cached_conds; + + // Parent environment. + CG_Env* p; + unsigned int sz; +}; + +struct CG { + typedef std::pair Binding; + + // This is currently (probably) sound, but unnecessarily weak. If an expression ever appears in + // root context, the root appearance should dominate. + struct Mode { + enum Strength { Root = 0, Imp = 1, Fun = 2 }; + Mode(BytecodeProc::Mode _m) : m(_m) {} + Mode(Strength s, bool is_neg) { + switch (s) { + case Root: + m = is_neg ? BytecodeProc::ROOT_NEG : BytecodeProc::ROOT; + break; + case Imp: + m = is_neg ? BytecodeProc::IMP_NEG : BytecodeProc::IMP; + break; + case Fun: + m = is_neg ? BytecodeProc::FUN_NEG : BytecodeProc::FUN; + break; + } + } + + bool is_neg(void) const { + switch (m) { + case BytecodeProc::ROOT_NEG: + case BytecodeProc::IMP_NEG: + case BytecodeProc::FUN_NEG: + return true; + default: + return false; + } + } + + Strength strength(void) const { + switch (m) { + case BytecodeProc::ROOT: + case BytecodeProc::ROOT_NEG: + return Root; + case BytecodeProc::IMP: + case BytecodeProc::IMP_NEG: + return Imp; + case BytecodeProc::FUN: + case BytecodeProc::FUN_NEG: + return Fun; + default: + throw InternalError("Unexpected mode."); + } + } + bool is_root(void) const { return strength() == Root; } + + Mode join(Mode o) { + if (is_neg() != o.is_neg()) return BytecodeProc::FUN; + return Mode(std::max(strength(), o.strength()), is_neg()); + } + bool is_submode(Mode o) { return is_neg() == o.is_neg() && strength() <= o.strength(); } + + // Half + Mode operator+(void) const { return Mode(strength() == Root ? Imp : strength(), is_neg()); } + Mode operator-(void) const { return Mode(strength(), !is_neg()); } + + // Switch the current mode to functional. + Mode operator*(void) const { return Mode(Fun, is_neg()); } + + operator BytecodeProc::Mode() const { return m; } + + BytecodeProc::Mode m; + }; + + inline static CG_Value g(int g) { return CG_Value::global(g); } + inline static CG_Value r(int r) { return CG_Value::reg(r); } + inline static CG_Value i(int i) { return CG_Value::immi(i); } + inline static CG_Value l(int l) { return CG_Value::label(l); } + + // Place a non-Boolean value in a register, and collect its partiality. + static Binding bind(Expression* e, CodeGen& cg, CG_Builder& frag); + // Compile a Boolean expression into a condition. + static CG_Cond::T compile(Expression* e, CodeGen& cg, CG_Builder& frag); + // Reify a condion, putting it in a register. + static int force(CG_Cond::T cond, Mode ctx, CodeGen& cg, CG_Builder& frag); + // Force compiled expression or Bind value depending on type + static Binding force_or_bind(Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag); + static int force_or_bind(Expression* e, Mode ctx, std::vector& cond, CodeGen& cg, + CG_Builder& frag); + + static void run(CodeGen& cg, Model* m); + + static int locate_immi(int x, CodeGen& cg, CG_Builder& frag); + + static int vec2array(std::vector vec, CodeGen& cg, CG_Builder& frag); + + static Binding bind(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(SetLit* l, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(ArrayLit* a, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(BinOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(UnOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag); + static Binding bind(Comprehension* let, Mode ctx, CodeGen& cg, CG_Builder& frag); + + static CG_Cond::T compile(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(BinOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(UnOp* op, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag); + static CG_Cond::T compile(Comprehension* let, Mode ctx, CodeGen& cg, CG_Builder& frag); +}; + +inline CG_Cond::T CG_Cond::_exists(BytecodeProc::Mode m, std::vector& args) { + size_t count = 0; + for (T x : args) { + _T* p(x.get()); + if (!p) { + // Either true or false. + if (!x.sign()) { + clear_seen(args.begin(), args.end()); + return T::ttt(); + } + continue; + } + // Otherwise, check if we've already seen this or its negation. + if (p->reg[1 - x.sign()].is_seen || p->reg[x.sign()].is_root) { + clear_seen(args.begin(), args.end()); + return T::ttt(); + } + if (p->reg[x.sign()].is_seen || p->reg[1 - x.sign()].is_root) { + continue; + } + // Haven't seen this yet, so save and mark it. + p->reg[x.sign()].is_seen = true; + args[count] = x; + count++; + } + clear_seen(args.begin(), args.end()); + args.resize(count); + if (count == 0) { + return T::fff(); + } + if (count == 1) { + return args[0]; + } + return ~T::of_ptr(new C_And(-CG::Mode(m), args)); +} + +// Partially compiled bytecode. +struct CG_Proc { + typedef std::vector body_t; + + static unsigned char mode_mask(BytecodeProc::Mode m) { + return 1 << (static_cast(m)); + } + struct mode_iterator { + mode_iterator(unsigned int _x) : x(_x) {} + bool operator!=(const mode_iterator& o) const { return x != o.x; } + BytecodeProc::Mode operator*(void) const { + assert(x); + return static_cast(find_lsb(x)); + } + mode_iterator& operator++(void) { + x &= (x - 1); + return *this; + } + + unsigned int x; + }; + mode_iterator begin(void) { return mode_iterator(available_modes); } + mode_iterator end(void) { return mode_iterator(0); } + + CG_Proc(std::string _ident, int _arity) : ident(_ident), arity(_arity), available_modes(0) {} + + CG_Proc(CG_Proc&& o) : ident(o.ident), arity(o.arity), available_modes(o.available_modes) { + unsigned char rm(available_modes); + while (rm) { + unsigned char m(find_lsb(rm)); + rm &= (rm - 1); + new (_body + m) body_t(std::move(o._body[m])); + o._body[m].~body_t(); + } + o.available_modes = 0; + } + + std::string ident; + unsigned int arity; + + bool is_available(BytecodeProc::Mode m) const { return available_modes & mode_mask(m); } + + std::vector& body(BytecodeProc::Mode m) { + static_assert(BytecodeProc::MAX_MODE < 8 * sizeof(unsigned char), + "Too many modes to to represent as unsigned char."); + + if (!(available_modes & mode_mask(m))) { + available_modes |= mode_mask(m); + new (_body + m) body_t(); + } + return _body[m]; + } + + unsigned char available_modes; + std::vector _body[BytecodeProc::MAX_MODE + 1]; +}; + +// For identifying a call... +struct CallSig { + ASTString id; + std::vector params; + + CallSig(ASTString _id, std::vector _params) : id(_id) { + // Normalize the call types to par. + for (Type p : _params) { + p.ti(Type::TI_PAR); + params.push_back(p); + } + } + + struct HashSig { + size_t operator()(const CallSig& c) const { return c.hash(); } + }; + struct EqSig { + bool operator()(const CallSig& x, const CallSig& y) const { return x == y; } + }; + + bool operator==(const CallSig& o) const { + if (id != o.id || params.size() != o.params.size()) return false; + for (int ii = 0; ii < params.size(); ++ii) { + if (params[ii] != o.params[ii]) return false; + } + return true; + } + + size_t hash(void) const { + size_t h(id.hash()); + for (int ii = 0; ii < params.size(); ++ii) + h ^= params[ii].toInt() + 0x9e3779b9 + (h << 6) + (h >> 2); + return h; + } +}; + +template +struct SigMap { + typedef std::unordered_map t; +}; + +struct CG_FunMap { + struct CG_FunDefn { + CG_FunDefn(ASTString _id) : id(_id) {} + + ASTString id; + std::vector bodies; + }; + + ASTStringMap::t id_map; + std::vector functions; + + void add_body(FunctionI* f) { + // FIXME: Currently discarding anything with var-set. + ASTExprVec params(f->params()); + for (int ii = 0; ii < params.size(); ++ii) { + if (params[ii]->type().is_set() && !params[ii]->type().ispar()) return; + } + + unsigned int fun_id; + ASTString id(f->id()); + auto it(id_map.find(id)); + if (it != id_map.end()) { + fun_id = (*it).second; + } else { + fun_id = functions.size(); + id_map.insert(std::make_pair(id, fun_id)); + functions.push_back(CG_FunDefn(id)); + } + functions[fun_id].bodies.push_back(f); + } + + bool dominates(FunctionI* f, FunctionI* g) { + auto f_params(f->params()); + auto g_params(g->params()); + assert(f_params.size() == g_params.size()); + int sz(f_params.size()); + for (int ii = 0; ii < sz; ++ii) { + if (!Type::bt_subtype(f_params[ii]->type(), g_params[ii]->type(), false)) return false; + } + return true; + } + void filter_bodies(std::vector::iterator& dest, std::vector::iterator b, + std::vector::iterator e, int arg, int sz) { + if (!(b != e)) // Empty partition + return; + if (arg == sz) { + // Find the best candidate between b and e, add it to the output. + // FIXME + auto best(b); + for (++b; b != e; ++b) { + if (dominates(*b, *best)) best = b; + } + (*dest) = (*best); + ++dest; + return; + } + // Otherwise, partition the arguments and recurse. + std::vector::iterator mid = + std::partition(b, e, [arg](FunctionI* b) { return b->params()[arg]->type().ispar(); }); + filter_bodies(dest, b, mid, arg + 1, sz); + filter_bodies(dest, mid, e, arg + 1, sz); + } + + std::vector get_bodies(unsigned int fun_id, const std::vector& args) { + CG_FunDefn& defn(functions[fun_id]); + + // First, restrict consideration to feasible specialisations. + std::vector candidates; + int sz = args.size(); + for (FunctionI* b : defn.bodies) { + ASTExprVec b_params(b->params()); + if (b_params.size() == sz) { + for (int pi = 0; pi < sz; ++pi) { + if (!args[pi].isSubtypeOf(b_params[pi]->type(), false)) goto get_bodies_continue; + } + // Can coerce args to b_params. + candidates.push_back(b); + } + get_bodies_continue: + continue; + } + // Now collect the relevant par-based refinements. + std::vector::iterator dest(candidates.begin()); + filter_bodies(dest, candidates.begin(), candidates.end(), 0, sz); + candidates.erase(dest, candidates.end()); + return candidates; + } + + std::vector get_bodies(const ASTString& ident, const std::vector& args) { + auto it(id_map.find(ident)); + if (it == id_map.end()) return {}; + unsigned int fun_id((*it).second); + return get_bodies(fun_id, args); + } + + std::vector get_bodies(Call* call) { + // Normalize all types to par, so isSubtype does what we want. + std::vector args; + int sz(call->n_args()); + for (int ii = 0; ii < sz; ++ii) { + Type arg(call->arg(ii)->type()); + arg.ti(Type::TI_PAR); + args.push_back(arg); + } + return get_bodies(call->id(), args); + } + + std::pair defines_mode(const ASTString& ident, const std::vector& args, + BytecodeProc::Mode mode) { + GCLock lock; + ASTString nident; + switch (mode) { + case BytecodeProc::ROOT_NEG: + nident = ident.str() + "_neg"; + break; + case BytecodeProc::FUN: + nident = ident.str() + "_reif"; + break; + case BytecodeProc::FUN_NEG: + nident = ident.str() + "_neg_reif"; + break; + case BytecodeProc::IMP: + nident = ident.str() + "_imp"; + break; + case BytecodeProc::IMP_NEG: + nident = ident.str() + "_neg_imp"; + break; + default: + return {false, ASTString("")}; + } + auto it(id_map.find(nident)); + if (it == id_map.end()) { + return {false, nident}; + } + unsigned int fun_id((*it).second); + std::vector reif_args; + reif_args.reserve(args.size() + 1); + for (int ii = 0; ii < args.size(); ++ii) { + Type arg(args[ii]); + arg.ti(Type::TI_PAR); + reif_args.push_back(arg); + } + if (mode != BytecodeProc::ROOT && mode != BytecodeProc::ROOT_NEG) { + reif_args.push_back(Type::parbool()); + } + return {!get_bodies(fun_id, reif_args).empty(), nident}; + } +}; + +struct CodeGen { + typedef unsigned int proc_id; + typedef unsigned int reg_id; + typedef std::pair Binding; + CodeGen(void) + : /*entry_proc(0) + ,*/ + current_env(new CG_Env()), + num_globals(0), + current_reg_count(0), + current_label_count(0) { + register_builtins(); + } + + void append(int proc, BytecodeProc::Mode m, CG_Builder& b) { + std::vector& body(bytecode[proc].body(m)); + + body.insert(body.end(), b.instrs.begin(), b.instrs.end()); + b.clear(); + } + + void env_push(void) { current_env = CG_Env::spawn(current_env); } + void env_pop(void) { + assert(current_env); + CG_Env* c(current_env); + c->clear_cached_conds(); + current_env = current_env->p; + delete c; + } + + // Consult/update the available expressions in the current environment. + Binding cache_lookup(Expression* e); + void cache_store(Expression* e, Binding l); + + // Function resolution + void register_function(FunctionI* f) { fun_map.add_body(f); } + + CG_ProcID resolve_fun(FunctionI* f, bool reserved_name = false); + // CG_ProcID resolve_fun_pred(FunctionI* f); + // CG_ProcID resolve_pred_def(FunctionI* f, BytecodeProc::Mode m); + + std::vector bytecode; // Bytecode we've built + + inline CG_Env& env(void) { return *current_env; } + + CG_Env* current_env; // Where are things in scope? + // Id -> + std::unordered_map> globals_env; + int num_globals; + std::vector req_solver_predicates; + + int add_global(VarDecl* vd, bool input = false) { + globals_env.insert(std::make_pair(vd, std::make_pair(num_globals, input))); + return num_globals++; + } + + int find_global(VarDecl* str) { + auto pair = globals_env.at(str); + return pair.first; + } + + std::vector reg_trail; + unsigned int current_reg_count; // How many registers have been used? + unsigned int current_label_count; + + // Helper information. For an expression, which variables does it refer to? + ASTStSet scope(Expression* e); + ExprMap::t _exp_scope; + + ExprMap::t mode_map; + + // Procedure information + void register_builtins(void); + CG_ProcID register_builtin(std::string s, unsigned int p); + CG_ProcID find_builtin(std::string s); + std::vector> _builtins; + std::unordered_map _proc_map; + + // Procedures yet to be emitted. + CG_FunMap fun_map; + + SigMap>::t dispatch; + + std::unordered_map fun_bodies; + std::vector>> + pending_bodies; +}; + +const char* instr_name(BytecodeStream::Instr i); +const char* agg_name(AggregationCtx::Symbol s); +const char* mode_name(BytecodeProc::Mode m); + +std::tuple find_call_fun(CodeGen& cg, const ASTString& ident, + const Type& ret_type, + std::vector arg_types, + BytecodeProc::Mode m, + bool reserved_name = false); +std::tuple find_call_fun(CodeGen& cg, Call* call, + BytecodeProc::Mode m, + bool reserved_name = false); + +void eval_let_body(Let* let, BytecodeProc::Mode ctx, CodeGen& cg, CG_Builder& frag, + std::vector& conj); + +}; // namespace MiniZinc + +#endif diff --git a/include/minizinc/codegen_support.hh b/include/minizinc/codegen_support.hh new file mode 100644 index 0000000..4c41f67 --- /dev/null +++ b/include/minizinc/codegen_support.hh @@ -0,0 +1,67 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Graeme Gange + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_CODEGEN_SUPPORT_HH__ +#define __MINIZINC_CODEGEN_SUPPORT_HH__ + +#include +#include + +#include +#include +#include + +#ifdef _MSC_VER +// Include header for _BitScanForward intrinsic. +#include +#pragma intrinsic(_BitScanForward) +#endif + +// Support data structures for code generation. +// Maps/tables/etc. +namespace MiniZinc { + +// TODO: Use a more efficient structure for tracking scopes, along +// the lines of fast-mergeable maps. +struct cmp_ASTString { + bool operator()(const ASTString& s, const ASTString& t) const { + if (s.size() != t.size()) return s.size() < t.size(); + return s.size() > 0 && strncmp(s.c_str(), t.c_str(), s.size()) < 0; + } +}; +typedef std::set ASTStSet; + +struct eq_Expression { + bool operator()(Expression* e, Expression* f) const { return Expression::equal(e, f); } +}; +struct hash_Expression { + bool operator()(Expression* e) const { return Expression::hash(e); } +}; + +template +struct ExprMap { + typedef std::unordered_map t; +}; + +#ifdef _MSC_VER +inline unsigned int find_lsb(unsigned int x) { + unsigned long p; + _BitScanForward(&p, x); + return p; +} +#else +// Assuming GCC or clang +inline unsigned int find_lsb(unsigned int x) { return __builtin_ctz(x); } +#endif + +}; // namespace MiniZinc + +#endif diff --git a/include/minizinc/config.hh.in b/include/minizinc/config.hh.in new file mode 100644 index 0000000..e1761a0 --- /dev/null +++ b/include/minizinc/config.hh.in @@ -0,0 +1,29 @@ +#define MZN_VERSION_MAJOR "${libminizinc_VERSION_MAJOR}" + +#define MZN_VERSION_MINOR "${libminizinc_VERSION_MINOR}" + +#define MZN_VERSION_PATCH "${libminizinc_VERSION_PATCH}" + +#define MZN_BUILD_REF "${BUILD_REF}" + +#cmakedefine HAS_DECLSPEC_THREAD + +#cmakedefine HAS_ATTR_THREAD + +#cmakedefine HAS_PIDPATH + +#cmakedefine HAS_GETMODULEFILENAME + +#cmakedefine HAS_GETFILEATTRIBUTES + +#cmakedefine HAS_MEMCPY_S + +#cmakedefine COMPILE_BOOST_MINCUT + +#cmakedefine HAS_DLFCN_H + +#cmakedefine HAS_WINDOWS_H + +#cmakedefine GUROBI_PLUGIN + +#cmakedefine CPLEX_PLUGIN diff --git a/include/minizinc/copy.hh b/include/minizinc/copy.hh new file mode 100644 index 0000000..de6ade3 --- /dev/null +++ b/include/minizinc/copy.hh @@ -0,0 +1,74 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_COPY_HH__ +#define __MINIZINC_COPY_HH__ + +#include + +namespace MiniZinc { + +class CopyMap { +protected: + typedef std::unordered_map ModelMap; + ModelMap model_m; + + ASTNodeWeakMap node_m; + +public: + void insert(Expression* e0, Expression* e1); + Expression* find(Expression* e); + void insert(Item* e0, Item* e1); + Item* find(Item* e); + void insert(Model* e0, Model* e1); + Model* find(Model* e); + void insert(const ASTString& e0, const ASTString& e1); + ASTStringO* find(const ASTString& e); + void insert(IntSetVal* e0, IntSetVal* e1); + IntSetVal* find(IntSetVal* e); + void insert(FloatSetVal* e0, FloatSetVal* e1); + FloatSetVal* find(FloatSetVal* e); + template + void insert(ASTExprVec e0, ASTExprVec e1) { + node_m.insert(e0.vec(), e1.vec()); + } + template + ASTExprVecO* find(ASTExprVec e) { + ASTNode* n = node_m.find(e.vec()); + return static_cast*>(n); + } + void clear() { + model_m.clear(); + node_m.clear(); + } +}; + +/// Create a deep copy of expression \a e +Expression* copy(EnvI& env, Expression* e, bool followIds = false, bool copyFundecls = false, + bool isFlatModel = false); +/// Create a deep copy of item \a i +Item* copy(EnvI& env, Item* i, bool followIds = false, bool copyFundecls = false, + bool isFlatModel = false); +/// Create a deep copy of model \a m +Model* copy(EnvI& env, Model* m); + +/// Create a deep copy of expression \a e +Expression* copy(EnvI& env, CopyMap& map, Expression* e, bool followIds = false, + bool copyFundecls = false, bool isFlatModel = false); +/// Create a deep copy of item \a i +Item* copy(EnvI& env, CopyMap& map, Item* i, bool followIds = false, bool copyFundecls = false, + bool isFlatModel = false); +/// Create a deep copy of model \a m +Model* copy(EnvI& env, CopyMap& map, Model* m, bool isFlatModel = false); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/eval_par.hh b/include/minizinc/eval_par.hh new file mode 100644 index 0000000..627b23e --- /dev/null +++ b/include/minizinc/eval_par.hh @@ -0,0 +1,283 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_EVAL_PAR_HH__ +#define __MINIZINC_EVAL_PAR_HH__ + +#include +#include +#include +#include + +namespace MiniZinc { + +/// Evaluate par int expression \a e +IntVal eval_int(EnvI& env, Expression* e); +/// Evaluate par bool expression \a e +bool eval_bool(EnvI& env, Expression* e); +/// Evaluate par float expression \a e +FloatVal eval_float(EnvI& env, Expression* e); +/// Evaluate an array expression \a e into an array literal +ArrayLit* eval_array_lit(EnvI& env, Expression* e); +/// Evaluate an access to array \a with indices \a idx and return whether +/// access succeeded in \a success +Expression* eval_arrayaccess(EnvI& env, ArrayLit* a, const std::vector& idx, bool& success); +/// Evaluate an array access \a e and return whether access succeeded in \a success +Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e, bool& success); +/// Evaluate a par integer set \a e +IntSetVal* eval_intset(EnvI& env, Expression* e); +/// Evaluate a par bool set \a e +IntSetVal* eval_boolset(EnvI& env, Expression* e); +/// Evaluate a par float set \a e +FloatSetVal* eval_floatset(EnvI& env, Expression* e); +/// Evaluate a par string \a e +std::string eval_string(EnvI& env, Expression* e); +/// Evaluate a par expression \a e and return it wrapped in a literal +Expression* eval_par(EnvI& env, Expression* e); +/// Check if expression \a e satisfies the domain constraint \a domain +bool checkParDomain(EnvI& env, Expression* e, Expression* domain); + +/// Representation for bounds of an integer expression +struct IntBounds { + /// Lower bound + IntVal l; + /// Upper bound + IntVal u; + /// Whether the bounds are valid + bool valid; + /// Constructor + IntBounds(IntVal l0, IntVal u0, bool valid0) : l(l0), u(u0), valid(valid0) {} +}; + +/// Compute bounds of an integer expression +IntBounds compute_int_bounds(EnvI& env, Expression* e); + +/// Representation for bounds of a float expression +struct FloatBounds { + /// Lower bound + FloatVal l; + /// Upper bound + FloatVal u; + /// Whether the bounds are valid + bool valid; + /// Constructor + FloatBounds(FloatVal l0, FloatVal u0, bool valid0) : l(l0), u(u0), valid(valid0) {} +}; + +/// Compute bounds of an integer expression +FloatBounds compute_float_bounds(EnvI& env, Expression* e); + +/** + * \brief Compute bounds of a set of int expression + * + * Returns NULL if bounds cannot be determined + */ +IntSetVal* compute_intset_bounds(EnvI& env, Expression* e); + +template +void eval_comp_array(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, KeepAlive in, + std::vector& a); + +template +void eval_comp_set(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, KeepAlive in, + std::vector& a); + +template +void eval_comp_set(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, IntVal i, KeepAlive in, + std::vector& a) { + { + GCLock lock; + GC::mark(); + e->decl(gen, id)->trail(); + e->decl(gen, id)->e(IntLit::a(i)); + } + CallStackItem csi(env, e->decl(gen, id)->id(), i); + if (id == e->n_decls(gen) - 1) { + bool where = true; + if (e->where(gen) != NULL) { + GCLock lock; + where = e->where(gen)->type().isvar() ? true : eval_bool(env, e->where(gen)); + } + if (where) { + if (gen == e->n_generators() - 1) { + a.push_back(eval.e(env, e->e())); + } else { + if (e->in(gen + 1) == NULL) { + eval_comp_array(env, eval, e, gen + 1, 0, 0, e->in(gen + 1), a); + } else { + KeepAlive nextin; + if (e->in(gen + 1)->type().dim() == 0) { + GCLock lock; + nextin = new SetLit(Location(), eval_intset(env, e->in(gen + 1))); + } else { + GCLock lock; + nextin = eval_array_lit(env, e->in(gen + 1)); + } + if (e->in(gen + 1)->type().dim() == 0) { + eval_comp_set(env, eval, e, gen + 1, 0, nextin, a); + } else { + eval_comp_array(env, eval, e, gen + 1, 0, nextin, a); + } + } + } + } + } else { + eval_comp_set(env, eval, e, gen, id + 1, in, a); + } + GC::untrail(); + e->decl(gen, id)->flat(NULL); +} + +template +void eval_comp_array(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, IntVal i, + KeepAlive in, std::vector& a) { + GC::mark(); + e->decl(gen, id)->trail(); + CallStackItem csi(env, e->decl(gen, id)->id(), i); + if (in() == NULL) { + // this is an assignment generator + Expression* asn = e->where(gen)->type().ispar() ? eval_par(env, e->where(gen)) + : eval.flatten(env, e->where(gen)); + e->decl(gen, id)->e(asn); + e->rehash(); + } else { + ArrayLit* al = in()->cast(); + e->decl(gen, id)->e((*al)[static_cast(i.toInt())]); + e->rehash(); + } + if (id == e->n_decls(gen) - 1) { + bool where = true; + if (e->in(gen) != NULL && e->where(gen) != NULL) { + GCLock lock; + where = e->where(gen)->type().isvar() ? true : eval_bool(env, e->where(gen)); + } + if (where) { + if (gen == e->n_generators() - 1) { + a.push_back(eval.e(env, e->e())); + } else { + if (e->in(gen + 1) == NULL) { + eval_comp_array(env, eval, e, gen + 1, 0, 0, e->in(gen + 1), a); + } else { + KeepAlive nextin; + if (e->in(gen + 1)->type().dim() == 0) { + GCLock lock; + nextin = new SetLit(Location(), eval_intset(env, e->in(gen + 1))); + } else { + GCLock lock; + nextin = eval_array_lit(env, e->in(gen + 1)); + } + if (e->in(gen + 1)->type().dim() == 0) { + eval_comp_set(env, eval, e, gen + 1, 0, nextin, a); + } else { + eval_comp_array(env, eval, e, gen + 1, 0, nextin, a); + } + } + } + } + } else { + eval_comp_array(env, eval, e, gen, id + 1, in, a); + } + GC::untrail(); + e->decl(gen, id)->flat(NULL); +} + +/** + * \brief Evaluate comprehension expression + * + * Calls \a eval.e for every element of the comprehension \a e, + * where \a gen is the current generator, \a id is the current identifier + * in that generator, \a in is the expression of that generator, and + * \a a is the array in which to place the result. + */ +template +void eval_comp_set(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, KeepAlive in, + std::vector& a) { + IntSetVal* isv = eval_intset(env, in()); + if (isv->card().isPlusInfinity()) { + throw EvalError(env, in()->loc(), "comprehension iterates over an infinite set"); + } + IntSetRanges rsi(isv); + Ranges::ToValues rsv(rsi); + for (; rsv(); ++rsv) { + eval_comp_set(env, eval, e, gen, id, rsv.val(), in, a); + } +} + +/** + * \brief Evaluate comprehension expression + * + * Calls \a eval.e for every element of the comprehension \a e, + * where \a gen is the current generator, \a id is the current identifier + * in that generator, \a in is the expression of that generator, and + * \a a is the array in which to place the result. + */ +template +void eval_comp_array(EnvI& env, Eval& eval, Comprehension* e, int gen, int id, KeepAlive in, + std::vector& a) { + ArrayLit* al = in()->cast(); + for (unsigned int i = 0; i < al->size(); i++) { + eval_comp_array(env, eval, e, gen, id, i, in, a); + } +} + +/** + * \brief Evaluate comprehension expression + * + * Calls \a eval.e for every element of the comprehension \a e and + * returns a vector with all the evaluated results. + */ +template +std::vector eval_comp(EnvI& env, Eval& eval, Comprehension* e) { + std::vector a; + if (e->in(0) == NULL) { + eval_comp_array(env, eval, e, 0, 0, 0, e->in(0), a); + } else { + KeepAlive in; + { + GCLock lock; + if (e->in(0)->type().dim() == 0) { + if (e->in(0)->type().isvar()) { + in = new SetLit(Location(), compute_intset_bounds(env, e->in(0))); + } else { + in = new SetLit(Location(), eval_intset(env, e->in(0))); + } + } else { + in = eval_array_lit(env, e->in(0)); + } + } + if (e->in(0)->type().dim() == 0) { + eval_comp_set(env, eval, e, 0, 0, in, a); + } else { + eval_comp_array(env, eval, e, 0, 0, in, a); + } + } + return a; +} + +/** + * \brief Evaluate comprehension expression + * + * Calls \a Eval::e for every element of the comprehension \a e and + * returns a vector with all the evaluated results. + */ +template +std::vector eval_comp(EnvI& env, Comprehension* e) { + Eval eval; + return eval_comp(env, eval, e); +} + +Expression* follow_id(Expression* e); +Expression* follow_id_to_decl(Expression* e); +Expression* follow_id_to_value(Expression* e); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/exception.hh b/include/minizinc/exception.hh new file mode 100644 index 0000000..44281da --- /dev/null +++ b/include/minizinc/exception.hh @@ -0,0 +1,68 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_EXCEPTION_HH__ +#define __MINIZINC_EXCEPTION_HH__ + +#include +#include + +namespace MiniZinc { + +class Exception : public std::exception { +protected: + std::string _msg; + +public: + Exception(const std::string& msg) : _msg(msg) {} + virtual ~Exception(void) throw() {} + virtual const char* what(void) const throw() = 0; + const std::string& msg(void) const { return _msg; } +}; + +class ParseException : public Exception { +public: + ParseException(const std::string& msg) : Exception(msg) {} + ~ParseException(void) throw() {} + virtual const char* what(void) const throw() { return ""; } +}; + +class InternalError : public Exception { +public: + InternalError(const std::string& msg) : Exception(msg) {} + ~InternalError(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: internal error"; } +}; + +class Error : public Exception { +public: + Error(const std::string& msg) : Exception(msg) {} + ~Error(void) throw() {} + virtual const char* what(void) const throw() { return ""; } +}; + +class Timeout : public Exception { +public: + Timeout(void) : Exception("time limit reached") {} + ~Timeout(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: time out"; } +}; + +class ArithmeticError : public Exception { +public: + ArithmeticError(const std::string& msg) : Exception(msg) {} + virtual ~ArithmeticError(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: arithmetic error"; } +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/file_utils.hh b/include/minizinc/file_utils.hh new file mode 100644 index 0000000..b6390d1 --- /dev/null +++ b/include/minizinc/file_utils.hh @@ -0,0 +1,96 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FILE_UTILS_HH__ +#define __MINIZINC_FILE_UTILS_HH__ + +#include +#include + +namespace MiniZinc { +namespace FileUtils { + +/// Return full path to current executable +std::string progpath(void); +/// Test if \a filename exists +bool file_exists(const std::string& filename); +/// Test if \a dirname exists and is a directory +bool directory_exists(const std::string& dirname); +/// Find executable \a filename anywhere on the path +/// On Windows, also check extensions .exe and .bat +std::string find_executable(const std::string& filename); +/// Return full path to file. If \a basePath is not empty, also try resolving +/// relative paths with respect to \a basePath. +std::string file_path(const std::string& filename, const std::string& basePath = std::string()); +/// Return directory name containing \a filename +std::string dir_name(const std::string& filename); +/// Return base name of \a filename (without dir_name) +std::string base_name(const std::string& filename); +/// Check whether path is absolute +bool is_absolute(const std::string& path); +/// Return list of files with extension \a ext in directory \a dir +std::vector directory_list(const std::string& dir, + const std::string& ext = std::string("*")); +/// Return share/minizinc directory if present anywhere above the executable +std::string share_directory(void); +/// Return current working directory +std::string working_directory(void); +/// Get global configuration file name (in share/minizinc directory) +std::string global_config_file(void); +/// Get per-user configuration file name (usually in home directory or AppData directory) +std::string user_config_file(void); +/// Get per-user configuration directory name (usually in home directory or AppData directory) +std::string user_config_dir(void); +/// Parse command line \a s into individual arguments +std::vector parseCmdLine(const std::string& s); +/// Combine individual arguments \a cmd into properly quoted command line +std::string combineCmdLine(const std::vector& cmd); + +/// Create a temporary file +class TmpFile { +private: + std::string _name; +#ifndef _WIN32 + int _tmpfile_desc; +#endif +public: + // Constructor for file with extension \a ext + TmpFile(const std::string& ext); + /// Destructor (removes file) + ~TmpFile(void); + std::string name(void) const { return _name; } +}; + +/// Create a temporary directory +class TmpDir { +private: + std::string _name; + +public: + // Constructor for difrectory + TmpDir(void); + /// Destructor (removes directory) + ~TmpDir(void); + std::string name(void) const { return _name; } +}; + +/// Inflate string \a s +void inflateString(std::string& s); +/// Deflate string \a s +std::string deflateString(const std::string& s); +/// Encode string into base 64 +std::string encodeBase64(const std::string& s); +/// Decode string from base 64 +std::string decodeBase64(const std::string& s); +} // namespace FileUtils +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/flat_exp.hh b/include/minizinc/flat_exp.hh new file mode 100644 index 0000000..ef1c97c --- /dev/null +++ b/include/minizinc/flat_exp.hh @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FLAT_EXP_HH__ +#define __MINIZINC_FLAT_EXP_HH__ + +#include + +namespace MiniZinc { +void addPathAnnotation(EnvI& env, Expression* e); +void addCtxAnn(VarDecl* vd, BCtx& c); +bool istrue(EnvI& env, Expression* e); +bool isfalse(EnvI& env, Expression* e); +Expression* createDummyValue(EnvI& env, const Type& t); +TypeInst* eval_typeinst(EnvI& env, VarDecl* vd); + +KeepAlive bind(EnvI& env, Ctx ctx, VarDecl* vd, Expression* e); +KeepAlive conj(EnvI& env, VarDecl* b, Ctx ctx, const std::vector& e); + +VarDecl* newVarDecl(EnvI& env, Ctx ctx, TypeInst* ti, Id* origId, VarDecl* origVd, Expression* rhs); + +KeepAlive flat_cv_exp(EnvI& env, Ctx ctx, Expression* e); + +void makeDefinedVar(VarDecl* vd, Call* c); +void checkIndexSets(EnvI& env, VarDecl* vd, Expression* e); + +class CallArgItem { +public: + EnvI& env; + CallArgItem(EnvI& env0); + ~CallArgItem(void); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/flatten.hh b/include/minizinc/flatten.hh new file mode 100644 index 0000000..10ab347 --- /dev/null +++ b/include/minizinc/flatten.hh @@ -0,0 +1,130 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FLATTEN_HH__ +#define __MINIZINC_FLATTEN_HH__ + +#include +#include + +namespace MiniZinc { + +/// Exception thrown for errors during flattening +class FlatteningError : public LocationException { +public: + FlatteningError(EnvI& env, const Location& loc, const std::string& msg); + ~FlatteningError(void) throw() {} + virtual const char* what(void) const throw() { return "MiniZinc: flattening error"; } +}; + +/// Options for the flattener +struct FlatteningOptions { + /// Keep output in resulting flat model + bool keepOutputInFzn; + /// Verbose output during flattening + bool verbose; + /// Only use paths for variables introduced by file 0 (the MiniZinc model) + bool only_toplevel_paths; + /// Construct and collect mzn_paths for expressions and VarDeclI during flattening + bool collect_mzn_paths; + /// Do not apply domain changes but insert them as constraints (useful for debugging) + bool record_domain_changes; + /// Only range domains for old linearization. Set from redefs to true if not here + bool onlyRangeDomains; + /// Allow the use of Half Reifications + bool enable_imp; + /// Timeout for flattening in milliseconds (0 means no timeout) + unsigned long long int timeout; + /// Create standard, DZN or JSON output + enum OutputMode { OUTPUT_ITEM, OUTPUT_DZN, OUTPUT_JSON } outputMode; + /// Output objective value (only for DZN and JSON mode) + bool outputObjective; + /// Output original output item as string (only for DZN and JSON mode) + bool outputOutputItem; + /// Default constructor + FlatteningOptions(void) + : keepOutputInFzn(false), + verbose(false), + only_toplevel_paths(false), + collect_mzn_paths(false), + record_domain_changes(false), + onlyRangeDomains(false), + enable_imp(true), + timeout(0), + outputMode(OUTPUT_ITEM), + outputObjective(false), + outputOutputItem(false) {} +}; + +class Pass { +public: + Pass(){}; + virtual Env* run(Env* env, std::ostream& log) = 0; + virtual ~Pass(){}; +}; + +/// Flatten model \a m +void flatten(Env& m, FlatteningOptions opt = FlatteningOptions()); + +/// Translate \a m into old FlatZinc syntax +void oldflatzinc(Env& m); + +/// Populate FlatZinc output model +void populateOutput(Env& e); + +/// Statistics on flat models +struct FlatModelStatistics { + /// Number of integer variables + int n_int_vars; + /// Number of bool variables + int n_bool_vars; + /// Number of float variables + int n_float_vars; + /// Number of set variables + int n_set_vars; + /// Number of bool constraints + int n_bool_ct; + /// Number of integer constraints + int n_int_ct; + /// Number of float constraints + int n_float_ct; + /// Number of set constraints + int n_set_ct; + /// Number of reified constraints evaluated + int n_reif_ct; + /// Number of half-reified constraints evaluated + int n_imp_ct; + /// Number of implications eliminated using path compression + int n_imp_del; + /// Number of linear expressions eliminated using path compression + int n_lin_del; + /// Constructor + FlatModelStatistics(void) + : n_int_vars(0), + n_bool_vars(0), + n_float_vars(0), + n_set_vars(0), + n_bool_ct(0), + n_int_ct(0), + n_float_ct(0), + n_set_ct(0), + n_reif_ct(0), + n_imp_ct(0), + n_imp_del(0), + n_lin_del(0) {} +}; + +/// Compute statistics for flat model in \a m +FlatModelStatistics statistics(Model* m); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/flatten_internal.hh b/include/minizinc/flatten_internal.hh new file mode 100644 index 0000000..77a0e0c --- /dev/null +++ b/include/minizinc/flatten_internal.hh @@ -0,0 +1,492 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FLATTEN_INTERNAL_HH__ +#define __MINIZINC_FLATTEN_INTERNAL_HH__ + +#include +#include +#include +#include + +#include + +namespace MiniZinc { + +/// Result of evaluation +class EE { +public: + /// The result value + KeepAlive r; + /// Boolean expression representing whether result is defined + KeepAlive b; + /// Constructor + explicit EE(Expression* r0 = NULL, Expression* b0 = NULL) : r(r0), b(b0) {} +}; + +/// Boolean evaluation context +enum BCtx { C_ROOT, C_POS, C_NEG, C_MIX }; + +/// Evaluation context +struct Ctx { + /// Boolean context + BCtx b; + /// Integer context + BCtx i; + /// Boolen negation flag + bool neg; + /// Default constructor (root context) + Ctx(void) : b(C_ROOT), i(C_MIX), neg(false) {} + /// Copy constructor + Ctx(const Ctx& ctx) : b(ctx.b), i(ctx.i), neg(ctx.neg) {} + /// Assignment operator + Ctx& operator=(const Ctx& ctx) { + if (this != &ctx) { + b = ctx.b; + i = ctx.i; + neg = ctx.neg; + } + return *this; + } +}; + +/// Turn \a c into positive context +BCtx operator+(const BCtx& c); +/// Negate context \a c +BCtx operator-(const BCtx& c); + +class EnvI { +public: + Model* model; + Model* orig_model; + Model* output; + VarOccurrences vo; + VarOccurrences output_vo; + + std::ostream& outstream; + std::ostream& errstream; + + // The current pass number (used for unifying and disabling path construction in final pass) + unsigned int current_pass_no; + // Used for disabling path construction in final pass + unsigned int final_pass_no; + // Used for disabling path construction past the maxPathDepth of previous passes + unsigned int maxPathDepth; + + VarOccurrences output_vo_flat; + CopyMap cmap; + IdMap reverseMappers; + struct WW { + WeakRef r; + WeakRef b; + WW(WeakRef r0, WeakRef b0) : r(r0), b(b0) {} + }; + typedef KeepAliveMap CSEMap; + bool ignorePartial; + bool ignoreUnknownIds; + std::vector callStack; + std::vector > errorStack; + std::vector idStack; + unsigned int maxCallStack; + std::vector warnings; + bool collect_vardecls; + std::vector modifiedVarDecls; + int in_redundant_constraint; + int in_maybe_partial; + int n_reif_ct; + int n_imp_ct; + int n_imp_del; + int n_lin_del; + bool in_reverse_map_var; + FlatteningOptions fopts; + unsigned int pathUse; + std::unordered_map reverseEnum; + + struct PathVar { + KeepAlive decl; + unsigned int pass_no; + }; + // Store mapping from path string to (VarDecl, pass_no) tuples + typedef std::unordered_map PathMap; + // Mapping from arbitrary Expressions to paths + typedef KeepAliveMap ReversePathMap; + // Map from filename to integer (space saving optimisation) + typedef std::unordered_map FilenameMap; + std::vector checkVars; + +protected: + CSEMap cse_map; + Model* _flat; + bool _failed; + unsigned int ids; + ASTStringMap::t reifyMap; + PathMap pathMap; + ReversePathMap reversePathMap; + FilenameMap filenameMap; + typedef std::unordered_map EnumMap; + EnumMap enumMap; + std::vector enumVarDecls; + typedef std::unordered_map ArrayEnumMap; + ArrayEnumMap arrayEnumMap; + std::vector > arrayEnumDecls; + +public: + EnvI(Model* orig0, std::ostream& outstream0 = std::cout, std::ostream& errstream0 = std::cerr); + ~EnvI(void); + long long int genId(void); + /// Set minimum new temporary id to \a i+1 + void minId(unsigned int i) { ids = std::max(ids, i + 1); } + void cse_map_insert(Expression* e, const EE& ee); + CSEMap::iterator cse_map_find(Expression* e); + void cse_map_remove(Expression* e); + CSEMap::iterator cse_map_end(void); + void dump(void); + + unsigned int registerEnum(VarDeclI* vdi); + VarDeclI* getEnum(unsigned int i) const; + unsigned int registerArrayEnum(const std::vector& arrayEnum); + const std::vector& getArrayEnum(unsigned int i) const; + /// Check if \a t1 is a subtype of \a t2 (including enumerated types if \a strictEnum is true) + bool isSubtype(const Type& t1, const Type& t2, bool strictEnum); + + void flat_addItem(Item* i); + void flat_removeItem(int i); + void flat_removeItem(Item* i); + void vo_add_exp(VarDecl* vd); + void annotateFromCallStack(Expression* e); + void fail(const std::string& msg = std::string()); + bool failed(void) const; + Model* flat(void); + void swap(); + void swap_output() { std::swap(model, output); } + ASTString reifyId(const ASTString& id); + ASTString halfReifyId(const ASTString& id); + std::ostream& dumpStack(std::ostream& os, bool errStack); + bool dumpPath(std::ostream& os, bool force = false); + void addWarning(const std::string& msg); + void collectVarDecls(bool b); + PathMap& getPathMap() { return pathMap; } + ReversePathMap& getReversePathMap() { return reversePathMap; } + FilenameMap& getFilenameMap() { return filenameMap; } + + void copyPathMapsAndState(EnvI& env); + /// deprecated, use Solns2Out + std::ostream& evalOutput(std::ostream& os); + void createErrorStack(void); + Call* surroundingCall(void) const; + + void cleanupExceptOutput(); +}; + +void setComputedDomain(EnvI& envi, VarDecl* vd, Expression* domain, bool is_computed); +EE flat_exp(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_id(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b, bool doNotFollowChains); + +class CmpExpIdx { +public: + std::vector& x; + CmpExpIdx(std::vector& x0) : x(x0) {} + bool operator()(int i, int j) const { + if (Expression::equal(x[i](), x[j]())) return false; + if (x[i]()->isa() && x[j]()->isa() && x[i]()->cast()->idn() != -1 && + x[j]()->cast()->idn() != -1) + return x[i]()->cast()->idn() < x[j]()->cast()->idn(); + return x[i]() < x[j](); + } +}; + +template +class LinearTraits {}; +template <> +class LinearTraits { +public: + typedef IntVal Val; + static Val eval(EnvI& env, Expression* e) { return eval_int(env, e); } + static void constructLinBuiltin(BinOpType bot, ASTString& callid, int& coeff_sign, Val& d) { + switch (bot) { + case BOT_LE: + callid = constants().ids.int_.lin_le; + coeff_sign = 1; + d += 1; + break; + case BOT_LQ: + callid = constants().ids.int_.lin_le; + coeff_sign = 1; + break; + case BOT_GR: + callid = constants().ids.int_.lin_le; + coeff_sign = -1; + d = -d + 1; + break; + case BOT_GQ: + callid = constants().ids.int_.lin_le; + coeff_sign = -1; + d = -d; + break; + case BOT_EQ: + callid = constants().ids.int_.lin_eq; + coeff_sign = 1; + break; + case BOT_NQ: + callid = constants().ids.int_.lin_ne; + coeff_sign = 1; + break; + default: + assert(false); + break; + } + } + static ASTString id_eq(void) { return constants().ids.int_.eq; } + typedef IntBounds Bounds; + static bool finite(const IntBounds& ib) { return ib.l.isFinite() && ib.u.isFinite(); } + static bool finite(const IntVal& v) { return v.isFinite(); } + static Bounds compute_bounds(EnvI& env, Expression* e) { return compute_int_bounds(env, e); } + typedef IntSetVal* Domain; + static Domain eval_domain(EnvI& env, Expression* e) { return eval_intset(env, e); } + static Expression* new_domain(Val v) { + return new SetLit(Location().introduce(), IntSetVal::a(v, v)); + } + static Expression* new_domain(Val v0, Val v1) { + return new SetLit(Location().introduce(), IntSetVal::a(v0, v1)); + } + static Expression* new_domain(Domain d) { return new SetLit(Location().introduce(), d); } + static bool domain_contains(Domain dom, Val v) { return dom->contains(v); } + static bool domain_equals(Domain dom, Val v) { + return dom->size() == 1 && dom->min(0) == v && dom->max(0) == v; + } + static bool domain_equals(Domain dom1, Domain dom2) { + IntSetRanges d1(dom1); + IntSetRanges d2(dom2); + return Ranges::equal(d1, d2); + } + static bool domain_tighter(Domain dom, Bounds b) { + return !b.valid || dom->min() > b.l || dom->max() < b.u; + } + static bool domain_intersects(Domain dom, Val v0, Val v1) { + return (v0 > v1) || (dom->size() > 0 && dom->min(0) <= v1 && v0 <= dom->max(dom->size() - 1)); + } + static bool domain_empty(Domain dom) { return dom->size() == 0; } + static Domain limit_domain(BinOpType bot, Domain dom, Val v) { + IntSetRanges dr(dom); + IntSetVal* ndomain; + switch (bot) { + case BOT_LE: + v -= 1; + // fall through + case BOT_LQ: { + Ranges::Bounded b = + Ranges::Bounded::maxiter(dr, v); + ndomain = IntSetVal::ai(b); + } break; + case BOT_GR: + v += 1; + // fall through + case BOT_GQ: { + Ranges::Bounded b = + Ranges::Bounded::miniter(dr, v); + ndomain = IntSetVal::ai(b); + } break; + case BOT_NQ: { + Ranges::Const c(v, v); + Ranges::Diff > d(dr, c); + ndomain = IntSetVal::ai(d); + } break; + default: + assert(false); + return NULL; + } + return ndomain; + } + static Domain intersect_domain(Domain dom, Val v0, Val v1) { + IntSetRanges dr(dom); + Ranges::Const c(v0, v1); + Ranges::Inter > inter(dr, c); + return IntSetVal::ai(inter); + } + static Val floor_div(Val v0, Val v1) { + return static_cast( + floor(static_cast(v0.toInt()) / static_cast(v1.toInt()))); + } + static Val ceil_div(Val v0, Val v1) { + return static_cast(ceil(static_cast(v0.toInt()) / v1.toInt())); + } + static IntLit* newLit(Val v) { return IntLit::a(v); } +}; +template <> +class LinearTraits { +public: + typedef FloatVal Val; + static Val eval(EnvI& env, Expression* e) { return eval_float(env, e); } + static void constructLinBuiltin(BinOpType bot, ASTString& callid, int& coeff_sign, Val& d) { + switch (bot) { + case BOT_LE: + callid = constants().ids.float_.lin_lt; + coeff_sign = 1; + break; + case BOT_LQ: + callid = constants().ids.float_.lin_le; + coeff_sign = 1; + break; + case BOT_GR: + callid = constants().ids.float_.lin_lt; + coeff_sign = -1; + d = -d; + break; + case BOT_GQ: + callid = constants().ids.float_.lin_le; + coeff_sign = -1; + d = -d; + break; + case BOT_EQ: + callid = constants().ids.float_.lin_eq; + coeff_sign = 1; + break; + case BOT_NQ: + callid = constants().ids.float_.lin_ne; + coeff_sign = 1; + break; + default: + assert(false); + break; + } + } + static ASTString id_eq(void) { return constants().ids.float_.eq; } + typedef FloatBounds Bounds; + static bool finite(const FloatBounds& ib) { return ib.l.isFinite() && ib.u.isFinite(); } + static bool finite(const FloatVal& v) { return v.isFinite(); } + static Bounds compute_bounds(EnvI& env, Expression* e) { return compute_float_bounds(env, e); } + typedef FloatSetVal* Domain; + static Domain eval_domain(EnvI& env, Expression* e) { return eval_floatset(env, e); } + + static Expression* new_domain(Val v) { + return new SetLit(Location().introduce(), FloatSetVal::a(v, v)); + } + static Expression* new_domain(Val v0, Val v1) { + return new SetLit(Location().introduce(), FloatSetVal::a(v0, v1)); + } + static Expression* new_domain(Domain d) { return new SetLit(Location().introduce(), d); } + static bool domain_contains(Domain dom, Val v) { return dom->contains(v); } + static bool domain_equals(Domain dom, Val v) { + return dom->size() == 1 && dom->min(0) == v && dom->max(0) == v; + } + + static bool domain_tighter(Domain dom, Bounds b) { + return !b.valid || dom->min() > b.l || dom->max() < b.u; + } + static bool domain_intersects(Domain dom, Val v0, Val v1) { + return (v0 > v1) || (dom->size() > 0 && dom->min(0) <= v1 && v0 <= dom->max(dom->size() - 1)); + } + static bool domain_empty(Domain dom) { return dom->size() == 0; } + + static bool domain_equals(Domain dom1, Domain dom2) { + FloatSetRanges d1(dom1); + FloatSetRanges d2(dom2); + return Ranges::equal(d1, d2); + } + + static Domain intersect_domain(Domain dom, Val v0, Val v1) { + if (dom) { + FloatSetRanges dr(dom); + Ranges::Const c(v0, v1); + Ranges::Inter > inter(dr, c); + return FloatSetVal::ai(inter); + } else { + Domain d = FloatSetVal::a(v0, v1); + return d; + } + } + static Domain limit_domain(BinOpType bot, Domain dom, Val v) { + FloatSetRanges dr(dom); + FloatSetVal* ndomain; + switch (bot) { + case BOT_LE: + return NULL; + case BOT_LQ: { + Ranges::Bounded b = + Ranges::Bounded::maxiter(dr, v); + ndomain = FloatSetVal::ai(b); + } break; + case BOT_GR: + return NULL; + case BOT_GQ: { + Ranges::Bounded b = + Ranges::Bounded::miniter(dr, v); + ndomain = FloatSetVal::ai(b); + } break; + case BOT_NQ: { + Ranges::Const c(v, v); + Ranges::Diff > d(dr, c); + ndomain = FloatSetVal::ai(d); + } break; + default: + assert(false); + return NULL; + } + return ndomain; + } + static Val floor_div(Val v0, Val v1) { return v0 / v1; } + static Val ceil_div(Val v0, Val v1) { return v0 / v1; } + static FloatLit* newLit(Val v) { return FloatLit::a(v); } +}; + +template +void simplify_lin(std::vector::Val>& c, std::vector& x, + typename LinearTraits::Val& d) { + std::vector idx(c.size()); + for (unsigned int i = static_cast(idx.size()); i--;) { + idx[i] = i; + Expression* e = follow_id_to_decl(x[i]()); + if (VarDecl* vd = e->dyn_cast()) { + if (vd->e() && vd->e()->isa()) { + x[i] = vd->e(); + } else { + x[i] = e->cast()->id(); + } + } else { + x[i] = e; + } + } + std::sort(idx.begin(), idx.end(), CmpExpIdx(x)); + unsigned int ci = 0; + for (; ci < x.size(); ci++) { + if (Lit* il = x[idx[ci]]()->dyn_cast()) { + d += c[idx[ci]] * il->v(); + c[idx[ci]] = 0; + } else { + break; + } + } + for (unsigned int i = ci + 1; i < x.size(); i++) { + if (Expression::equal(x[idx[i]](), x[idx[ci]]())) { + c[idx[ci]] += c[idx[i]]; + c[idx[i]] = 0; + } else if (Lit* il = x[idx[i]]()->dyn_cast()) { + d += c[idx[i]] * il->v(); + c[idx[i]] = 0; + } else { + ci = i; + } + } + ci = 0; + for (unsigned int i = 0; i < c.size(); i++) { + if (c[i] != 0) { + c[ci] = c[i]; + x[ci] = x[i]; + ci++; + } + } + c.resize(ci); + x.resize(ci); +} + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/flattener.hh b/include/minizinc/flattener.hh new file mode 100644 index 0000000..986ea81 --- /dev/null +++ b/include/minizinc/flattener.hh @@ -0,0 +1,136 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FLATTENER_H__ +#define __MINIZINC_FLATTENER_H__ + +#include +#include +#include +#include +#include +#include // temp., TODO +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#ifdef HAS_GECODE +#include +#endif + +namespace MiniZinc { + +class Flattener { +private: + std::unique_ptr pEnv; + std::ostream& os; + std::ostream& log; + +public: + Flattener(std::ostream& os, std::ostream& log, const std::string& stdlibDir); + ~Flattener(); + bool processOption(int& i, std::vector& argv); + void printVersion(std::ostream&); + void printHelp(std::ostream&); + + void flatten(const std::string& modelString = std::string(), + const std::string& modelName = std::string("stdin")); + void printStatistics(std::ostream&); + + void set_flag_verbose(bool f) { flag_verbose = f; } + bool get_flag_verbose() const { return flag_verbose; } + void set_flag_statistics(bool f) { flag_statistics = f; } + bool get_flag_statistics() const { return flag_statistics; } + void set_flag_timelimit(unsigned long long int t) { fopts.timeout = t; } + unsigned long long int get_flag_timelimit(void) { return fopts.timeout; } + void set_flag_output_by_default(bool f) { fOutputByDefault = f; } + Env* getEnv() const { + assert(pEnv.get()); + return pEnv.get(); + } + bool hasInputFiles(void) const { + return !filenames.empty() || flag_stdinInput || !flag_solution_check_model.empty(); + } + + SolverInstance::Status status = SolverInstance::UNKNOWN; + +private: + Env* multiPassFlatten(const std::vector >& passes); + + bool fOutputByDefault = false; // if the class is used in mzn2fzn, write .fzn+.ozn by default + std::vector filenames; + std::vector datafiles; + std::vector includePaths; + bool is_flatzinc = false; + + bool flag_ignoreStdlib = false; + bool flag_typecheck = true; + bool flag_verbose = false; + bool flag_newfzn = false; + bool flag_optimize = true; + bool flag_chain_compression = true; + bool flag_werror = false; + bool flag_only_range_domains = false; + bool flag_allow_unbounded_vars = false; + bool flag_noMIPdomains = false; + int opt_MIPDmaxIntvEE = 0; + double opt_MIPDmaxDensEE = 0.0; + bool flag_statistics = false; + bool flag_stdinInput = false; + bool flag_allow_multi_assign = false; + + bool flag_gecode = false; + bool flag_two_pass = false; + bool flag_sac = false; + bool flag_shave = false; + unsigned int flag_pre_passes = 1; + + std::string std_lib_dir; + std::string globals_dir; + + bool flag_no_output_ozn = false; + std::string flag_output_base; + std::string flag_output_fzn; + std::string flag_output_ozn; + std::string flag_output_paths; + bool flag_keep_mzn_paths = false; + bool flag_output_fzn_stdout = false; + bool flag_output_ozn_stdout = false; + bool flag_output_paths_stdout = false; + bool flag_instance_check_only = false; + bool flag_model_check_only = false; + bool flag_model_interface_only = false; + bool flag_model_types_only = false; + FlatteningOptions::OutputMode flag_output_mode = FlatteningOptions::OUTPUT_ITEM; + bool flag_output_objective = false; + bool flag_output_output_item = false; + std::string flag_solution_check_model; + bool flag_compile_solution_check_model = false; + FlatteningOptions fopts; + + Timer starttime; +}; + +} // namespace MiniZinc + +#endif // __MINIZINC_FLATTENER_H__ diff --git a/include/minizinc/gc.hh b/include/minizinc/gc.hh new file mode 100644 index 0000000..d533ba1 --- /dev/null +++ b/include/minizinc/gc.hh @@ -0,0 +1,266 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GC_HH__ +#define __MINIZINC_GC_HH__ + +#include +#include + +#include +#include +#include +#include + +namespace MiniZinc { + +/** + * \brief Base class for abstract syntax tree nodes + */ +class ASTNode { + friend class GC; + +protected: + /// Mark for garbage collection + mutable unsigned int _gc_mark : 1; + /// Id of the node + unsigned int _id : 7; + /// Secondary id + unsigned int _sec_id : 7; + /// Flag + unsigned int _flag_1 : 1; + /// Flag + unsigned int _flag_2 : 1; + /// Flag + unsigned int _flag_3 : 1; + /// Flag + unsigned int _flag_4 : 1; + + enum BaseNodes { NID_FL, NID_CHUNK, NID_VEC, NID_END = NID_VEC }; + + /// Constructor + ASTNode(unsigned int id) : _gc_mark(0), _id(id), _flag_1(0), _flag_2(0), _flag_3(0), _flag_4(0) {} + +public: + /// Allocate node + void* operator new(size_t size); + + /// Placement-new + void* operator new(size_t, void* n) throw() { return n; } + + /// Delete node (no-op) + void operator delete(void*, size_t) throw() {} + /// Delete node (no-op) + void operator delete(void*, void*) throw() {} + + /// Delete node (no-op) + void operator delete(void*) throw() {} + + /// Return state of first user-defined flag + bool user_flag0(void) const { return _flag_3; } + /// Set state of first user-defined flag + void user_flag0(bool b) { _flag_3 = b; } + /// Return state of second user-defined flag + bool user_flag1(void) const { return _flag_4; } + /// Set state of second user-defined flag + void user_flag1(bool b) { _flag_4 = b; } +}; + +/** + * \brief Base class for unstructured garbage collected data + */ +class ASTChunk : public ASTNode { + friend class GC; + +protected: + /// Allocated size + size_t _size; + /// Storage + char _data[4]; + /// Constructor + ASTChunk(size_t size); + /// Actual size of object in memory + size_t memsize(void) const { + size_t s = sizeof(ASTChunk) + (_size <= 4 ? 0 : _size - 4) * sizeof(char); + s += ((8 - (s & 7)) & 7); + return s; + } + /// Allocate raw memory + static void* alloc(size_t size); +}; + +/** + * \brief Base class for structured garbage collected data + */ +class ASTVec : public ASTNode { + friend class GC; + +protected: + /// Allocated size + size_t _size; + /// Storage + void* _data[2]; + /// Constructor + ASTVec(size_t size); + /// Actual size of object in memory + size_t memsize(void) const { + size_t s = sizeof(ASTVec) + (_size <= 2 ? 0 : _size - 2) * sizeof(void*); + s += ((8 - (s & 7)) & 7); + return s; + } + /// Allocate raw memory + static void* alloc(size_t size); +}; + +class Model; +class Expression; + +class KeepAlive; +class WeakRef; + +class ASTNodeWeakMap; + +/// Garbage collector +class GC { + friend class ASTNode; + friend class ASTVec; + friend class ASTChunk; + friend class KeepAlive; + friend class WeakRef; + friend class ASTNodeWeakMap; + +private: + class Heap; + /// The memory controlled by the collector + Heap* _heap; + /// Count how many locks are currently active + unsigned int _lock_count; + /// Timeout in milliseconds + unsigned long long int _timeout; + /// Counter for timeout + int _timeout_counter; + /// Timer for timeout + Timer _timeout_timer; + /// Return thread-local GC object + static GC*& gc(void); + /// Constructor + GC(void); + + /// Allocate garbage collected memory + void* alloc(size_t size); + + static void addKeepAlive(KeepAlive* e); + static void removeKeepAlive(KeepAlive* e); + static void addWeakRef(WeakRef* e); + static void removeWeakRef(WeakRef* e); + static void addNodeWeakMap(ASTNodeWeakMap* m); + static void removeNodeWeakMap(ASTNodeWeakMap* m); + +public: + /// Acquire garbage collector lock for this thread + static void lock(void); + /// Release garbage collector lock for this thread + static void unlock(void); + /// Manually trigger garbage collector (must be unlocked) + static void trigger(void); + /// Test if garbage collector is locked + static bool locked(void); + /// Add model \a m to root set + static void add(Model* m); + /// Remove model \a m from root set + static void remove(Model* m); + + /// Put a mark on the trail + static void mark(void); + /// Add a trail entry + static void trail(Expression**, Expression*); + /// Untrail to previous mark + static void untrail(void); + + /// Set timeout of \a t milliseconds, 0 means disable + static void setTimeout(unsigned long long int t); + + /// Return maximum allocated memory (high water mark) + static size_t maxMem(void); +}; + +/// Automatic garbage collection lock +class GCLock { +public: + /// Acquire lock + GCLock(void); + /// Release lock upon destruction + ~GCLock(void); +}; + +/// Expression wrapper that is a member of the root set +class KeepAlive { + friend class GC; + +private: + Expression* _e; + KeepAlive* _p; + KeepAlive* _n; + +public: + KeepAlive(Expression* e = NULL); + ~KeepAlive(void); + KeepAlive(const KeepAlive& e); + KeepAlive& operator=(const KeepAlive& e); + Expression* operator()(void) { return _e; } + Expression* operator()(void) const { return _e; } + KeepAlive* next(void) const { return _n; } +}; + +/// Expression wrapper that is a member of the root set +class WeakRef { + friend class GC; + +private: + Expression* _e; + WeakRef* _p; + WeakRef* _n; + bool _valid; + +public: + WeakRef(Expression* e = NULL); + ~WeakRef(void); + WeakRef(const WeakRef& e); + WeakRef& operator=(const WeakRef& e); + Expression* operator()(void) { return _valid ? _e : NULL; } + Expression* operator()(void) const { return _valid ? _e : NULL; } + WeakRef* next(void) const { return _n; } +}; + +class ASTNodeWeakMap { + friend class GC; + +private: + ASTNodeWeakMap(const WeakRef& e); + ASTNodeWeakMap& operator=(const ASTNodeWeakMap& e); + +protected: + typedef std::unordered_map NodeMap; + ASTNodeWeakMap* _p; + ASTNodeWeakMap* _n; + ASTNodeWeakMap* next(void) const { return _n; } + NodeMap _m; + +public: + ASTNodeWeakMap(void); + ~ASTNodeWeakMap(void); + void insert(ASTNode* n0, ASTNode* n1); + ASTNode* find(ASTNode* n); + void clear() { _m.clear(); } +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/hash.hh b/include/minizinc/hash.hh new file mode 100644 index 0000000..7010256 --- /dev/null +++ b/include/minizinc/hash.hh @@ -0,0 +1,203 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_HASH_HH__ +#define __MINIZINC_HASH_HH__ + +#include +#include + +#include +#include + +namespace MiniZinc { + +/// Hash class for expressions +struct ExpressionHash { + size_t operator()(const Expression* e) const { return Expression::hash(e); } +}; + +/// Equality test for expressions +struct ExpressionEq { + bool operator()(const Expression* e0, const Expression* e1) const { + return Expression::equal(e0, e1); + } +}; + +/// Hash map from expression to \a T +template +class ExpressionMap { +protected: + /// The underlying map implementation + std::unordered_map _m; + +public: + /// Iterator type + typedef + typename std::unordered_map::iterator iterator; + /// Insert mapping from \a e to \a t + iterator insert(Expression* e, const T& t) { + assert(e != NULL); + return _m.insert(std::pair(e, t)).first; + } + /// Find \a e in map + iterator find(Expression* e) { return _m.find(e); } + /// Begin of iterator + iterator begin(void) { return _m.begin(); } + /// End of iterator + iterator end(void) { return _m.end(); } + /// Remove binding of \a e from map + void remove(Expression* e) { _m.erase(e); } + /// Remove all elements from the map + void clear(void) { _m.clear(); } +}; + +/// Equality test for identifiers +struct IdEq { + bool operator()(const Id* e0, const Id* e1) const { + if (e0->idn() == e1->idn()) { + if (e0->idn() == -1) return e0->v() == e1->v(); + return true; + } + return false; + } +}; + +/// Hash map from identifier to \a T +template +class IdMap { +protected: + /// The underlying map implementation + std::unordered_map _m; + +public: + /// Iterator type + typedef typename std::unordered_map::iterator iterator; + /// Insert mapping from \a e to \a t + void insert(Id* e, const T& t) { + assert(e != NULL); + _m.insert(std::pair(e, t)); + } + /// Find \a e in map + iterator find(Id* e) { return _m.find(e); } + /// Begin of iterator + iterator begin(void) { return _m.begin(); } + /// End of iterator + iterator end(void) { return _m.end(); } + /// Remove binding of \a e from map + void remove(Id* e) { _m.erase(e); } + /// Return number of elements in the map + int size(void) const { return _m.size(); } + /// Remove all elements from the map + void clear(void) { _m.clear(); } + T& get(Id* ident) { + iterator it = find(ident); + // assert(it != _m.end()); + if (_m.end() == it) { // Changing so it stays in Release version + std::string msg = "Id "; + // if (ident) // could be a segfault... + // msg += ident->v().c_str(); + msg += " not found"; + throw InternalError(msg); + } + return it->second; + } +}; + +/// Hash class for KeepAlive objects +struct KAHash { + size_t operator()(const KeepAlive& e) const { return Expression::hash(e()); } +}; + +/// Equality test for KeepAlive objects +struct KAEq { + bool operator()(const KeepAlive& e0, const KeepAlive& e1) const { + return Expression::equal(e0(), e1()); + } +}; + +/// Hash map from KeepAlive to \a T +template +class KeepAliveMap { +protected: + /// The underlying map implementation + std::unordered_map _m; + +public: + /// Iterator type + typedef typename std::unordered_map::iterator iterator; + /// Insert mapping from \a e to \a t + void insert(KeepAlive& e, const T& t) { + assert(e() != NULL); + _m.insert(std::pair(e, t)); + } + /// Find \a e in map + iterator find(KeepAlive& e) { return _m.find(e); } + /// Begin of iterator + iterator begin(void) { return _m.begin(); } + /// End of iterator + iterator end(void) { return _m.end(); } + /// Remove binding of \a e from map + void remove(KeepAlive& e) { _m.erase(e); } + void clear() { _m.clear(); } + template + void dump(void) { + for (iterator i = _m.begin(); i != _m.end(); ++i) { + std::cerr << D::k(i->first()) << ": " << D::d(i->second) << std::endl; + } + } +}; + +class ExpressionSetIter + : public std::unordered_set::iterator { +protected: + bool _empty; + typedef std::unordered_set::iterator Iter; + +public: + ExpressionSetIter(void) : _empty(false) {} + ExpressionSetIter(bool) : _empty(true) {} + ExpressionSetIter(const Iter& i) : Iter(i), _empty(false) {} + bool operator==(const ExpressionSetIter& i) const { + return (_empty && i._empty) || static_cast(*this) == static_cast(i); + } + bool operator!=(const ExpressionSetIter& i) const { return !operator==(i); } +}; + +/// Hash set for expressions +class ExpressionSet { +protected: + /// The underlying set implementation + std::unordered_set _s; + +public: + /// Insert \a e + void insert(Expression* e) { + assert(e != NULL); + _s.insert(e); + } + /// Find \a e in map + ExpressionSetIter find(Expression* e) { return _s.find(e); } + /// Begin of iterator + ExpressionSetIter begin(void) { return _s.begin(); } + /// End of iterator + ExpressionSetIter end(void) { return _s.end(); } + /// Remove binding of \a e from map + void remove(Expression* e) { _s.erase(e); } + bool contains(Expression* e) { return find(e) != end(); } + /// Remove all elements from the map + void clear(void) { _s.clear(); } + bool isEmpty(void) const { return _s.begin() == _s.end(); } +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/htmlprinter.hh b/include/minizinc/htmlprinter.hh new file mode 100644 index 0000000..803500f --- /dev/null +++ b/include/minizinc/htmlprinter.hh @@ -0,0 +1,51 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_HTMLPRINTER_HH +#define __MINIZINC_HTMLPRINTER_HH + +#include +#include + +namespace MiniZinc { + +class Model; + +class HtmlDocument { +protected: + std::string _filename; + std::string _title; + std::string _doc; + +public: + HtmlDocument(const std::string& filename, const std::string& title, const std::string& document) + : _filename(filename), _title(title), _doc(document) {} + std::string filename(void) const { return _filename; } + std::string title(void) const { return _title; } + std::string document(void) const { return _doc; } +}; + +class HtmlPrinter { +public: + static std::vector printHtml(EnvI& env, Model* m, const std::string& basename, + int splitLevel, bool includeStdLib, + bool generateIndex); +}; + +class RSTPrinter { +public: + static std::vector printRST(EnvI& env, Model* m, const std::string& basename, + int splitLevel, bool includeStdLib, bool generateIndex); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/interpreter.hh b/include/minizinc/interpreter.hh new file mode 100644 index 0000000..aa8025e --- /dev/null +++ b/include/minizinc/interpreter.hh @@ -0,0 +1,541 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// #define DBG_INTERPRETER(msg) std::cerr << msg +#define DBG_INTERPRETER(msg) \ + do { \ + } while (0) +#define DBG_TRIM_OUTPUT true + +namespace MiniZinc { + +class Interpreter; + +class RegisterFile { +protected: + std::vector _r; + size_t _size; + // Note: It seems that the standard library sometimes tries to be smart when + // using resize(n) when n < capacity. We therefore use _r.size() as capacity + // and _size as the actual size. + + // FIXME: The RegisterFile is said to always have a given size, but it is + // currently unknown how many globals are used. This can trigger an assertion. +public: + RegisterFile(int n = 0) : _r(n), _size(n) {} + const Val& operator[](int r) { + assert(r < _r.size()); + return _r[r]; + } + size_t size() { return _size; } + void resize(Interpreter* interpreter, size_t n) { + if (n >= _r.size()) { + _r.resize(_r.size() * 2); + } + if (n < _size) { + for (int i = n; i < _size; ++i) { + _r[i].rmRef(interpreter); + _r[i] = 0; + } + } + _size = n; + } + std::vector::const_iterator cbegin() { return _r.cbegin(); } + std::vector::const_iterator cend() { return _r.cend(); } + std::vector::const_iterator iter_n(size_t n) { return this->cbegin() + n; } + void assign(Interpreter* interpreter, int r, const Val& v) { + assert(r < size()); + _r[r].assign(interpreter, v); + } + + void cp(Interpreter* interpreter, int r1, int r2) { + assert(r1 < size()); + _r[r2].assign(interpreter, _r[r1]); + } + void cp(Interpreter* interpreter, int r1, RegisterFile& rf, int r2) { + assert(r1 < size()); + rf._r[r2].assign(interpreter, _r[r1]); + } + /// Destroy this register file + void destroy(Interpreter* interpreter) { + for (auto& v : _r) { + v.rmRef(interpreter); + } + } + void clear(Interpreter* interpreter) { + destroy(interpreter); + _r.clear(); + } + void dump(std::ostream& os) { + for (unsigned int i = 0; i < size(); i++) { + os << " R" << i << " = " << _r[i].toString() << "\n"; + } + } +}; + +class AggregationCtx { +protected: + /// Stack of values that need to be aggregated + std::vector stack; + +public: + /// Earliest time stamp for definitions in the current aggregation + int def_ident_start; + /// Constraints attached to the computed value + std::vector constraints; + /// Type of function represented by this context + enum Symbol { VCTX_AND, VCTX_OR, VCTX_VEC, VCTX_OTHER, MAX_SYMBOL = VCTX_OTHER } symbol; + static const std::string symbol_to_string[MAX_SYMBOL + 1]; + /// Nesting depth for this symbol (how many of these are open) + int n_symbols; + /// Constructor + AggregationCtx(Interpreter* interpreter, int s); + /// Destructor + ~AggregationCtx(void); + /// Push value onto aggregation stack + void push(Interpreter* interpreter, const Val& v) { + assert(stack.empty() || symbol != VCTX_OTHER); + stack.push_back(v); + stack.back().addRef(interpreter); + } + void pop(Interpreter* interpreter) { + stack.back().rmRef(interpreter); + stack.pop_back(); + } + const Val& back(void) const { return stack.back(); } + const Val& operator[](int i) const { return stack[i]; } + int size(void) const { return stack.size(); } + bool empty(void) const { return stack.empty(); } + Val createVec(Interpreter* interpreter, int timestamp) const; + /// Destroy stack values + void destroyStack(Interpreter* interpreter) { + for (auto& v : stack) { + v.rmRef(interpreter); + } + } +}; + +// Frame information for Common Subexpression Elimination +class CSEFrame { +public: + int proc; + BytecodeProc::Mode mode; + int nargs; + union KeyUnion { + VariadicKey vk; + FixedKey<1> f1; + FixedKey<2> f2; + FixedKey<3> f3; + FixedKey<4> f4; + + KeyUnion() { new (&vk) VariadicKey(); } + ~KeyUnion() {} + } key; + size_t stack_size; + + CSEFrame(Interpreter& interpreter, int _proc, BytecodeProc::Mode _mode, arg_iter arg_start, + arg_iter arg_end, size_t _nargs, size_t _stack_size) + : proc(_proc), mode(_mode), stack_size(_stack_size), nargs(_nargs) { + switch (nargs) { + case 1: { + key.f1 = FixedKey<1>(interpreter, arg_start, arg_end); + break; + } + case 2: { + key.f2 = FixedKey<2>(interpreter, arg_start, arg_end); + break; + } + case 3: { + key.f3 = FixedKey<3>(interpreter, arg_start, arg_end); + break; + } + case 4: { + key.f4 = FixedKey<4>(interpreter, arg_start, arg_end); + break; + } + default: { + key.vk = VariadicKey(interpreter, arg_start, arg_end, nargs); + break; + } + } + } + + CSEFrame(const CSEFrame& other) + : proc(other.proc), mode(other.mode), stack_size(other.stack_size), nargs(other.nargs) { + switch (nargs) { + case 1: { + key.f1 = other.key.f1; + break; + } + case 2: { + key.f2 = other.key.f2; + break; + } + case 3: { + key.f3 = other.key.f3; + break; + } + case 4: { + key.f4 = other.key.f4; + break; + } + default: { + key.vk = other.key.vk; + break; + } + } + }; + + inline void destroy(Interpreter& interpreter) { + switch (nargs) { + case 1: { + key.f1.destroy(interpreter); + break; + } + case 2: { + key.f2.destroy(interpreter); + break; + } + case 3: { + key.f3.destroy(interpreter); + break; + } + case 4: { + key.f4.destroy(interpreter); + break; + } + default: { + key.vk.destroy(interpreter); + break; + } + } + } + + inline CSEKey& getKey() { + switch (nargs) { + case 1: { + return key.f1; + } + case 2: { + return key.f2; + } + case 3: { + return key.f3; + } + case 4: { + return key.f4; + } + default: { + return key.vk; + } + } + } +}; + +class BytecodeFrame { +public: + size_t reg_offset; + const BytecodeStream* bs; + int pc; + int _pred; + char _mode; + size_t cse_frame_depth = 0; + + BytecodeFrame(const BytecodeStream& bs0, int pred, char mode) + : reg_offset(0), bs(&bs0), pc(0), _pred(pred), _mode(mode) {} +}; + +class Trail { + friend class MznSolver; + +protected: + std::vector> var_list_trail; + std::vector obj_trail; + // + std::vector> alias_trail; + std::vector> domain_trail; + std::vector> def_trail; + // + std::vector> trail_size; + std::vector timestamp_trail; + bool last_operation_pop = false; + +public: + Trail() = default; + virtual ~Trail() { + for (auto& i : obj_trail) { + if (i->rcoType() == RefCountedObject::VAR) { + Variable::free(static_cast(i)); + } else { + free(i); + } + } + obj_trail.clear(); + }; + + size_t len() { return trail_size.size(); } + inline bool is_trailed(RefCountedObject* rco) { + return (!trail_size.empty() && timestamp_trail.back() > rco->timestamp()); + } + + // Trail hedge pointer change + inline bool trail_ptr(Variable* obj, Variable** member) { + if (!is_trailed(obj)) { + return false; + } + var_list_trail.emplace_back(member, *member); + return true; + } + // Trail Reference Counted Object removal + inline bool trail_removal(RefCountedObject* obj) { + if (!is_trailed(obj)) { + return false; + } + obj_trail.push_back(obj); + return true; + } + // Trail variable aliasing + inline bool trail_alias(Interpreter* interpreter, Variable* v) { + if (!is_trailed(v)) { + return false; + } + v->alias().addMemRef(interpreter); + alias_trail.emplace_back(v, v->alias()); + return true; + } + // Trail definition domain change + inline bool trail_domain(Interpreter* interpreter, Variable* v, Vec* dom) { + if (!is_trailed(v)) { + return false; + } + assert(dom); + dom->addMemRef(interpreter); + domain_trail.emplace_back(v, dom); + return true; + } + bool trail_add_def(Variable* var, Constraint* c) { + if (!is_trailed(var)) { + return false; + } + def_trail.emplace_back(var, c, true); + return true; + } + bool trail_rm_def(Variable* var, Constraint* c) { + if (!is_trailed(var)) { + return false; + } + def_trail.emplace_back(var, c, false); + return true; + } + + size_t save_state(Interpreter* interpreter); + void untrail(Interpreter* interpreter); +}; + +// Structure for active loops +struct LoopState { + LoopState(Vec* vec, int _exit_pc) + : pos(reinterpret_cast(vec->begin())), + end(reinterpret_cast(vec->end())), + exit_pc(_exit_pc), + is_range(false) {} + + LoopState(int l, int u, int _exit_pc) : pos(l), end(u + 1), exit_pc(_exit_pc), is_range(true) {} + + intptr_t pos; + intptr_t end; + + int exit_pc : 31; + int is_range : 1; +}; + +class Interpreter { + friend class Trail; + friend class MznSolver; + +public: + enum Status { ROGER, ABORTED, INCONSISTENT, ERROR, MAX_STATUS = ERROR }; + static const std::string status_to_string[MAX_STATUS + 1]; + +protected: + RegisterFile _registers; + std::vector _stack; + std::vector _cse_stack; + std::vector _agg; + std::vector _loops; + std::vector& _procs; + int _identCount; + std::vector cse; // Different instantiations of CSETable + std::unordered_map delayed_constraints; + std::deque _propQueue; + RegisterFile globals; + Status _status = ROGER; + + // The root variable. It's fixed to true, all toplevel constraints + // are attached to it, and it's the head of the linked list of + // all variables + Variable* _root_var; + + Vec* infinite_dom; + Vec* boolean_dom; + Vec* true_dom; + +public: + Trail trail; + std::unordered_map solutions; + + Interpreter(std::vector& procs, const BytecodeFrame& f, int max_globals = -1) + : _registers(4096), + _procs(procs), + _identCount(0), + cse(procs.size()), + globals(max_globals + 1) { + _stack.reserve(32); + _cse_stack.reserve(32); + _stack.emplace_back(f); + _registers.resize(this, f.bs->maxRegister() + 1); + + infinite_dom = Vec::a(this, newIdent(), {-Val::infinity(), Val::infinity()}); + infinite_dom->addRef(this); + boolean_dom = Vec::a(this, newIdent(), {0, 1}); + boolean_dom->addRef(this); + true_dom = Vec::a(this, newIdent(), {1, 1}); + true_dom->addRef(this); + _root_var = Variable::createRoot(this, Val(true_dom), newIdent()); + _root_var->addRef(this); + for (int i = 0; i < cse.size(); ++i) { + switch (_procs[i].nargs) { + case 1: { + cse[i] = new CSETable>(); + break; + } + case 2: { + cse[i] = new CSETable>(); + break; + } + case 3: { + cse[i] = new CSETable>(); + break; + } + case 4: { + cse[i] = new CSETable>(); + break; + } + default: { + cse[i] = new CSETable(); + break; + } + } + } + } + ~Interpreter(void); + Status status() { return _status; } + void run(void); + bool runDelayed(); + void pushAgg(const Val& v, int stackOffset); + void pushConstraint(Constraint* d); + std::pair cse_find(int proc, const CSEKey& key, BytecodeProc::Mode& mode); + void cse_insert(int proc, CSEKey& key, BytecodeProc::Mode& mode, Val& val); + void set_global(int i, const Val& val) { globals.assign(this, i, val); } + void clear_globals() { globals.clear(this); } + const Val get_global(int i) { return globals[i]; } + PropStatus subscribe(Constraint* c); + void unsubscribe(Constraint* d); + int newIdent(void) { return _identCount++; } + int currentIdent(void) const { return _identCount; } + void dumpState(std::ostream& os); + void dumpState(const BytecodeFrame& bf, std::ostream& os); + void dumpState(); + void schedule(Constraint* d, const Variable::SubscriptionEvent& ev); + void deschedule(Constraint* d); + void propagate(void); + void call(int code, std::vector&& args); + + const Val& reg(const BytecodeFrame& bf, int r) { return _registers[bf.reg_offset + r]; } + void assign(const BytecodeFrame& bf, int r, const Val& v) { + _registers.assign(this, bf.reg_offset + r, v); + } + BytecodeFrame& frame() { return _stack.back(); } + + Val infinite_domain() { return Val(infinite_dom); } + Val boolean_domain() { return Val(boolean_dom); } + void register_delayed(Constraint* c, Variable* v) { delayed_constraints[c] = v; } + void remove_delayed(Constraint* c) { delayed_constraints.erase(c); } + + Variable* root() { return _root_var; } + + /// Perform optimizatin by basic propagation on generated FlatZinc + void optimize(void); +}; + +inline void RefCountedObject::rmRef(Interpreter* interpreter, RefCountedObject* rco) { + assert(rco->_model_ref_count > 0); + if (--rco->_model_ref_count == 0) { + switch (rco->rcoType()) { + case VAR: + static_cast(rco)->destroy(interpreter); + break; + case VEC: + static_cast(rco)->destroyModel(interpreter); + break; + default: + assert(false); + } + if (interpreter->trail.is_trailed(rco)) { + interpreter->trail.trail_removal(rco); + } else if (rco->_memory_ref_count == 0) { + // INVARIANT: All children of a definition are already promoted, cut, or freed. + // assert(rco->rcoType() != VAR || + // !static_cast(rco)->constraints().empty()); + if (rco->rcoType() == VAR) { + Variable::free(static_cast(rco)); + } else { + ::free(rco); + } + } + } +} + +inline void RefCountedObject::rmMemRef(Interpreter* interpreter, RefCountedObject* rco) { + if (--rco->_memory_ref_count == 0u && !interpreter->trail.is_trailed(rco) && + rco->_model_ref_count == 0u) { + // INVARIANT: All children of a definition are already promoted, cut, or freed. + // assert(rco->rcoType() != DEF || !static_cast(rco)->defs()); + if (rco->rcoType() == VEC) { + static_cast(rco)->destroyMemory(interpreter); + } + free(rco); + } +} + +inline AggregationCtx::AggregationCtx(Interpreter* interpreter, int s) + : def_ident_start(interpreter->currentIdent()), symbol(static_cast(s)), n_symbols(1) { + assert(s >= 0 && s <= VCTX_OTHER); +} +} // namespace MiniZinc + +#include +#include diff --git a/include/minizinc/interpreter.hpp b/include/minizinc/interpreter.hpp new file mode 100644 index 0000000..709fe37 --- /dev/null +++ b/include/minizinc/interpreter.hpp @@ -0,0 +1,89 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +namespace MiniZinc { +inline std::pair Interpreter::cse_find(int proc, const CSEKey& key, + BytecodeProc::Mode& mode) { + std::pair result; + DTRACE2(CSE_FIND_START, (uintptr_t)this, _procs[proc].nargs); + switch (_procs[proc].nargs) { + case 1: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + result = table->find(*this, fkey, mode); + break; + } + case 2: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + result = table->find(*this, fkey, mode); + break; + } + case 3: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + result = table->find(*this, fkey, mode); + break; + } + case 4: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + result = table->find(*this, fkey, mode); + break; + } + default: { + auto vkey = static_cast(key); + auto table = static_cast*>(cse[proc]); + result = table->find(*this, vkey, mode); + break; + } + } + DTRACE2(CSE_FIND_END, (uintptr_t)this, result.second); + return result; +} +inline void Interpreter::cse_insert(int proc, CSEKey& key, BytecodeProc::Mode& mode, Val& val) { + DTRACE2(CSE_INSERT_START, (uintptr_t)this, _procs[proc].nargs); + switch (_procs[proc].nargs) { + case 1: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + table->insert(*this, fkey, mode, val); + break; + } + case 2: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + table->insert(*this, fkey, mode, val); + break; + } + case 3: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + table->insert(*this, fkey, mode, val); + break; + } + case 4: { + auto fkey = static_cast&>(key); + auto table = static_cast>*>(cse[proc]); + table->insert(*this, fkey, mode, val); + break; + } + default: { + auto vkey = static_cast(key); + auto table = static_cast*>(cse[proc]); + table->insert(*this, vkey, mode, val); + break; + } + } + DTRACE1(CSE_INSERT_END, (uintptr_t)this); +} +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/_val_decl.hh b/include/minizinc/interpreter/_val_decl.hh new file mode 100644 index 0000000..c3ea9b9 --- /dev/null +++ b/include/minizinc/interpreter/_val_decl.hh @@ -0,0 +1,241 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include + +namespace MiniZinc { +class Val; +} +namespace std { +MiniZinc::Val abs(const MiniZinc::Val& x); +} + +namespace MiniZinc { + +class Interpreter; +class Variable; +class Vec; +class WeakVal; + +enum PropStatus { PS_OK, PS_FAILED, PS_ENTAILED }; + +/// Value tagged union +class Val { + friend class WeakVal; + friend Val operator+(const Val& x, const Val& y); + friend Val operator-(const Val& x, const Val& y); + friend Val operator*(const Val& x, const Val& y); + friend Val operator/(const Val& x, const Val& y); + friend Val operator%(const Val& x, const Val& y); + friend Val std::abs(const Val& x); + friend bool operator==(const Val& x, const Val& y); + friend bool operator!=(const Val& x, const Val& y); + +protected: + static const long long int maxUnboxedVal = + (static_cast(1) << (sizeof(void*) * 8 - 3)) - static_cast(1); + /// The value + // Bit 0: 0=int, 1=RefCountedObject + // Bit 1: 0=int, 1=Infinity + // Bit 2: 0=negative, 1=positive + void* _v; + + /// TODO: implement correct overflow handling for the new type (which is smaller than IntVal) + static long long int safePlus(long long int x, long long int y) { + if (x < 0) { + if (y < -maxUnboxedVal - x) throw ArithmeticError("integer overflow"); + } else { + if (y > maxUnboxedVal - x) throw ArithmeticError("integer overflow"); + } + return x + y; + } + static long long int safeMinus(long long int x, long long int y) { + if (x < 0) { + if (y > x - -maxUnboxedVal) throw ArithmeticError("integer overflow"); + } else { + if (y < x - maxUnboxedVal) throw ArithmeticError("integer overflow"); + } + return x - y; + } + static long long int safeMult(long long int x, long long int y) { + if (y == 0) return 0; + long long unsigned int x_abs = (x < 0 ? 0 - x : x); + long long unsigned int y_abs = (y < 0 ? 0 - y : y); + if (x_abs > maxUnboxedVal / y_abs) throw ArithmeticError("integer overflow"); + return x * y; + } + static long long int safeDiv(long long int x, long long int y) { + if (y == 0) throw ArithmeticError("integer division by zero"); + if (x == 0) return 0; + if (x == -maxUnboxedVal && y == -1) throw ArithmeticError("integer overflow"); + return x / y; + } + static long long int safeMod(long long int x, long long int y) { + if (y == 0) throw ArithmeticError("integer division by zero"); + if (y == -1) return 0; + return x % y; + } + void safeSetVal(long long int i) { + ptrdiff_t ubi_p; + ubi_p = (static_cast(i < 0 ? -i : i) << 3); + if (i < 0) { + ubi_p = ubi_p | static_cast(4); + } + _v = reinterpret_cast(ubi_p); + } + +public: + static Val follow_alias(const Val& v, Interpreter* interpreter = nullptr); + + bool isRCO(void) const { + return (reinterpret_cast(_v) & static_cast(1)) == + static_cast(1); + } + RefCountedObject* toRCO(void) const { + assert(isRCO()); + return reinterpret_cast(reinterpret_cast(_v) & + ~static_cast(1)); + } + bool exists() const { return !isRCO() || toRCO()->exists(); } + bool unique() const { return !isRCO() || toRCO()->unique(); } + bool isVec(void) const { return isRCO() && toRCO()->rcoType() == RefCountedObject::VEC; } + bool isInt(void) const { + return (reinterpret_cast(_v) & static_cast(1)) == 0; + } + bool isVar(void) const { return isRCO() && toRCO()->rcoType() == RefCountedObject::VAR; } + bool containsVar(void) const { + if (isInt()) return false; + if (isVar()) return true; + for (int i = 0; i < size(); ++i) { + if ((*this)[i].containsVar()) return true; + } + return false; + } + bool contains(const Val& v) const { + if (*this == v) { + return true; + } + if (this->isVec()) { + for (int i = 0; i < this->size(); ++i) { + if ((*this)[i].contains(v)) { + return true; + } + } + } + return false; + } + + /// Access value as Variable + Variable* toVar(void) const; + + long long int toInt(void) const { + assert(isInt()); + unsigned long long int i = reinterpret_cast(_v) & ~static_cast(7); + bool inf = ((reinterpret_cast(_v) & static_cast(2)) != 0); + assert(!inf); + bool pos = ((reinterpret_cast(_v) & static_cast(4)) == 0); + if (pos) { + return static_cast(i >> 3); + } else { + return -(static_cast(i >> 3)); + } + } + IntVal toIntVal(void) const { + assert(isInt()); + unsigned long long int i = reinterpret_cast(_v) & ~static_cast(7); + bool inf = ((reinterpret_cast(_v) & static_cast(2)) != 0); + bool pos = ((reinterpret_cast(_v) & static_cast(4)) == 0); + if (inf) { + return pos ? IntVal::infinity() : -IntVal::infinity(); + } + if (pos) { + return static_cast(i >> 3); + } else { + return -(static_cast(i >> 3)); + } + } + + int timestamp() const { + assert(isRCO()); + return toRCO()->timestamp(); + } + + void rmRef(Interpreter* interpreter) { + if (isRCO()) { + RefCountedObject::rmRef(interpreter, toRCO()); + } + }; + void addRef(Interpreter* interpreter) { + if (isRCO()) { + toRCO()->addRef(interpreter); + } + } + void addMemRef(Interpreter* interpreter) { + if (isRCO()) { + toRCO()->addMemRef(interpreter); + } + }; + void rmMemRef(Interpreter* interpreter) { + if (isRCO()) { + RefCountedObject::rmMemRef(interpreter, toRCO()); + } + }; + + /// Access value as vector, return element \a i + const Val& operator[](int i) const; + /// Access value as vector, return size + size_t size(void) const; + /// Access value as vector + Vec* toVec(void) const; + +public: + Val(const long long int i = 0); + explicit Val(const RefCountedObject* d); + static Val fromIntVal(const IntVal& iv); + static Val infinity(void); + ~Val(void); + Val(const Val& v); + Val(Val&& v); + Val& operator=(const Val& v); + Val& operator=(Val&& v); + void assign(Interpreter* interpreter, const Val& v); + void assign(Interpreter* interpreter, Val&& v); + std::string toString(bool trim = false) const; + Val lb() const; + Val ub() const; + bool isFixed() const; + void finalizeLin(Interpreter* interpreter); + + // Integer interface + bool isFinite(void) const; + bool isPlusInfinity(void) const; + bool isMinusInfinity(void) const; + Val& operator+=(const Val& x); + Val& operator-=(const Val& x); + Val& operator*=(const Val& x); + Val& operator/=(const Val& x); + Val operator-() const; + Val& operator++(); + Val operator++(int); + Val& operator--(); + Val operator--(int); + Val pow(const Val& exponent); + /// Infinity-safe addition + Val plus(int x) const; + /// Infinity-safe subtraction + Val minus(int x) const; +}; + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/bytecode.hh b/include/minizinc/interpreter/bytecode.hh new file mode 100644 index 0000000..bf789ba --- /dev/null +++ b/include/minizinc/interpreter/bytecode.hh @@ -0,0 +1,226 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include + +namespace MiniZinc { + +class BytecodeProc; + +class BytecodeStream { +protected: + /// The bytecode stream + std::vector _bs; + /// The largest register used by this code + int _max_reg; + +public: + /// Constructor + BytecodeStream(void) : _max_reg(0) {} + + enum Instr { + ADDI, // R1, R2 -> R3 + SUBI, // R1, R2 -> R3 + MULI, // R1, R2 -> R3 + DIVI, // R1, R2 -> R3 + MODI, // R1, R2 -> R3 + INCI, // R1 + DECI, // R1 + + IMMI, // I, R : Load immediate integer into register + CLEAR, // R1, Rn : Clear all registers R1, R2, ..., Rn + LOAD_GLOBAL, // i -> R : Load global i into register R (globals are registers of the bottom + // stack frame) + STORE_GLOBAL, // R -> i : Store register R into global i + MOV, // R1 -> R2 + + JMP, // i : pc=i + JMPIF, // R, i: if R then pc=i + JMPIFNOT, // R, i: if not R then pc=i + + EQI, + LTI, + LEI, + + AND, + OR, + NOT, + XOR, + + ISPAR, // R1 -> R2: put whether value in R1 is not a variable into R2 + ISEMPTY, // R1 -> R2: put whether vector in R1 is empty into R2 + LENGTH, // R1 -> R2: put length of array in R1 into R2 + GET_VEC, // R1, R2 -> R3: put element R2 of vector in R1 into R3 + GET_ARRAY, // n, R1, R2, ... Rn -> Rn+1 Rn+2: put element [R2,...,Rn] of n-dimensional vector + // in R1 into Rn+1 with success signal Rn+2 + + LB, // R1 -> R2: put lower bound of value in R1 into R2 + UB, // R1 -> R2: put upper bound of value in R1 into R2 + DOM, // R1 -> R2: put domain of value in R1 into R2 + + MAKE_SET, // R1 -> R2: turn a vector (of values) into a set + INTERSECTION, // R1, R2 -> R3: put intersection of sets in R1 and R2 into R3 + UNION, // R1, R2 -> R3: put union of sets in R1 and R2 into R3 + DIFF, // R1, R2 -> R3: put difference of sets in R1 and R2 into R3 + + INTERSECT_DOMAIN, // R1, R2 -> R3: Update domain of R1 with set R2, place result in R3 + + OPEN_AGGREGATION, // i: Create a new aggregation context with symbol i + CLOSE_AGGREGATION, // Close current aggregation context, put result onto context above + + SIMPLIFY_LIN, // R1, R2, i -> R3, R4, R5: simplify linear expression (R1-R2-i), return + // coefficients (R3), variables (R4), constant (R5) + + PUSH, // R: push R onto value stack + POP, // R: pop from value stack into R + POST, // R: post constraint in R + + RET, // return from call + CALL, // m, i, cse, R1, ..., Rn: call code i in mode m with n arguments. The 0/1 flag 'cse' is + // used to signal if a CSE lookup should be performed + BUILTIN, // i, n, R1, ..., Rn : call builtin function i + TCALL, // m, i, cse : call code i in mode m (arguments are assumed to be in correct registers + // already). The 0/1 flag 'cse' is used to signal if a CSE lookup should be performed + + ITER_ARRAY, // R, l: Iterate over array in R, jump to l when finished. + ITER_VEC, // R, l: Iterate over vector in R, jump to l when finished. + ITER_RANGE, // R1, R2, l: Iterate over values in [R1, R2] + ITER_NEXT, // R: increment the topmost loop, binding the result to R. Pop and jump to loop exit + // if finished. + ITER_BREAK, // i : drop i iterators, jump to the exit of the last. + + TRACE, // R: output string representation of R + ABORT, // abort execution + + }; + + /// Get instruction at \a pc and increment \a pc + Instr instr(int& pc) const { + assert(pc < _bs.size()); + return static_cast(_bs[pc++]); + } + Val intval(int& pc) const { + assert(pc < _bs.size()); + const Val* iv = reinterpret_cast(&_bs[pc]); + pc += sizeof(Val); + return *iv; + } + Expression* expr(int& pc) { + assert(pc < _bs.size()); + Expression** e = reinterpret_cast(&_bs[pc]); + pc += sizeof(Expression*); + return *e; + } + int reg(int& pc) const { + assert(pc < _bs.size()); + const int* iv = reinterpret_cast(&_bs[pc]); + pc += sizeof(int); + return *iv; + } + char chr(int& pc) const { + assert(pc < _bs.size()); + return _bs[pc++]; + } + const char* str(int& pc) const { + assert(pc < _bs.size()); + int n = reg(pc); + const char* ret = &_bs[pc]; + pc += n + 1; + return ret; + } + + int size(void) const { return _bs.size(); } + bool eos(int pc) const { return pc >= _bs.size(); } + + void patchAddress(int pc, int addr) { + const char* cp = reinterpret_cast(&addr); + for (int i = 0; i < sizeof(int); i++) { + _bs[pc + i] = cp[i]; + } + } + + void addInstr(const Instr& i) { _bs.push_back(i); } + void addReg(int iv, bool global = false) { + if (!global) { + _max_reg = std::max(_max_reg, iv); + } + const char* cp = reinterpret_cast(&iv); + for (int i = 0; i < sizeof(int); i++) { + _bs.push_back(cp[i]); + } + } + void addSmallInt(int iv) { + const char* cp = reinterpret_cast(&iv); + for (int i = 0; i < sizeof(int); i++) { + _bs.push_back(cp[i]); + } + } + void addIntVal(const IntVal& iv) { + Val v = Val::fromIntVal(iv); + const char* cp = reinterpret_cast(&v); + for (int i = 0; i < sizeof(Val); i++) { + _bs.push_back(cp[i]); + } + } + void addCharVal(const char c) { _bs.push_back(c); } + void addStr(const std::string& s) { + addReg(s.size()); + for (char c : s) _bs.push_back(c); + _bs.push_back(0); + } + + void setNArgs(int n) { _max_reg = std::max(_max_reg, n); } + + std::string toString(const std::vector& procs = std::vector()) const; + int maxRegister(void) const { return _max_reg; } +}; + +class BytecodeProc { +public: + /// The name of this procedure + std::string name; + /// Number of arguments + int nargs; + /// Delayed execution + bool delay; + /// Modes + enum Mode { ROOT, ROOT_NEG, FUN, FUN_NEG, IMP, IMP_NEG, MAX_MODE = IMP_NEG }; + static const std::string mode_to_string[MAX_MODE + 1]; + static const bool is_neg(const Mode& mode) { + return mode == ROOT_NEG || mode == FUN_NEG || mode == IMP_NEG; + } + static const Mode negate(const Mode& mode) { + switch (mode) { + case ROOT: + return ROOT_NEG; + case IMP: + return IMP_NEG; + case FUN: + return FUN_NEG; + case ROOT_NEG: + return ROOT; + case IMP_NEG: + return IMP; + case FUN_NEG: + return FUN; + default: + assert(false); + } + } + /// The code for different modes + BytecodeStream mode[MAX_MODE + 1]; +}; + +std::vector parse(const std::string& s); +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/constraint.hh b/include/minizinc/interpreter/constraint.hh new file mode 100644 index 0000000..585ab94 --- /dev/null +++ b/include/minizinc/interpreter/constraint.hh @@ -0,0 +1,75 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include + +namespace MiniZinc { + +class Interpreter; +class Trail; + +class Constraint { + friend class Trail; + +protected: + Val _ann; + unsigned int _pred : 24; + unsigned int _mode : 8; + unsigned int _size : 30; + /// Whether the constraint is scheduled for propagation + unsigned int _scheduled : 1; + unsigned int _delayed : 1; + Val _defines; + Val _args[1]; + Constraint(Interpreter* interpreter, int pred, char mode, arg_iter arg_start, arg_iter arg_end, + size_t nargs, Val ann, Val defines, bool delayed); + Constraint(Interpreter* interpreter, int pred, char mode, const std::vector& args, Val ann, + Val defines, bool delayed); + +public: + static std::pair a(Interpreter* interpreter, int pred, char mode, + const std::vector& args, Val ann = 0, Val defines = 1, + bool delayed = false); + static std::pair a(Interpreter* interpreter, int pred, char mode, + arg_iter arg_start, arg_iter arg_end, size_t nargs, + Val ann = 0, Val defines = 1, bool delayed = false); + void destroy(Interpreter* interpreter); + void reconstruct(Interpreter* interpreter); + + ~Constraint(void) = delete; + + Val ann(void) const { return _ann; } + int pred(void) const { return _pred; } + char mode(void) const { return _mode; } + int size(void) const { return _size; } + bool delayed(void) const { return _delayed == 1; } + const Val& arg(int i) const { + assert(i < _size); + return _args[i]; + } + void arg(Interpreter* interpreter, int i, Val nv) { + assert(i < _size); + assert(_pred != 8 || i != 1 || nv.isVec()); + nv.addRef(interpreter); + _args[i].rmRef(interpreter); + _args[i] = nv; + } + + /// Flag whether constraint is currently scheduled + bool scheduled(void) const { return _scheduled == 1; } + /// Set flag whether constraint is currently scheduled + void scheduled(bool f) { _scheduled = f; } +}; + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/cse.hh b/include/minizinc/interpreter/cse.hh new file mode 100644 index 0000000..e562390 --- /dev/null +++ b/include/minizinc/interpreter/cse.hh @@ -0,0 +1,261 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include + +#include +#include + +namespace MiniZinc { + +class Interpreter; + +class WeakVal { +protected: + // Value of the Val + void* _v; + +public: + WeakVal() : _v(nullptr) {} + explicit WeakVal(Interpreter& interpreter, const Val& val) { + if (val.isInt()) { + assert((reinterpret_cast(val._v) & static_cast(3)) == 0); + _v = val._v; + } else if (val.isVar()) { + auto timestamp = val.timestamp(); + // TODO: assert timestamp <= unboxed int + assert(timestamp >= 0); + _v = reinterpret_cast(static_cast(timestamp) << 2 | + static_cast(1)); + } else { + assert(val.isVec()); + _v = reinterpret_cast(reinterpret_cast(val._v) | static_cast(3)); + toVec()->addMemRef(&interpreter); + } + } + + void destroy(Interpreter& interpreter) { + if (isVec()) { + RefCountedObject::rmMemRef(&interpreter, toVec()); + } + } + + bool isVec(void) const { + return (reinterpret_cast(_v) & static_cast(3)) == + static_cast(3); + } + Vec* toVec() const { + assert(isVec()); + return reinterpret_cast(reinterpret_cast(_v) & ~static_cast(3)); + } + size_t hash() const { + if (isVec()) { + return toVec()->hash(); + } + std::hash h; + return h(_v); + } + inline bool operator==(const WeakVal& rhs) const { + if (isVec()) { + return rhs.isVec() && (*toVec() == *rhs.toVec()); + } + return reinterpret_cast(_v) == reinterpret_cast(rhs._v); + } + inline bool operator!=(const WeakVal& rhs) const { return !(*this == rhs); } +}; + +class CSEKey { +protected: + size_t _hash; + +public: + CSEKey() : _hash(0) {} + virtual ~CSEKey() {} + // INVARIANT: Key is not used after destroy is called + virtual void destroy(Interpreter& interpreter) = 0; + const size_t hash() const { return _hash; } + + // Any implementation of CSEKey should implement a == operator for unordered_map; + // virtual const bool operator==(const CSEKey& rhs) const = 0; +}; + +class VariadicKey : public CSEKey { +private: + size_t _size; + WeakVal* _vals; + +public: + VariadicKey() : _size(0), _vals(nullptr) {} + + VariadicKey(Interpreter& interpreter, arg_iter start, arg_iter end, size_t nargs) { + _size = nargs; + _vals = (WeakVal*)malloc(_size * sizeof(WeakVal)); + + int i = 0; + for (arg_iter it = start; it != end; ++it) { + _vals[i++] = WeakVal(interpreter, *it); + } + assert(i == _size); + _hash = compute_hash(*this); + } + VariadicKey(Interpreter& interpreter, const std::vector& vals) { + _size = vals.size(); + _vals = (WeakVal*)malloc(_size * sizeof(WeakVal)); + + for (int i = 0; i < _size; ++i) { + _vals[i] = WeakVal(interpreter, vals[i]); + } + _hash = compute_hash(*this); + } + virtual ~VariadicKey() {} + + void destroy(Interpreter& interpreter) override { + for (int i = 0; i < _size; ++i) { + _vals[i].destroy(interpreter); + } + free(_vals); + } + + const bool operator==(const VariadicKey& rhs) const { + if (_size != rhs._size) { + return false; + } + for (int i = 0; i < _size; ++i) { + if (_vals[i] != rhs._vals[i]) { + return false; + } + } + return true; + } + +protected: + static size_t compute_hash(const VariadicKey& k) { + auto combine = [](size_t& incumbent, size_t h) { + incumbent ^= h + 0x9e3779b9 + (incumbent << 6) + (incumbent >> 2); + }; + size_t hash = 0; + for (int i = 0; i < k._size; ++i) { + combine(hash, k._vals[i].hash()); + } + return hash; + } +}; + +template +class FixedKey : public CSEKey { +private: + std::array _vals; + +public: + FixedKey() {} + FixedKey(Interpreter& interpreter, arg_iter start, arg_iter end) { + int i = 0; + for (arg_iter it = start; it != end; ++it) { + _vals[i++] = WeakVal(interpreter, *it); + } + assert(i == nargs); + _hash = compute_hash(*this); + } + FixedKey(Interpreter& interpreter, const std::vector& vals) { + assert(vals.size() == nargs); + for (int i = 0; i < nargs; ++i) { + _vals[i] = WeakVal(interpreter, vals[i]); + } + _hash = compute_hash(*this); + } + virtual ~FixedKey() {} + + void destroy(Interpreter& interpreter) override { + for (int i = 0; i < nargs; ++i) { + _vals[i].destroy(interpreter); + } + } + const bool operator==(const FixedKey& rhs) const { + for (int i = 0; i < nargs; ++i) { + if (_vals[i] != rhs._vals[i]) { + return false; + } + } + return true; + } + +protected: + static size_t compute_hash(const FixedKey& k) { + auto combine = [](size_t& incumbent, size_t h) { + incumbent ^= h + 0x9e3779b9 + (incumbent << 6) + (incumbent >> 2); + }; + size_t hash = 0; + for (int i = 0; i < nargs; ++i) { + combine(hash, k._vals[i].hash()); + } + return hash; + } +}; + +/// CSE table: Saved results of historical executions +template +class CSETable { +protected: + struct Hash { + size_t operator()(const Key& key) const { return key.hash(); } + }; + struct Equals { + bool operator()(const Key& lhs, const Key& rhs) const { return lhs == rhs; } + }; + typedef std::unordered_map, Hash, Equals> impl; + std::vector _table = std::vector(1); + +public: + ~CSETable() { assert(_table.size() == 1 && _table[0].empty()); } + + std::pair find(Interpreter& interpreter, const Key& key, BytecodeProc::Mode& mode); + void insert(Interpreter& interpreter, Key& key, const BytecodeProc::Mode& mode, Val& val); + + // TODO: Is this const_cast actually legal?? + void destroy(Interpreter* interpreter) { + for (auto& table : _table) { + for (auto& item : table) { + const_cast(item.first).destroy(*interpreter); + item.second.second.rmMemRef(interpreter); + } + } + _table = std::vector(1); + } + void push(Interpreter* interpreter, bool cleanup) { + if (cleanup) { + auto& table = _table.back(); + auto it = table.begin(); + while (it != table.end()) { + if (!it->second.second.exists()) { + const_cast(it->first).destroy(*interpreter); + it->second.second.rmMemRef(interpreter); + it = table.erase(it); + } else { + ++it; + } + } + } + _table.emplace_back(); + } + void pop(Interpreter* interpreter) { + for (auto& item : _table.back()) { + const_cast(item.first).destroy(*interpreter); + item.second.second.rmMemRef(interpreter); + } + _table.pop_back(); + } +}; + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/cse.hpp b/include/minizinc/interpreter/cse.hpp new file mode 100644 index 0000000..d0fb842 --- /dev/null +++ b/include/minizinc/interpreter/cse.hpp @@ -0,0 +1,152 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +template +inline std::pair CSETable::find(Interpreter& interpreter, const Key& key, + BytecodeProc::Mode& mode) { + size_t i = _table.size(); + auto it = _table[i].end(); + do { + i--; + it = _table[i].find(key); + } while (it == _table[i].end() && i > 0); + if (it != _table[i].end()) { + // FIXME: This can be a permanent replacement, but the change needs to be stored in the CSETable + // and we are removing a weak reference, not a strong reference. + Val val = Val::follow_alias(it->second.second); + BytecodeProc::Mode val_m = it->second.first; + if (!val.exists()) { + this->_table[i].erase(it); + return {Val(), false}; + } + DBG_INTERPRETER("--- CSE hit! hash(" << key.hash() + << ") -> Mode: " << BytecodeProc::mode_to_string[val_m] + << " Value: " << val.toString(DBG_TRIM_OUTPUT) << "\n"); + auto convert = [&interpreter, val_m, mode](Val v) { + assert(!v.isVec()); + if (BytecodeProc::is_neg(mode) != BytecodeProc::is_neg(val_m)) { + if (v.isInt()) { + assert(v == 0 || v == 1); + return 1 - v; + } else { + FixedKey<1> nkey(interpreter, {v}); + Val new_val; + bool found; + auto cmode = BytecodeProc::FUN; + std::tie(new_val, found) = interpreter.cse_find(PrimitiveMap::OP_NOT, nkey, cmode); + if (!found) { + Variable* new_var = Variable::a(&interpreter, interpreter.boolean_domain(), true, + interpreter.newIdent()); + new_val = Val(new_var); + auto c = Constraint::a(&interpreter, PrimitiveMap::BOOLNOT, BytecodeProc::ROOT, + {v, new_val}); + assert(c.first); + new_var->addRef(&interpreter); + new_var->addDefinition(&interpreter, c.first); + interpreter.cse_insert(PrimitiveMap::OP_NOT, nkey, cmode, new_val); + RefCountedObject::rmRef(&interpreter, new_var); + } else { + nkey.destroy(interpreter); + } + return new_val; + } + } + return v; + }; + if (mode == val_m) { + return std::make_pair(val, true); + // Assumption: 'val' must be of boolean type, otherwise mode is always FUN + } else if (val_m == BytecodeProc::ROOT || val_m == BytecodeProc::ROOT_NEG) { + return {convert(val), true}; + } else if (mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG) { + DBG_INTERPRETER("--- Run call in " + BytecodeProc::mode_to_string[mode] + " context\n"); + return {Val(), false}; + } else if (val_m == BytecodeProc::IMP || val_m == BytecodeProc::IMP_NEG) { + mode = BytecodeProc::FUN; + DBG_INTERPRETER("--- Run call in " + BytecodeProc::mode_to_string[mode] + " context\n"); + return {Val(), false}; + } else { + return {convert(val), true}; + } + } + return {Val(), false}; +} + +template +inline void CSETable::insert(Interpreter& interpreter, Key& key, + const BytecodeProc::Mode& mode, Val& val) { + DBG_INTERPRETER("--- CSE add: hash(" << key.hash() + << ") -> Mode: " << BytecodeProc::mode_to_string[mode] + << " Value: " << val.toString(DBG_TRIM_OUTPUT) << "\n"); + // If value is reference counted, flag that it's in CSE + val.addMemRef(&interpreter); + auto insertion = _table.back().emplace(key, std::make_pair(mode, val)); + if (!insertion.second) { + auto& it = insertion.first; + // We are replacing another entry within the CSE table. + assert(it->first == key); + key.destroy(interpreter); + if (it->second.first != mode) { + Val oldVal = Val::follow_alias(it->second.second); + BytecodeProc::Mode& oldMode = it->second.first; + if (mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG) { + if (oldVal.isVar()) { + Variable* v = oldVal.toVar(); + v->alias(&interpreter, + BytecodeProc::is_neg(oldMode) == BytecodeProc::is_neg(mode) ? 1 : 0); + } + } else if (mode == BytecodeProc::FUN || mode == BytecodeProc::FUN_NEG) { + if (oldVal.isVar()) { + Variable* v = oldVal.toVar(); + if (BytecodeProc::is_neg(oldMode) == BytecodeProc::is_neg(mode)) { + // Value might have already been aliased earlier in the call stack + if (val != oldVal) { + v->alias(&interpreter, val); + } + } else { + FixedKey<1> nkey(interpreter, {val}); + Val new_val; + bool found; + auto cmode = BytecodeProc::FUN; + std::tie(new_val, found) = interpreter.cse_find(PrimitiveMap::OP_NOT, nkey, cmode); + if (!found) { + Variable* new_var = Variable::a(&interpreter, interpreter.boolean_domain(), true, + interpreter.newIdent()); + new_val = Val(new_var); + auto c = Constraint::a(&interpreter, PrimitiveMap::BOOLNOT, BytecodeProc::ROOT, + {val, new_val}); + assert(c.first); + new_var->addRef(&interpreter); + new_var->addDefinition(&interpreter, c.first); + interpreter.cse_insert(PrimitiveMap::OP_NOT, nkey, cmode, new_val); + RefCountedObject::rmRef(&interpreter, new_var); + } else { + nkey.destroy(interpreter); + } + if (new_val != oldVal) { + v->alias(&interpreter, new_val); + } + } + } + } + it->second.second.rmMemRef(&interpreter); + it->second = std::make_pair(mode, val); + } + } +} + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/primitives.hh b/include/minizinc/interpreter/primitives.hh new file mode 100644 index 0000000..0f731b0 --- /dev/null +++ b/include/minizinc/interpreter/primitives.hh @@ -0,0 +1,900 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_BYTECODE_PRIMITIVES_HH__ +#define __MINIZINC_BYTECODE_PRIMITIVES_HH__ + +#include +#include +#include + +#include + +namespace MiniZinc { + +class PrimitiveMap { +public: + enum Id { + // Partial Linear Primitives + INT_PLUS, + INT_MINUS, + INT_SUM, + INT_TIMES, + + // Linear Primitives + INT_LIN_EQ, + INT_LIN_EQ_REIF, + INT_LIN_LE, + INT_LIN_LE_REIF, + + MK_INTVAR, + BOOLNOT, + OP_NOT, + CLAUSE, + CLAUSE_REIF, + FORALL, + EXISTS, + UNIFORM, + SOL, + SORT, + SORT_BY, + INT_MAX_, + INFINITY_, + INFINITE_DOMAIN, + BOOLEAN_DOMAIN, + SLICE_XD, + ARRAY_XD, + INDEX_SET, + + PARTIAL_LINEAR = INT_TIMES, + MAX_LIN = INT_LIN_LE_REIF, + MAX_ID = INDEX_SET, + }; + class Primitive { + protected: + std::string _name; + int _n_args; + Id _ident; + Primitive(const std::string& name, const Id& ident0, int n_args0) + : _name(name), _n_args(n_args0), _ident(ident0) {} + + public: + PropStatus ps_combine(const PropStatus& ps0, const PropStatus& ps1) { + if (ps0 == PS_FAILED || ps1 == PS_FAILED) return PS_FAILED; + if (ps0 == PS_ENTAILED || ps1 == PS_ENTAILED) return PS_ENTAILED; + return PS_OK; + } + const Id& ident(void) const { return _ident; } + int n_args(void) const { return _n_args; } + const std::string& name(void) const { return _name; } + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + assert(false); + throw Error("internal error"); + }; + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + assert(false); + throw Error("internal error"); + }; + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + assert(false); + throw Error("internal error"); + }; + virtual void execute(Interpreter& i, const std::vector& args) { + assert(false); + throw Error("internal error"); + }; + }; + +protected: + std::vector _p; + std::unordered_map _s; + std::vector _n; + +public: + PrimitiveMap(void); + Primitive* operator[](const std::string& s) { return _s[s]; } + Primitive* operator[](int i) { + assert(i >= 0 && i <= MAX_ID); + assert(_p.size() == MAX_ID + 1); + return _p[i]; + } + + std::vector::iterator begin(void) { return _p.begin(); } + std::vector::iterator end(void) { return _p.end(); } + int size(void) const { return _n.size(); } +}; + +PrimitiveMap& primitiveMap(void); + +namespace BytecodePrimitives { + +class IntPlus : public PrimitiveMap::Primitive { +public: + IntPlus(void) : PrimitiveMap::Primitive("int_plus", PrimitiveMap::INT_PLUS, 3) {} + virtual PropStatus subscribe(Interpreter& interpreter, Constraint* c) const { + Val lb = Val::follow_alias(c->arg(0), &interpreter).lb() + + Val::follow_alias(c->arg(1), &interpreter).lb(); + Val ub = Val::follow_alias(c->arg(0), &interpreter).ub() + + Val::follow_alias(c->arg(1), &interpreter).ub(); + + std::vector ndom = {lb, ub}; + // TODO: We officially don't know that it is not binding + c->arg(2).toVar()->domain(&interpreter, ndom, false); + + return PS_OK; + } +}; + +class IntMinus : public PrimitiveMap::Primitive { +public: + IntMinus(void) : PrimitiveMap::Primitive("int_minus", PrimitiveMap::INT_MINUS, 3) {} + virtual PropStatus subscribe(Interpreter& interpreter, Constraint* c) const { + Val lb = Val::follow_alias(c->arg(0), &interpreter).lb() - + Val::follow_alias(c->arg(1), &interpreter).ub(); + Val ub = Val::follow_alias(c->arg(0), &interpreter).ub() - + Val::follow_alias(c->arg(1), &interpreter).lb(); + + std::vector ndom = {lb, ub}; + // TODO: We officially don't know that it is not binding + c->arg(2).toVar()->domain(&interpreter, ndom, false); + + return PS_OK; + } +}; + +class IntSum : public PrimitiveMap::Primitive { +public: + IntSum(void) : PrimitiveMap::Primitive("int_sum", PrimitiveMap::INT_SUM, 2) {} + virtual PropStatus subscribe(Interpreter& interpreter, Constraint* c) const { + Val lb = 0; + Val ub = 0; + + Val arr = c->arg(0).toVec()->raw_data(); + + for (int i = 0; i < arr.size(); ++i) { + lb += Val::follow_alias(arr[i], &interpreter).lb(); + ub += Val::follow_alias(arr[i], &interpreter).ub(); + } + + std::vector ndom = {lb, ub}; + // TODO: We officially don't know that it is not binding + c->arg(1).toVar()->domain(&interpreter, ndom, false); + + return PS_OK; + } +}; + +class IntTimes : public PrimitiveMap::Primitive { +public: + IntTimes(void) : PrimitiveMap::Primitive("int_times", PrimitiveMap::INT_TIMES, 3) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + assert(c->mode() == BytecodeProc::ROOT); + bool propImmediately = true; + for (int j = 0; j < _n_args; ++j) { + Val arg = Val::follow_alias(c->arg(j), &i); + if (arg.isVar()) { + arg.toVar()->subscribe(c, Variable::SES_ANY); + if (j <= 1 && !arg.toVar()->isBounded()) { + propImmediately = false; + } + } + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + for (int j = 0; j < _n_args; ++j) { + Val arg = Val::follow_alias(c->arg(j)); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + Val a = Val::follow_alias(c->arg(0), &i); + Val b = Val::follow_alias(c->arg(1), &i); + Val res = Val::follow_alias(c->arg(2), &i); + if (b.isInt() && a.isVar()) { + std::swap(a, b); + } + + Val lb, ub; + if ((a.isVar() && !a.toVar()->isBounded()) || (b.isVar() && !b.toVar()->isBounded())) { + return PS_OK; + } + if (a.isInt() && a == 1) { + if (res.isVar()) { + if (b.isVar()) { + bool success = res.toVar()->intersectDom(&i, Val(b.toVar()->domain())); + if (!success) { + return PS_FAILED; + } + res.toVar()->alias(&i, b); + return PS_ENTAILED; + } else { + return res.toVar()->setVal(&i, b.toInt()) ? PS_ENTAILED : PS_FAILED; + } + } else { + if (b.isVar()) { + return b.toVar()->setVal(&i, res.toInt()) ? PS_ENTAILED : PS_FAILED; + } else { + return res.toInt() == b.toInt() ? PS_ENTAILED : PS_FAILED; + } + } + } + + if (a.lb() > 0 && b.lb() > 0) { + lb = a.lb() * b.lb(); + ub = a.ub() * b.ub(); + } else { + std::vector mults = {a.lb() * b.lb(), a.lb() * b.ub(), a.ub() * b.lb(), a.ub() * b.ub()}; + lb = *std::min_element(mults.begin(), mults.end()); + ub = *std::max_element(mults.begin(), mults.end()); + } + + if (lb == ub) { + if (res.isVar()) { + return res.toVar()->setVal(&i, lb) ? PS_ENTAILED : PS_FAILED; + } else { + return res.toInt() == lb ? PS_ENTAILED : PS_FAILED; + } + } else { + if (res.isVar()) { + return res.toVar()->intersectDom(&i, {lb, ub}) ? PS_OK : PS_FAILED; + } else { + return (res.toInt() >= lb && res.toInt() <= ub) ? PS_OK : PS_FAILED; + } + } + // TODO: Backwards Propagation + } +}; + +class IntLinEq : public PrimitiveMap::Primitive { +public: + IntLinEq(void) : PrimitiveMap::Primitive("int_lin_eq", PrimitiveMap::INT_LIN_EQ, 3) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + // Check me: linear equation should be in its simplified form. + + bool propImmediately = false; + int vars = 0; + Val arr = c->arg(1).toVec()->raw_data(); + for (unsigned int j = 0; j < arr.size(); j++) { + Val v = arr[j]; + assert(v.isVar()); + v.toVar()->subscribe(c, Variable::SES_VAL); + } + if (arr.size() <= 2 /* || propImmediately */) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val arr = c->arg(1).toVec()->raw_data(); + for (int j = 0; j < arr.size(); ++j) { + Val arg = Val::follow_alias(arr[j], &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + Val var = c->arg(1).toVec()->raw_data(); + Val weight = c->arg(0).toVec()->raw_data(); + if (var.size() == 1) { + Val v = Val::follow_alias(var[0], &i); + if (v.isVar()) { + if (c->arg(2) % weight[0] == 0) { + return v.toVar()->setVal(&i, c->arg(2) / weight[0]) ? PS_ENTAILED : PS_FAILED; + } else { + return PS_FAILED; + } + } else { + // aliased to val + return weight[0] * v == c->arg(2) ? PS_ENTAILED : PS_FAILED; + } + } + if (var.size() == 2) { + Val lhs = Val::follow_alias(var[0], &i); + Val rhs = Val::follow_alias(var[1], &i); + Val lhs_c = weight[0]; + Val rhs_c = weight[1]; + if (!lhs.isVar()) { + std::swap(lhs, rhs); + std::swap(lhs_c, rhs_c); + } + if (lhs.isVar()) { + if (rhs.isVar()) { + if (c->arg(2) == 0 && (lhs_c + rhs_c) == 0) { + if (lhs.toVar()->timestamp() < rhs.toVar()->timestamp()) { + std::swap(lhs, rhs); + } + bool success = rhs.toVar()->intersectDom(&i, Val(lhs.toVar()->domain())); + if (!success) { + return PS_FAILED; + } + lhs.toVar()->alias(&i, rhs); + return PS_ENTAILED; + } + } else { + if (c->arg(2) % lhs_c == 0) { + return lhs.toVar()->setVal(&i, (c->arg(2) - rhs_c * rhs) / lhs_c) ? PS_ENTAILED + : PS_FAILED; + } else { + return PS_FAILED; + } + } + } else { + return lhs * lhs_c + rhs * rhs_c == c->arg(2) ? PS_ENTAILED : PS_FAILED; + } + } + // More propagation? + return PS_OK; + } +}; + +class IntLinEqReif : public PrimitiveMap::Primitive { +public: + IntLinEqReif(void) + : PrimitiveMap::Primitive("int_lin_eq_reif", PrimitiveMap::INT_LIN_EQ_REIF, 4) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + // Check me: linear equation should be in its simplified form. + + bool propImmediately = false; + int vars = 0; + Val arr = c->arg(1).toVec()->raw_data(); + for (unsigned int j = 0; j < arr.size(); j++) { + Val v = arr[j]; + assert(v.isVar()); + v.toVar()->subscribe(c, Variable::SES_VAL); + } + if (c->arg(3).isVar()) { + c->arg(3).toVar()->subscribe(c, Variable::SES_VAL); + } + if (arr.size() <= 1 /* || propImmediately */) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val arr = c->arg(1).toVec()->raw_data(); + for (int j = 0; j < arr.size(); ++j) { + Val arg = Val::follow_alias(arr[j], &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + Val r = Val::follow_alias(c->arg(3), &i); + if (r.isVar()) { + r.toVar()->subscribe(c, Variable::SES_VAL); + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + Val var = c->arg(1).toVec()->raw_data(); + Val weight = c->arg(0).toVec()->raw_data(); + Val r = Val::follow_alias(c->arg(3), &i); + if (r.isInt()) { + // TODO: Rewrite to int_lin_eq + return PS_OK; + } + if (var.size() == 1) { + Val v = Val::follow_alias(var[0], &i); + Val mult = Val::follow_alias(weight[0], &i); + if (v.isVar()) { + if (c->arg(2) % mult == 0) { + Val res = c->arg(2) / mult; + Vec* dom = v.toVar()->domain(); + bool indom = true; + for (int j = 0; j < dom->size(); j += 2) { + if ((*dom)[j] <= res && res <= (*dom)[j + 1]) { + return PS_OK; + } + } + return r.toVar()->setVal(&i, false) ? PS_ENTAILED : PS_FAILED; + } else { + return PS_FAILED; + } + } else { + // aliased to val + return r.toVar()->setVal(&i, mult * v == c->arg(2)) ? PS_ENTAILED : PS_FAILED; + } + } + // More propagation? + return PS_OK; + } +}; + +class IntLinLe : public PrimitiveMap::Primitive { +public: + IntLinLe(void) : PrimitiveMap::Primitive("int_lin_le", PrimitiveMap::INT_LIN_LE, 3) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + // Check me: linear equation should be in its simplified form. + + Val arr = c->arg(1).toVec()->raw_data(); + for (unsigned int j = 0; j < arr.size(); j++) { + Val v = arr[j]; + assert(v.isVar()); // cannot be alias because of simplify_linexp + v.toVar()->subscribe(c, Variable::SES_VAL); + } + if (arr.size() <= 2 /* || propImmediately */) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val arr = c->arg(1).toVec()->raw_data(); + for (int j = 0; j < arr.size(); ++j) { + Val arg = Val::follow_alias(arr[j], &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + Val var = c->arg(1).toVec()->raw_data(); + Val weight = c->arg(0).toVec()->raw_data(); + if (var.size() == 1) { + Val v = Val::follow_alias(var[0], &i); + if (v.isVar()) { + Val newBound = c->arg(2) / weight[0]; + if (weight[0] > 0) { + return v.toVar()->setMax(&i, newBound) ? PS_ENTAILED : PS_FAILED; + } else { + return v.toVar()->setMin(&i, newBound) ? PS_ENTAILED : PS_FAILED; + } + } else { + // aliased to val + return weight[0] * v <= c->arg(2) ? PS_ENTAILED : PS_FAILED; + } + } + // More propagation? + return PS_OK; + } +}; + +class IntLinLeReif : public PrimitiveMap::Primitive { +public: + IntLinLeReif(void) + : PrimitiveMap::Primitive("int_lin_le_reif", PrimitiveMap::INT_LIN_LE_REIF, 4) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + // Check me: linear equation should be in its simplified form. + + Val arr = c->arg(1).toVec()->raw_data(); + for (unsigned int j = 0; j < arr.size(); j++) { + Val v = arr[j]; + assert(v.isVar()); // cannot be alias because of simplify_linexp + v.toVar()->subscribe(c, Variable::SES_VAL); + } + if (c->arg(3).isVar()) { + c->arg(3).toVar()->subscribe(c, Variable::SES_VAL); + } + if (arr.size() <= 1 /* || propImmediately */) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val arr = c->arg(1).toVec()->raw_data(); + for (int j = 0; j < arr.size(); ++j) { + Val arg = Val::follow_alias(arr[j], &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + Val r = Val::follow_alias(c->arg(3), &i); + if (r.isVar()) { + r.toVar()->unsubscribe(c); + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + Val var = c->arg(1).toVec()->raw_data(); + Val weight = c->arg(0).toVec()->raw_data(); + Val r = Val::follow_alias(c->arg(3), &i); + if (r.isInt()) { + // TODO: Replace with int_lin_le + return PS_OK; + } + if (var.size() == 1) { + Val mult = weight[0]; + Val v = Val::follow_alias(var[0], &i); + if (v.isVar()) { + if (mult * v.lb() <= c->arg(2) && mult * v.ub() <= c->arg(2)) { + return r.toVar()->setVal(&i, true) ? PS_ENTAILED : PS_FAILED; + } else if (mult * v.lb() > c->arg(2) && mult * v.ub() > c->arg(2)) { + return r.toVar()->setVal(&i, false) ? PS_ENTAILED : PS_FAILED; + } + } else { + // aliased to val + return r.toVar()->setVal(&i, mult * v <= c->arg(2)) ? PS_ENTAILED : PS_FAILED; + } + } + // More propagation? + return PS_OK; + } +}; + +class MkIntVar : public PrimitiveMap::Primitive { +public: + MkIntVar(void) : PrimitiveMap::Primitive("mk_intvar", PrimitiveMap::MK_INTVAR, 1) {} +}; + +class BoolNot : public PrimitiveMap::Primitive { +public: + BoolNot(void) : PrimitiveMap::Primitive("bool_not", PrimitiveMap::BOOLNOT, 2) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + bool propImmediately = false; + for (unsigned int j = 0; j < _n_args; j++) { + Val arg = c->arg(j); + if (arg.isVar()) { + arg.toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + for (unsigned int j = 0; j < _n_args; j++) { + Val arg = Val::follow_alias(c->arg(j), &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { + Val lhs = Val::follow_alias(c->arg(0), &i); + Val rhs = Val::follow_alias(c->arg(1), &i); + if (!lhs.isInt()) { + std::swap(lhs, rhs); + } + if (!lhs.isInt()) { + return PS_OK; + } + if (rhs.isInt()) { + return lhs == rhs ? PS_ENTAILED : PS_FAILED; + } + return rhs.toVar()->setVal(&i, 1 - lhs) ? PS_ENTAILED : PS_FAILED; + } +}; +// Reserve procedure code for op_not operation (to create CSE entries) +class OpNot : public PrimitiveMap::Primitive { +public: + OpNot(void) : PrimitiveMap::Primitive("f_op_not_vb", PrimitiveMap::OP_NOT, 1) {} +}; + +class Clause : public PrimitiveMap::Primitive { +public: + Clause(void) : PrimitiveMap::Primitive("bool_clause", PrimitiveMap::CLAUSE, 2) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + bool propImmediately = false; + Val pos = c->arg(0).toVec()->raw_data(); + Val neg = c->arg(1).toVec()->raw_data(); + + for (unsigned int i = 0; i < pos.size(); i++) { + if (pos[i].isVar()) { + pos[i].toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + for (unsigned int i = 0; i < neg.size(); i++) { + if (neg[i].isVar()) { + neg[i].toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val pos = c->arg(0).toVec()->raw_data(); + Val neg = c->arg(1).toVec()->raw_data(); + + for (unsigned int i = 0; i < pos.size(); i++) { + Val arg = Val::follow_alias(pos[i]); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + for (unsigned int i = 0; i < neg.size(); i++) { + Val arg = Val::follow_alias(neg[i]); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { return PS_OK; } +}; + +class ClauseReif : public PrimitiveMap::Primitive { +public: + ClauseReif(void) : PrimitiveMap::Primitive("bool_clause_reif", PrimitiveMap::CLAUSE_REIF, 3) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + bool propImmediately = false; + Val pos = c->arg(0).toVec()->raw_data(); + Val neg = c->arg(1).toVec()->raw_data(); + + for (unsigned int i = 0; i < pos.size(); i++) { + if (pos[i].isVar()) { + pos[i].toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + for (unsigned int i = 0; i < neg.size(); i++) { + if (neg[i].isVar()) { + neg[i].toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + if (c->arg(2).isVar()) { + c->arg(2).toVar()->subscribe(c, Variable::SES_VAL); + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val pos = c->arg(0).toVec()->raw_data(); + Val neg = c->arg(1).toVec()->raw_data(); + + for (unsigned int i = 0; i < pos.size(); i++) { + Val arg = Val::follow_alias(pos[i]); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + for (unsigned int i = 0; i < neg.size(); i++) { + Val arg = Val::follow_alias(neg[i]); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + + Val arg = Val::follow_alias(c->arg(2)); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { return PS_OK; } +}; + +class Forall : public PrimitiveMap::Primitive { +public: + Forall(void) : PrimitiveMap::Primitive("array_bool_and", PrimitiveMap::FORALL, 2) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + bool propImmediately = false; + + Val arr = c->arg(0).toVec()->raw_data(); + for (unsigned int j = 0; j < arr.size(); j++) { + if (arr[j].isVar()) { + arr[j].toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + if (c->arg(1).isVar()) { + c->arg(1).toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val arr = c->arg(0).toVec()->raw_data(); + for (unsigned int i = 0; i < arr.size(); i++) { + Val arg = Val::follow_alias(arr[i]); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + Val arg = Val::follow_alias(c->arg(1)); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { return PS_OK; } +}; + +class Exists : public PrimitiveMap::Primitive { +public: + Exists(void) : PrimitiveMap::Primitive("array_bool_or", PrimitiveMap::EXISTS, 2) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + bool propImmediately = false; + + Val arr = c->arg(0).toVec()->raw_data(); + for (int j = 0; j < arr.size(); ++j) { + if (arr[j].isVar()) { + arr[j].toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + } + if (c->arg(1).isVar()) { + c->arg(1).toVar()->subscribe(c, Variable::SES_VAL); + } else { + propImmediately = true; + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + Val arr = c->arg(0).toVec()->raw_data(); + for (int j = 0; j < arr.size(); ++j) { + Val arg = Val::follow_alias(arr[j], &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* c) const { return PS_OK; } +}; + +class Uniform : public PrimitiveMap::Primitive { +public: + Uniform() : PrimitiveMap::Primitive("uniform", PrimitiveMap::UNIFORM, 2) { + std::random_device rnd; + generator = std::mt19937(0); + } + virtual void execute(Interpreter& i, const std::vector& args); + void setSeed(int seed) { generator = std::mt19937(seed); } + +private: + std::mt19937 generator; +}; + +class Sol : public PrimitiveMap::Primitive { +public: + Sol() : PrimitiveMap::Primitive("sol", PrimitiveMap::SOL, 1) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; + +class Sort : public PrimitiveMap::Primitive { +public: + Sort() : PrimitiveMap::Primitive("internal_sort", PrimitiveMap::SORT, 1) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; + +class SortBy : public PrimitiveMap::Primitive { +public: + SortBy() : PrimitiveMap::Primitive("sort_by", PrimitiveMap::SORT_BY, 2) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; + +class IntMax : public PrimitiveMap::Primitive { +public: + IntMax(void) : PrimitiveMap::Primitive("int_max", PrimitiveMap::INT_MAX_, 3) {} + virtual PropStatus subscribe(Interpreter& i, Constraint* c) const { + bool propImmediately = true; + for (int j = 0; j < _n_args; ++j) { + Val arg = Val::follow_alias(c->arg(j), &i); + if (arg.isVar()) { + arg.toVar()->subscribe(c, Variable::SES_ANY); + if (!arg.toVar()->isBounded()) { + propImmediately = false; + } + } + } + if (propImmediately) { + return propagate(i, c); + } else { + return PS_OK; + } + } + virtual void unsubscribe(Interpreter& i, Constraint* c) const { + for (int j = 0; j < _n_args; ++j) { + Val arg = Val::follow_alias(c->arg(j), &i); + if (arg.isVar()) { + arg.toVar()->unsubscribe(c); + } + } + } + virtual PropStatus propagate(Interpreter& i, Constraint* con) const { + Val a = Val::follow_alias(con->arg(0), &i); + Val b = Val::follow_alias(con->arg(1), &i); + Val c = Val::follow_alias(con->arg(2), &i); + + if ((a.isVar() && !a.toVar()->isBounded()) || (b.isVar() && !b.toVar()->isBounded())) { + return PS_OK; + } else if (a.ub() <= b.lb() || a.ub() < c.lb() || a.lb() > c.ub()) { + if (c.isVar()) { + c.toVar()->alias(&i, b); + return PS_ENTAILED; + } else if (b.isVar()) { + return b.toVar()->setVal(&i, c) ? PS_ENTAILED : PS_FAILED; + } else { + return b == c ? PS_ENTAILED : PS_FAILED; + } + } else if (b.ub() <= a.lb() || b.ub() < c.lb() || b.lb() > c.ub()) { + if (c.isVar()) { + c.toVar()->alias(&i, a); + return PS_ENTAILED; + } else if (a.isVar()) { + return a.toVar()->setVal(&i, c) ? PS_ENTAILED : PS_FAILED; + } else { + return a == c ? PS_ENTAILED : PS_FAILED; + } + } + + Val lb, ub; + lb = std::max(a.lb(), b.lb()); + ub = std::max(a.ub(), b.ub()); + // FIXME: c is not guaranteed to be a variable + if (lb == ub) { + return c.toVar()->setVal(&i, lb) ? PS_ENTAILED : PS_FAILED; + } else { + return c.toVar()->intersectDom(&i, {lb, ub}) ? PS_OK : PS_FAILED; + } + } +}; + +class Infinity : public PrimitiveMap::Primitive { +public: + Infinity() : PrimitiveMap::Primitive("infinity", PrimitiveMap::INFINITY_, 1) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; + +class InfiniteDomain : public PrimitiveMap::Primitive { +public: + InfiniteDomain() : PrimitiveMap::Primitive("infinite_domain", PrimitiveMap::INFINITE_DOMAIN, 0) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; + +class BooleanDomain : public PrimitiveMap::Primitive { +public: + BooleanDomain() : PrimitiveMap::Primitive("boolean_domain", PrimitiveMap::BOOLEAN_DOMAIN, 0) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; + +class SliceXd : public PrimitiveMap::Primitive { +public: + SliceXd() : PrimitiveMap::Primitive("slice_Xd", PrimitiveMap::SLICE_XD, 3) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; +class ArrayXd : public PrimitiveMap::Primitive { +public: + ArrayXd() : PrimitiveMap::Primitive("array_Xd", PrimitiveMap::ARRAY_XD, 2) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; +class IndexSet : public PrimitiveMap::Primitive { +public: + IndexSet() : PrimitiveMap::Primitive("index_set", PrimitiveMap::INDEX_SET, 2) {} + virtual void execute(Interpreter& i, const std::vector& args); +}; +} // namespace BytecodePrimitives +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/interpreter/rco.hh b/include/minizinc/interpreter/rco.hh new file mode 100644 index 0000000..06ba7b6 --- /dev/null +++ b/include/minizinc/interpreter/rco.hh @@ -0,0 +1,49 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +namespace MiniZinc { + +class Interpreter; + +class RefCountedObject { +public: + enum RCOType { VEC, VAR }; + +protected: + unsigned int _model_ref_count; + unsigned int _memory_ref_count : 31; + unsigned int _rco_type : 1; + int _timestamp; + RefCountedObject(const RCOType& t, int timestamp) + : _model_ref_count(0), + _memory_ref_count(0), + _rco_type(t == VEC ? 1 : 0), + _timestamp(timestamp) {} + +public: + RCOType rcoType(void) const { return _rco_type == 1 ? VEC : VAR; } + const int timestamp() const { return _timestamp; } + + bool exists() const { return _model_ref_count > 0; } + bool alive() const { return _model_ref_count + _memory_ref_count > 0; } + bool unique() const { return _model_ref_count == 1; } + + void addRef(Interpreter* interpreter) { _model_ref_count++; } + void addMemRef(Interpreter* interpreter) { _memory_ref_count++; } + + static void rmRef(Interpreter* interpreter, RefCountedObject* rco); + static void rmMemRef(Interpreter* interpreter, RefCountedObject* rco); +}; + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/values.hh b/include/minizinc/interpreter/values.hh new file mode 100644 index 0000000..e922359 --- /dev/null +++ b/include/minizinc/interpreter/values.hh @@ -0,0 +1,304 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include +#include +#include + +namespace MiniZinc { + +typedef std::vector::const_iterator arg_iter; + +void simplify_linexp(std::vector& coeffs, std::vector& vars, Val& d); +std::tuple, std::vector, Val> simplify_linexp(Val v); + +inline Val::Val(const long long int i) { + static const unsigned int pointerBits = sizeof(void*) * 8; + static const long long int maxUnboxedVal = + (static_cast(1) << (pointerBits - 3)) - static_cast(1); + assert(i >= -maxUnboxedVal && i <= maxUnboxedVal); + ptrdiff_t ubi_p; + ubi_p = (static_cast(i < 0 ? -i : i) << 3); + if (i < 0) { + ubi_p = ubi_p | static_cast(4); + } + _v = reinterpret_cast(ubi_p); +} +inline Val::Val(const RefCountedObject* d) { + assert(d != nullptr); + _v = reinterpret_cast(reinterpret_cast(d) | static_cast(1)); +} + +inline Val::~Val(void) {} +inline Val::Val(const Val& v) : _v(v._v) {} +inline Val& Val::operator=(const Val& v) { + _v = v._v; + return *this; +} +inline Val& Val::operator=(Val&& v) { + _v = v._v; + v._v = nullptr; + return *this; +} +inline Val::Val(Val&& v) : _v(v._v) { v._v = nullptr; } +inline void Val::assign(Interpreter* interpreter, const Val& v) { + if (_v != v._v) { + if (v.isRCO()) { + v.toRCO()->addRef(interpreter); + } + rmRef(interpreter); + _v = v._v; + } +} +inline void Val::assign(Interpreter* interpreter, Val&& v) { + if (_v != v._v) { + rmRef(interpreter); + _v = v._v; + v._v = nullptr; + } +} + +inline const Val& Val::operator[](int i) const { + assert(isVec()); + return (*toVec())[i]; +} +/// Access value as vector, return size +inline size_t Val::size(void) const { + assert(isVec()); + return toVec()->size(); +} + +inline bool operator==(const Val& lhs, const Val& rhs) { + if ((reinterpret_cast(lhs._v) & static_cast(3)) != + (reinterpret_cast(rhs._v) & static_cast(3))) { + return false; + } else if (lhs.isVec() && rhs.isVec()) { + return (*lhs.toVec()) == (*rhs.toVec()); + } else if (lhs.isInt() && rhs.isInt()) { + return lhs.toInt() == rhs.toInt(); + } else { + return reinterpret_cast(lhs._v) == reinterpret_cast(rhs._v); + } +} + +inline bool operator<=(const Val& x, const Val& y) { + return y.isPlusInfinity() || x.isMinusInfinity() || + (x.isFinite() && y.isFinite() && x.toInt() <= y.toInt()); +} +inline bool operator<(const Val& x, const Val& y) { + return (y.isPlusInfinity() && !x.isPlusInfinity()) || + (x.isMinusInfinity() && !y.isMinusInfinity()) || + (x.isFinite() && y.isFinite() && x.toInt() < y.toInt()); +} +inline bool operator>=(const Val& x, const Val& y) { return y <= x; } +inline bool operator>(const Val& x, const Val& y) { return y < x; } +inline bool operator!=(const Val& x, const Val& y) { return !(x == y); } + +inline Val Val::operator-() const { + Val r = *this; + r._v = reinterpret_cast(reinterpret_cast(r._v) ^ static_cast(4)); + return r; +} +inline Val Val::infinity(void) { + Val v; + v._v = reinterpret_cast(static_cast(2)); + return v; +} +inline Val Val::fromIntVal(const IntVal& iv) { + if (iv.isPlusInfinity()) return Val::infinity(); + if (iv.isMinusInfinity()) return -Val::infinity(); + return iv.toIntUnsafe(); +} +inline bool Val::isFinite(void) const { + assert(isInt()); + return ((reinterpret_cast(_v) & static_cast(2)) == 0); +} +inline bool Val::isPlusInfinity(void) const { + assert(isInt()); + return ((reinterpret_cast(_v) & static_cast(6)) == + static_cast(2)); +} +inline bool Val::isMinusInfinity(void) const { + assert(isInt()); + return ((reinterpret_cast(_v) & static_cast(6)) == + static_cast(6)); +} + +inline Val& Val::operator+=(const Val& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + safeSetVal(safePlus(toInt(), x.toInt())); + return *this; +} +inline Val& Val::operator-=(const Val& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + safeSetVal(safeMinus(toInt(), x.toInt())); + return *this; +} +inline Val& Val::operator*=(const Val& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + safeSetVal(safeMult(toInt(), x.toInt())); + return *this; +} +inline Val& Val::operator/=(const Val& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + safeSetVal(safeDiv(toInt(), x.toInt())); + return *this; +} +inline Val& Val::operator++() { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + safeSetVal(safePlus(toInt(), 1)); + return *this; +} +inline Val Val::operator++(int) { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + Val ret = *this; + safeSetVal(safePlus(toInt(), 1)); + return ret; +} +inline Val& Val::operator--() { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + safeSetVal(safeMinus(toInt(), 1)); + return *this; +} +inline Val Val::operator--(int) { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + Val ret = *this; + safeSetVal(safeMinus(toInt(), 1)); + return ret; +} +inline Val Val::pow(const Val& exponent) { + if (!exponent.isFinite() || !isFinite()) + throw ArithmeticError("arithmetic operation on infinite value"); + if (exponent == 0) return 1; + if (exponent == 1) return *this; + Val result = 1; + for (int i = 0; i < exponent.toInt(); i++) { + result *= *this; + } + return result; +} +inline Val Val::plus(int x) const { + if (isFinite()) + return safePlus(toInt(), x); + else + return *this; +} +inline Val Val::minus(int x) const { + if (isFinite()) + return safeMinus(toInt(), x); + else + return *this; +} +/// Return whether an interval ending with \a x overlaps with an interval starting at \a y +inline bool overlaps(const Val& x, const Val& y) { return x.plus(1) >= y; } +inline Val nextHigher(const Val& x) { return x.plus(1); } +inline Val nextLower(const Val& x) { return x.minus(1); } +inline Val operator+(const Val& x, const Val& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return Val::safePlus(x.toInt(), y.toInt()); +} +inline Val operator-(const Val& x, const Val& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return Val::safeMinus(x.toInt(), y.toInt()); +} +inline Val operator*(const Val& x, const Val& y) { + if (!x.isFinite()) { + if (y.isFinite()) { + if (y == 1) return x; + if (y == -1) return -x; + } + } else if (!y.isFinite()) { + if (x == 1) return y; + if (x == -1) return -y; + } else { + return Val::safeMult(x.toInt(), y.toInt()); + } + throw ArithmeticError("arithmetic operation on infinite value"); +} +inline Val operator/(const Val& x, const Val& y) { + if (y.isFinite()) { + if (y == 1) return x; + if (y == -1) return -x; + } + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return Val::safeDiv(x.toInt(), y.toInt()); +} +inline Val operator%(const Val& x, const Val& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return Val::safeMod(x.toInt(), y.toInt()); +} + +inline Variable* Val::toVar(void) const { + assert(isVar()); + return static_cast(toRCO()); +} +inline Vec* Val::toVec(void) const { + assert(isVec()); + return static_cast(toRCO()); +} + +/// Iterator over a Vec interpreted as a range set +class VecSetRanges { + /// The vector + const Vec* rs; + /// The current range + int n; + +public: + /// Constructor + VecSetRanges(const Vec* r) : rs(r), n(0) {} + /// Check if iterator is still valid + bool operator()(void) const { return n + 1 < rs->size(); } + /// Move to next range + void operator++(void) { n += 2; } + /// Return minimum of current range + Val min(void) const { return (*rs)[n]; } + /// Return maximum of current range + Val max(void) const { return (*rs)[n + 1]; } + /// Return width of current range + Val width(void) const { return (*rs)[n + 1] - (*rs)[n] + 1; } +}; + +/// Iterator over a std::vector interpreted as a range set +class StdVecSetRanges { + /// The vector + const std::vector* rs; + /// The current range + int n; + +public: + /// Constructor + StdVecSetRanges(const std::vector* r) : rs(r), n(0) {} + /// Check if iterator is still valid + bool operator()(void) const { return n + 1 < rs->size(); } + /// Move to next range + void operator++(void) { n += 2; } + /// Return minimum of current range + Val min(void) const { return (*rs)[n]; } + /// Return maximum of current range + Val max(void) const { return (*rs)[n + 1]; } + /// Return width of current range + Val width(void) const { return (*rs)[n + 1] - (*rs)[n] + 1; } +}; + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/variable.hh b/include/minizinc/interpreter/variable.hh new file mode 100644 index 0000000..0a0cf60 --- /dev/null +++ b/include/minizinc/interpreter/variable.hh @@ -0,0 +1,133 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include + +namespace MiniZinc { + +class BytecodeProc; +class Constraint; +class Vec; + +class Variable : public RefCountedObject { + friend class Trail; + friend class Interpreter; + friend class Val; + +public: + enum SubscriptionEvent { SEV_VAL, SEV_UNIFY, SEV_DOM, SEV }; + /// Event sets propagators can subscribe to: only value events, value+unification, or any change + enum SubscriptionEventSet { SES_VAL, SES_VALUNIFY, SES_ANY }; + typedef std::unordered_map Subscriptions; + +protected: + Variable* _prev; + Variable* _next; + Val _domain; // could be the domain, or the variable or value this variable is aliased to + Val _ann; + Subscriptions _subscriptions; + std::vector _definitions; + + /// Whether domain is binding + /// TODO: tag _domain pointer instead + bool _binding; + + /// Whether variable is aliased + /// TODO: tag _domain pointer instead + bool _aliased; + + Variable(Interpreter* interpreter, Val domain, int ident); + Variable(Interpreter* interpreter, Val domain, bool binding, int ident, Val ann); + static Variable* createRoot(Interpreter* interpreter, Val domain, int ident) { + Variable* v = static_cast(::malloc(sizeof(Variable))); + return new (v) Variable(interpreter, domain, ident); + } + +public: + bool aliased(void) const { return _aliased; } + Vec* domain(void) const { + assert(!aliased()); + return _domain.toVec(); + } + Val lb() const { + assert(!aliased()); + assert(_domain[0].isInt()); + return _domain[0]; + } + Val ub() const { + assert(!aliased()); + assert(_domain[_domain.size() - 1].isInt()); + return _domain[_domain.size() - 1]; + } + bool isBounded() const { return lb().isFinite() && ub().isFinite(); } + + /// Set new minimum value included in the domain + bool setMin(Interpreter* interpreter, Val i, bool binding = true); + /// Set new maximum value included in the domain + bool setMax(Interpreter* interpreter, Val i, bool binding = true); + /// Restrict domain to a single value + bool setVal(Interpreter* interpreter, Val i, bool binding = true); + /// Intersect current domain with given domain + bool intersectDom(Interpreter* interpreter, const std::vector& dom, bool binding = true); + bool intersectDom(Interpreter* interpreter, Val dom, bool binding = true); + /// Set domain to \a newDomain, schedule propagators + void domain(Interpreter* interpreter, const Val& newDomain, bool binding); + /// Set domain to \a newDomain, schedule propagators + void domain(Interpreter* interpreter, const std::vector& newDomain, bool binding); + Val ann(void) const { return _ann; } + + Constraint* defined_by() { return (_definitions.size() == 1) ? _definitions[0] : nullptr; } + const std::vector& definitions(void) const { return _definitions; } + void addDefinition(Interpreter* interpreter, Constraint* c); + + static Variable* a(Interpreter* interpreter, Val domain, bool binding, int ident, Val ann = 0) { + Variable* v = static_cast(::malloc(sizeof(Variable))); + return new (v) Variable(interpreter, domain, binding, ident, ann); + } + static void free(Variable* var) { + // INVARIANT: var->destroy() must be called before free(var); + assert(var->_model_ref_count == 0 && var->_memory_ref_count == 0); + for (auto c : var->_definitions) { + ::free(c); + } + ::free(var); + } + ~Variable(void) = delete; + /// Destroy and unlink this variable + void destroy(Interpreter* interpreter); + void reconstruct(Interpreter* interpreter); + void alias(Interpreter* interpreter, Val v); + void unalias(Interpreter* interpreter, Val dom); + Val alias(void) { + assert(aliased()); + return _domain; + }; + Variable* prev(void) const { return _prev; } + Variable* next(void) const { return _next; } + + static void dump(Variable* d, const std::vector& bs, std::ostream& os); + + // Propagation interface + /// Flag whether definition's domain is binding + bool binding(void) const { return _binding == 1; } + /// Set flag whether definition's domain is binding + void binding(Interpreter* interpreter, bool f); + /// Add \a c to set of subscribed constraints + void subscribe(Constraint* d, const SubscriptionEventSet& events); + /// Remove \a c from set of subscribed constraints + void unsubscribe(Constraint* c); +}; + +} // namespace MiniZinc diff --git a/include/minizinc/interpreter/vector.hh b/include/minizinc/interpreter/vector.hh new file mode 100644 index 0000000..5b91efb --- /dev/null +++ b/include/minizinc/interpreter/vector.hh @@ -0,0 +1,157 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include +#include +#include + +namespace MiniZinc { + +class Vec : public RefCountedObject { +protected: + int _size : 31; + bool _has_index : 1; + Val _data[1]; + Vec(Interpreter* interpreter, int timestamp, const std::vector& v, bool has_index) + : RefCountedObject(RefCountedObject::VEC, timestamp), _size(v.size()), _has_index(has_index) { + for (unsigned int i = 0; i < v.size(); i++) { + new (&_data[i]) Val(v[i]); + _data[i].addRef(interpreter); + } + } + ~Vec(void) = delete; + +public: + int size(void) const { + assert(alive()); + return _size; + } + // TODO: Should vectors be indexed from 1 internally? + const Val& operator[](int i) const { + assert(alive()); + assert(i >= 0 && i < _size); + return _data[i]; + } + static Vec* a(Interpreter* interpreter, int timestamp, const std::vector& v, + bool has_index = false) { + Vec* nv = static_cast( + ::malloc(sizeof(Vec) + sizeof(Val) * std::max(0, static_cast(v.size() - 1)))); + new (nv) Vec(interpreter, timestamp, v, has_index); + return nv; + } + void destroyModel(Interpreter* interpreter) { + for (unsigned int i = 0; i < _size; i++) { + if (_memory_ref_count > 0u) { + _data[i].addMemRef(interpreter); + } + _data[i].rmRef(interpreter); + } + } + void destroyMemory(Interpreter* interpreter) { + for (unsigned int i = 0; i < _size; i++) { + _data[i].rmMemRef(interpreter); + } + } + void reconstruct(Interpreter* interpreter) { + for (unsigned int i = 0; i < _size; i++) { + _data[i].addRef(interpreter); + } + } + inline bool operator==(const Vec& rhs) const { + assert(alive()); + assert(rhs.alive()); + if (_size != rhs._size) { + return false; + } + for (int i = 0; i < _size; ++i) { + if ((*this)[i].isVar() && !(*this)[i].toVar()->exists()) { + return false; + } + if (rhs[i].isVar() && !rhs[i].toVar()->exists()) { + return false; + } + if (!((*this)[i] == rhs[i])) { + return false; + } + } + return true; + } + bool isPar() const; + bool hasIndexSet() const { return _has_index; } + Val raw_data() const { + if (_has_index) { + assert(_size == 2); + assert((*this)[0].isVec()); + return (*this)[0]; + } + return Val(this); + } + Val index_set() const { + assert(_has_index); + assert(_size == 2); + assert((*this)[1].isVec()); + return (*this)[1]; + } + std::vector as_vector() { + assert(alive()); + std::vector nv; + nv.reserve(size()); + for (int i = 0; i < size(); ++i) { + nv.push_back(_data[i]); + } + return nv; + } + int count(Val v) { + assert(alive()); + int count = 0; + for (int i = 0; i < _size; ++i) { + if (_data[i].isVec()) { + count += _data[i].toVec()->count(v); + } else { + count += _data[i] == v; + } + } + return count; + } + const size_t hash() const { + auto combine = [](size_t& incumbent, size_t h) { + incumbent ^= h + 0x9e3779b9 + (incumbent << 6) + (incumbent >> 2); + }; + std::hash h; + size_t hash = 0; + for (int i = 0; i < size(); ++i) { + Val v = _data[i]; + if (v.isInt()) { + combine(hash, h(v.toInt())); + } else if (v.isVar()) { + combine(hash, h(v.timestamp())); + } else { + assert(v.isVec()); + combine(hash, v.toVec()->hash()); + } + } + return hash; + } + + void finalizeLin(Interpreter* interpreter) { + for (int i = 0; i < _size; ++i) { + _data[i].finalizeLin(interpreter); + } + } + + const Val* begin(void) const { return _data; } + const Val* end(void) const { return _data + _size; } +}; + +} // namespace MiniZinc diff --git a/include/minizinc/iter.hh b/include/minizinc/iter.hh new file mode 100644 index 0000000..83979b3 --- /dev/null +++ b/include/minizinc/iter.hh @@ -0,0 +1,756 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_ITER_HH__ +#define __MINIZINC_ITER_HH__ + +#include + +#include + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#undef ERROR // MICROsoft. +#undef min +#undef max +#endif + +namespace MiniZinc { +namespace Ranges { + +/** + * \brief Base for range iterators with explicit min and max + * + * The iterator provides members \a mi and \a ma for storing the + * limits of the currently iterated range. The iterator + * continues until \a mi becomes greater than \a ma. The member function + * finish does exactly that. + * + * \ingroup FuncIterRanges + */ + +template +class MinMax { +protected: + /// Minimum of current range + Val mi; + /// Maximum of current range + Val ma; + /// %Set range such that iteration stops + void finish(void); + +public: + /// \name Constructors and initialization + //@{ + /// Default constructor + MinMax(void); + /// Initialize with range \a min to \a max + MinMax(Val min, Val max); + //@} + + /// \name Iteration control + //@{ + /// Test whether iterator is still at a range or done + bool operator()(void) const; + //@} + + /// \name Range access + //@{ + /// Return smallest value of range + Val min(void) const; + /// Return largest value of range + Val max(void) const; + /// Return width of range (distance between minimum and maximum) + Val width(void) const; + //@} +}; + +template +inline void MinMax::finish(void) { + mi = 1; + ma = 0; +} + +template +inline MinMax::MinMax(void) {} + +template +inline MinMax::MinMax(Val min, Val max) : mi(min), ma(max) {} + +template +inline bool MinMax::operator()(void) const { + return mi <= ma; +} + +template +inline Val MinMax::min(void) const { + return mi; +} +template +inline Val MinMax::max(void) const { + return ma; +} +template +inline Val MinMax::width(void) const { + if (mi > ma) return 0; + if (mi.isFinite() && ma.isFinite()) return ma - mi + 1; + return Val::infinity(); +} + +template +class Bounded { +protected: + I i; + Val _min; + bool use_min; + Val _max; + bool use_max; + Bounded(I& i, Val min0, bool umin0, Val max0, bool umax0); + +public: + static Bounded miniter(I& i, Val min); + static Bounded maxiter(I& i, Val max); + static Bounded minmaxiter(I& i, Val min, Val max); + + /// \name Iteration control + //@{ + /// Test whether iterator is still at a range or done + bool operator()(void) const; + /// Move iterator to next range (if possible) + void operator++(void); + //@} + + /// \name Range access + //@{ + /// Return smallest value of range + Val min(void) const; + /// Return largest value of range + Val max(void) const; + /// Return width of range (distance between minimum and maximum) + Val width(void) const; + //@} +}; + +template +inline Bounded::Bounded(I& i0, Val min0, bool umin0, Val max0, bool umax0) + : i(i0), _min(min0), use_min(umin0), _max(max0), use_max(umax0) { + while (i() && use_min && i.max() < _min) ++i; +} +template +inline Bounded Bounded::miniter(I& i, Val min) { + return Bounded(i, min, true, 0, false); +} +template +inline Bounded Bounded::maxiter(I& i, Val max) { + return Bounded(i, 0, false, max, true); +} +template +inline Bounded Bounded::minmaxiter(I& i, Val min, Val max) { + return Bounded(i, min, true, max, true); +} + +template +inline bool Bounded::operator()(void) const { + return i() && (!use_max || i.min() <= _max); +} +template +inline void Bounded::operator++(void) { + ++i; + while (i() && use_min && i.max() < _min) ++i; +} +template +inline Val Bounded::min(void) const { + return use_min ? std::max(_min, i.min()) : i.min(); +} +template +inline Val Bounded::max(void) const { + return use_max ? std::min(_max, i.max()) : i.max(); +} +template +inline Val Bounded::width(void) const { + if (min() > max()) return 0; + if (min().isFinite() && max().isFinite()) return max() - min() + 1; + return Val::infinity(); +} + +template +class Const { +protected: + Val _min; + Val _max; + bool done; + +public: + Const(Val min0, Val max0); + + /// \name Iteration control + //@{ + /// Test whether iterator is still at a range or done + bool operator()(void) const; + /// Move iterator to next range (if possible) + void operator++(void); + //@} + + /// \name Range access + //@{ + /// Return smallest value of range + Val min(void) const; + /// Return largest value of range + Val max(void) const; + /// Return width of range (distance between minimum and maximum) + Val width(void) const; + //@} +}; + +template +inline Const::Const(Val min0, Val max0) : _min(min0), _max(max0), done(min0 > max0) {} +template +inline bool Const::operator()(void) const { + return !done; +} +template +inline void Const::operator++(void) { + done = true; +} +template +inline Val Const::min(void) const { + return _min; +} +template +inline Val Const::max(void) const { + return _max; +} +template +inline Val Const::width(void) const { + if (min() > max()) return 0; + if (min().isFinite() && max().isFinite()) return max() - min() + 1; + return Val::infinity(); +} + +/** + * \brief Range iterator for computing union (binary) + * + * \ingroup FuncIterRanges + */ +template +class Union : public MinMax { +protected: + /// First iterator + I i; + /// Second iterator + J j; + +public: + /// \name Constructors and initialization + //@{ + /// Default constructor + Union(void); + /// Initialize with iterator \a i and \a j + Union(I& i, J& j); + /// Initialize with iterator \a i and \a j + void init(I& i, J& j); + //@} + + /// \name Iteration control + //@{ + /// Move iterator to next range (if possible) + void operator++(void); + //@} +}; + +/// Return whether an interval ending with \a x overlaps with an interval starting at \a y +inline bool overlaps(const IntVal& x, const IntVal& y) { return x.plus(1) >= y; } +/// Return whether an interval ending with \a x overlaps with an interval starting at \a y +inline bool overlaps(const FloatVal& x, const FloatVal& y) { + if (x.isPlusInfinity()) return true; + if (y.isMinusInfinity()) return true; + if (x.isFinite() && y.isFinite()) { + return std::nextafter(x.toDouble(), INFINITY) >= y.toDouble(); + } + return x >= y; +} +inline IntVal nextHigher(const IntVal& x) { return x.plus(1); } +inline IntVal nextLower(const IntVal& x) { return x.minus(1); } +inline FloatVal nextHigher(const FloatVal& x) { + if (x.isFinite()) return std::nextafter(x.toDouble(), INFINITY); + return x; +} +inline FloatVal nextLower(const FloatVal& x) { + if (x.isFinite()) return std::nextafter(x.toDouble(), -INFINITY); + return x; +} + +/* + * Binary union + * + */ + +template +inline void Union::operator++(void) { + if (!i() && !j()) { + MinMax::finish(); + return; + } + + if (!i() || (j() && (!overlaps(j.max(), i.min())))) { + MinMax::mi = j.min(); + MinMax::ma = j.max(); + ++j; + return; + } + if (!j() || (i() && (!overlaps(i.max(), j.min())))) { + MinMax::mi = i.min(); + MinMax::ma = i.max(); + ++i; + return; + } + + MinMax::mi = std::min(i.min(), j.min()); + MinMax::ma = std::max(i.max(), j.max()); + + ++i; + ++j; + +next: + if (i() && (overlaps(MinMax::ma, i.min()))) { + MinMax::ma = std::max(MinMax::ma, i.max()); + ++i; + goto next; + } + if (j() && (overlaps(MinMax::ma, j.min()))) { + MinMax::ma = std::max(MinMax::ma, j.max()); + ++j; + goto next; + } +} + +template +inline Union::Union(void) {} + +template +inline Union::Union(I& i0, J& j0) : i(i0), j(j0) { + operator++(); +} + +template +inline void Union::init(I& i0, J& j0) { + i = i0; + j = j0; + operator++(); +} + +/** + * \brief Range iterator for computing intersection (binary) + * + * \ingroup FuncIterRanges + */ +template +class Inter : public MinMax { +protected: + /// First iterator + I i; + /// Second iterator + J j; + +public: + /// \name Constructors and initialization + //@{ + /// Default constructor + Inter(void); + /// Initialize with iterator \a i and \a j + Inter(I& i, J& j); + /// Initialize with iterator \a i and \a j + void init(I& i, J& j); + //@} + + /// \name Iteration control + //@{ + /// Move iterator to next range (if possible) + void operator++(void); + //@} +}; + +/* + * Binary intersection + * + */ + +template +inline void Inter::operator++(void) { + if (!i() || !j()) goto done; + do { + while (i() && (i.max() < j.min())) ++i; + if (!i()) goto done; + while (j() && (j.max() < i.min())) ++j; + if (!j()) goto done; + } while (i.max() < j.min()); + // Now the intervals overlap: consume the smaller interval + MinMax::ma = std::min(i.max(), j.max()); + MinMax::mi = std::max(i.min(), j.min()); + if (i.max() < j.max()) + ++i; + else + ++j; + return; +done: + MinMax::finish(); +} + +template +inline Inter::Inter(void) {} + +template +inline Inter::Inter(I& i0, J& j0) : i(i0), j(j0) { + operator++(); +} + +template +inline void Inter::init(I& i0, J& j0) { + i = i0; + j = j0; + operator++(); +} + +/** + * \brief Range iterator for computing set difference + * + * \ingroup FuncIterRanges + */ + +template +class Diff : public MinMax { +protected: + /// Iterator from which to subtract + I i; + /// Iterator to be subtracted + J j; + +public: + /// \name Constructors and initialization + //@{ + /// Default constructor + Diff(void); + /// Initialize with iterator \a i and \a j + Diff(I& i, J& j); + /// Initialize with iterator \a i and \a j + void init(I& i, J& j); + //@} + + /// \name Iteration control + //@{ + /// Move iterator to next range (if possible) + void operator++(void); + //@} +}; + +template +inline void Diff::operator++(void) { + // Precondition: mi <= ma + // Task: find next mi greater than ma + while (true) { + if (!i()) break; + bool isInfinite = (!MinMax::ma.isFinite() && MinMax::ma > 0); + MinMax::mi = nextHigher(MinMax::ma); + MinMax::ma = i.max(); + if (isInfinite || MinMax::mi > i.max()) { + ++i; + if (!i()) break; + MinMax::mi = i.min(); + MinMax::ma = i.max(); + } + while (j() && (j.max() < MinMax::mi)) ++j; + if (j() && (j.min() <= MinMax::ma)) { + // Now the interval [mi ... ma] must be shrunken + // Is [mi ... ma] completely consumed? + if ((MinMax::mi >= j.min()) && (MinMax::ma <= j.max())) continue; + // Does [mi ... ma] overlap on the left? + if (j.min() <= MinMax::mi) { + MinMax::mi = nextHigher(j.max()); + // Search for max! + ++j; + if (j() && (j.min() <= MinMax::ma)) MinMax::ma = nextLower(j.min()); + } else { + MinMax::ma = nextLower(j.min()); + } + } + return; + } + MinMax::finish(); +} + +template +inline Diff::Diff(void) {} + +template +inline Diff::Diff(I& i0, J& j0) : i(i0), j(j0) { + if (!i()) { + MinMax::finish(); + } else { + MinMax::mi = nextLower(i.min()); + MinMax::ma = MinMax::mi; + operator++(); + } +} + +template +inline void Diff::init(I& i0, J& j0) { + i = i0; + j = j0; + if (!i()) { + MinMax::finish(); + } else { + MinMax::mi = nextLower(i.min()); + MinMax::ma = MinMax::mi; + operator++(); + } +} + +/** + * \brief Value iterator from range iterator + * + * \ingroup FuncIterValues + */ +template +class ToValues { +protected: + /// Range iterator used + I i; + /// Current value + IntVal cur; + /// End of current range + IntVal max; + /// Initialize iterator + void start(void); + +public: + /// \name Constructors and initialization + //@{ + /// Default constructor + ToValues(void); + /// Initialize with values from range iterator \a i + ToValues(I& i); + /// Initialize with values from range iterator \a i + void init(I& i); + //@} + + /// \name Iteration control + //@{ + /// Test whether iterator is still at a value or done + bool operator()(void) const; + /// Move iterator to next value (if possible) + void operator++(void); + //@} + + /// \name Value access + //@{ + /// Return current value + IntVal val(void) const; + //@} +}; + +template +inline ToValues::ToValues(void) {} + +template +inline void ToValues::start(void) { + if (i()) { + cur = i.min(); + max = i.max(); + } else { + cur = 1; + max = 0; + } +} + +template +inline ToValues::ToValues(I& i0) : i(i0) { + start(); +} + +template +inline void ToValues::init(I& i0) { + i = i0; + start(); +} + +template +inline bool ToValues::operator()(void) const { + return (cur <= max); +} + +template +inline void ToValues::operator++(void) { + ++cur; + if (cur > max) { + ++i; + if (i()) { + cur = i.min(); + max = i.max(); + } + } +} + +template +inline IntVal ToValues::val(void) const { + return cur; +} + +/** + * \defgroup FuncIterRangesOp Operations on range iterators + * + * \ingroup FuncIterRanges + */ + +//@{ +/// Cardinality of the set represented by range iterator \a i +template +IntVal cardinality(I& i); + +/// Check whether range iterators \a i and \a j are equal +template +bool equal(I& i, J& j); + +/// Check whether range iterator \a i is subset of range iterator \a j +template +bool subset(I& i, J& j); + +/// Check whether range iterators \a i and \a j are disjoint +template +bool disjoint(I& i, J& j); + +/// Comapre two iterators with each other +enum CompareStatus { + CS_SUBSET, ///< First is subset of second iterator + CS_DISJOINT, ///< Intersection is empty + CS_NONE ///< Neither of the above +}; + +/// Check whether range iterator \a i is a subset of \a j, or whether they are disjoint +template +CompareStatus compare(I& i, J& j); +//@} + +template +inline IntVal cardinality(I& i) { + IntVal s = 0; + while (i()) { + if (i.width().isFinite()) { + s += i.width(); + ++i; + } else { + return IntVal::infinity(); + } + } + return s; +} + +template +inline bool equal(I& i, J& j) { + // Are i and j equal? + while (i() && j()) + if ((i.min() == j.min()) && (i.max() == j.max())) { + ++i; + ++j; + } else { + return false; + } + return !i() && !j(); +} + +template +inline bool subset(I& i, J& j) { + // Is i subset of j? + while (i() && j()) + if (j.max() < i.min()) { + ++j; + } else if ((i.min() >= j.min()) && (i.max() <= j.max())) { + ++i; + } else { + return false; + } + return !i(); +} + +template +inline bool disjoint(I& i, J& j) { + // Are i and j disjoint? + while (i() && j()) + if (j.max() < i.min()) { + ++j; + } else if (i.max() < j.min()) { + ++i; + } else { + return false; + } + return true; +} + +template +inline CompareStatus compare(I& i, J& j) { + bool subset = true; + bool disjoint = true; + while (i() && j()) { + if (j.max() < i.min()) { + ++j; + } else if (i.max() < j.min()) { + ++i; + subset = false; + } else if ((i.min() >= j.min()) && (i.max() <= j.max())) { + ++i; + disjoint = false; + } else if (i.max() <= j.max()) { + ++i; + disjoint = false; + subset = false; + } else if (j.max() <= i.max()) { + ++j; + disjoint = false; + subset = false; + } + } + if (i()) subset = false; + if (subset) return CS_SUBSET; + return disjoint ? CS_DISJOINT : CS_NONE; +} + +template +inline bool less(I& i, J& j) { + while (i()) { + if (!j()) return false; + if (i.min() < j.min()) return true; + if (i.min() > j.min()) return false; + if (i.max() < j.max()) return true; + if (i.max() > j.max()) return false; + ++i; + ++j; + } + if (j()) return true; + return false; +} + +template +inline bool lessEq(I& i, J& j) { + while (i()) { + if (!j()) return false; + if (i.min() < j.min()) return true; + if (i.min() > j.min()) return false; + if (i.max() < j.max()) return true; + if (i.max() > j.max()) return false; + ++i; + ++j; + } + return true; +} + +} // namespace Ranges +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/json_parser.hh b/include/minizinc/json_parser.hh new file mode 100644 index 0000000..6c7725c --- /dev/null +++ b/include/minizinc/json_parser.hh @@ -0,0 +1,78 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_JSON_PARSER_HH__ +#define __MINIZINC_JSON_PARSER_HH__ + +#include +#include + +#include +#include +#include + +namespace MiniZinc { + +class JSONError : public LocationException { +public: + JSONError(EnvI& env, const Location& loc, const std::string& msg) + : LocationException(env, loc, msg) {} + virtual const char* what(void) const throw() { return "MiniZinc: JSON parsing error"; } +}; + +class JSONParser { +protected: + enum TokenT { + T_LIST_OPEN, + T_LIST_CLOSE, + T_OBJ_OPEN, + T_OBJ_CLOSE, + T_COMMA, + T_COLON, + T_STRING, + T_INT, + T_FLOAT, + T_BOOL, + T_NULL, + T_EOF + } t; + + class Token; + EnvI& env; + int line; + int column; + std::string filename; + Location errLocation(void) const; + Token readToken(std::istream& is); + void expectToken(std::istream& is, TokenT t); + std::string expectString(std::istream& is); + Token parseEnumString(std::istream& is); + Expression* parseExp(std::istream& is); + ArrayLit* parseArray(std::istream& is); + Expression* parseObject(std::istream& is); + + void parse(Model* m, std::istream& is); + +public: + JSONParser(EnvI& env0) : env(env0) {} + /// Parses \a filename as MiniZinc data and creates assign items in \a m + void parse(Model* m, const std::string& filename); + /// Parses \a data as JSON-encoded MiniZinc data and creates assign items in \a m + void parseFromString(Model* m, const std::string& data); + /// Check if file \a filename may contain JSON-encoded MiniZinc data + static bool fileIsJSON(const std::string& filename); + /// Check if string \a data may contain JSON-encoded MiniZinc data + static bool stringIsJSON(const std::string& data); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/model.hh b/include/minizinc/model.hh new file mode 100644 index 0000000..faf4bf8 --- /dev/null +++ b/include/minizinc/model.hh @@ -0,0 +1,420 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MODEL_HH__ +#define __MINIZINC_MODEL_HH__ + +#include +#include + +#include +#include +#include +#include + +namespace MiniZinc { + +class VarDeclIterator; +class ConstraintIterator; +class FunctionIterator; + +class CopyMap; +class EnvI; + +/// A MiniZinc model +class Model { + friend class GC; + friend class FZNSolverInstance; + friend Model* copy(EnvI& env, CopyMap& cm, Model* m, bool isFlatModel); + +protected: + /// Previous model in root set list + Model* _roots_prev; + /// Next model in root set list + Model* _roots_next; + + struct FnEntry { + std::vector t; + FunctionI* fi; + bool isPolymorphic; + FnEntry(FunctionI* fi0); + bool operator<(const FnEntry&) const; + static bool compare(const FnEntry& e1, const FnEntry& e2); + }; + + /// Add all instances of polymorphic entry \a fe to \a entries + void addPolymorphicInstances(Model::FnEntry& fe, std::vector& entries); + + /// Type of map from identifiers to function declarations + typedef ASTStringMap >::t FnMap; + /// Map from identifiers to function declarations + FnMap fnmap; + + /// Type of map from Type (represented as int) to reverse mapper functions + typedef std::unordered_map RevMapperMap; + /// Map from Type (represented as int) to reverse mapper functions + RevMapperMap revmapmap; + + /// Filename of the model + ASTString _filename; + /// Path of the model + ASTString _filepath; + /// Parent model if model was included + Model* _parent; + /// Items in the model + std::vector _items; + /// Pointer to the solve item + SolveI* _solveItem; + /// Pointer to the output item + OutputI* _outputItem; + /// File-level documentation comment + std::string _docComment; + + /// Store some declarations + struct FnDecls { + using TCheckedDecl = std::pair; // bool means that it was checked + TCheckedDecl bounds_disj = {false, nullptr}; // SCIP's bound disjunction + } fnDecls; + +public: + /// Construct empty model + Model(void); + /// Destructor + ~Model(void); + + /// Add \a i to the model + void addItem(Item* i); + + /// Get parent model + Model* parent(void) const { return _parent; } + /// Set parent model to \a p + void setParent(Model* p) { + assert(_parent == NULL); + _parent = p; + } + + /// Get file name + ASTString filename(void) const { return _filename; } + /// Get file path + ASTString filepath(void) const { return _filepath; } + /// Set file name + void setFilename(const std::string& f) { + assert(_filename.size() == 0); + _filename = ASTString(f); + } + /// Set file path + void setFilepath(const std::string& f) { + assert(_filepath.size() == 0); + _filepath = ASTString(f); + } + + /// Register a builtin function item + void registerFn(EnvI& env, FunctionI* fi); + /// Sort functions by type + void sortFn(void); + /// Check that registered functions do not clash wrt overloading + void checkFnOverloading(EnvI& env); + /// Fix function table after type checking + void fixFnMap(void); + /// Return function declaration for \a id matching \a args + FunctionI* matchFn(EnvI& env, const ASTString& id, const std::vector& args, + bool strictEnums) const; + /// Return function declaration for \a id matching types \a t + FunctionI* matchFn(EnvI& env, const ASTString& id, const std::vector& t, bool strictEnums); + /// Return function declaration matching call \a c + FunctionI* matchFn(EnvI& env, Call* c, bool strictEnums) const; + /// Return function declaration for reverse mapper for type \a t + FunctionI* matchRevMap(EnvI& env, const Type& t) const; + /// Merge all builtin functions into \a m + void mergeStdLib(EnvI& env, Model* m) const; + + /// Return item \a i + Item*& operator[](int i); + /// Return item \a i + const Item* operator[](int i) const; + /// Return number of items + unsigned int size(void) const; + + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + /// Iterator for beginning of items + iterator begin(void); + /// Iterator for beginning of items + const_iterator begin(void) const; + /// Iterator for end of items + iterator end(void); + /// Iterator for end of items + const_iterator end(void) const; + + ConstraintIterator begin_constraints(void); + ConstraintIterator end_constraints(void); + VarDeclIterator begin_vardecls(void); + VarDeclIterator end_vardecls(void); + FunctionIterator begin_functions(void); + FunctionIterator end_functions(void); + + SolveI* solveItem(void); + + OutputI* outputItem(void); + void setOutputItem(OutputI* oi); + + /// Add a file-level documentation comment + void addDocComment(std::string s) { _docComment += s; } + + /// Return the file-level documentation comment + const std::string& docComment(void) const { return _docComment; } + + /// Remove all items marked as removed + void compact(void); + + /// Get the stored function declarations + FnDecls& getFnDecls() { return fnDecls; } +}; + +class VarDeclIterator { + Model* _model; + Model::iterator _it; + +public: + typedef Model::iterator::difference_type difference_type; + typedef Model::iterator::value_type value_type; + typedef VarDeclI& reference; + typedef VarDeclI* pointer; + typedef std::forward_iterator_tag iterator_category; + + VarDeclIterator() {} + VarDeclIterator(const VarDeclIterator& vi) : _it(vi._it) {} + VarDeclIterator(Model* model, const Model::iterator& it) : _model(model), _it(it) { + while (_it != _model->end() && !(*_it)->isa()) { + ++_it; + } + } + ~VarDeclIterator() {} + + VarDeclIterator& operator=(const VarDeclIterator& vi) { + if (this != &vi) { + _it = vi._it; + } + return *this; + } + bool operator==(const VarDeclIterator& vi) const { return _it == vi._it; } + bool operator!=(const VarDeclIterator& vi) const { return _it != vi._it; } + VarDeclIterator& operator++() { + do { + ++_it; + } while (_it != _model->end() && !(*_it)->isa()); + return *this; + } + + reference operator*() const { return *(*_it)->cast(); } + pointer operator->() const { return (*_it)->cast(); } +}; + +class ConstraintIterator { + Model* _model; + Model::iterator _it; + +public: + typedef Model::iterator::difference_type difference_type; + typedef Model::iterator::value_type value_type; + typedef ConstraintI& reference; + typedef ConstraintI* pointer; + typedef std::forward_iterator_tag iterator_category; + + ConstraintIterator() {} + ConstraintIterator(const ConstraintIterator& vi) : _it(vi._it) {} + ConstraintIterator(Model* model, const Model::iterator& it) : _model(model), _it(it) { + while (_it != _model->end() && !(*_it)->isa()) { + ++_it; + } + } + ~ConstraintIterator() {} + + ConstraintIterator& operator=(const ConstraintIterator& vi) { + if (this != &vi) { + _it = vi._it; + } + return *this; + } + bool operator==(const ConstraintIterator& vi) const { return _it == vi._it; } + bool operator!=(const ConstraintIterator& vi) const { return _it != vi._it; } + ConstraintIterator& operator++() { + do { + ++_it; + } while (_it != _model->end() && !(*_it)->isa()); + return *this; + } + + reference operator*() const { return *(*_it)->cast(); } + pointer operator->() const { return (*_it)->cast(); } +}; + +class FunctionIterator { + Model* _model; + Model::iterator _it; + +public: + typedef Model::iterator::difference_type difference_type; + typedef Model::iterator::value_type value_type; + typedef FunctionI& reference; + typedef FunctionI* pointer; + typedef std::forward_iterator_tag iterator_category; + + FunctionIterator() {} + FunctionIterator(const FunctionIterator& vi) : _it(vi._it) {} + FunctionIterator(Model* model, const Model::iterator& it) : _model(model), _it(it) { + while (_it != _model->end() && !(*_it)->isa()) { + ++_it; + } + } + ~FunctionIterator() {} + + FunctionIterator& operator=(const FunctionIterator& vi) { + if (this != &vi) { + _it = vi._it; + } + return *this; + } + bool operator==(const FunctionIterator& vi) const { return _it == vi._it; } + bool operator!=(const FunctionIterator& vi) const { return _it != vi._it; } + FunctionIterator& operator++() { + do { + ++_it; + } while (_it != _model->end() && !(*_it)->isa()); + return *this; + } + + reference operator*() const { return *(*_it)->cast(); } + pointer operator->() const { return (*_it)->cast(); } +}; + +class EnvI; + +/// Environment +class Env { +private: + EnvI* e; + +public: + Env(Model* m = NULL, std::ostream& outstream = std::cout, std::ostream& errstream = std::cerr); + ~Env(void); + + Model* model(void); + void model(Model* m); + Model* flat(void); + void swap(); + Model* output(void); + EnvI& envi(void); + const EnvI& envi(void) const; + std::ostream& dumpErrorStack(std::ostream& os); + const std::vector& warnings(void); + void clearWarnings(void); + unsigned int maxCallStack(void) const; + std::ostream& evalOutput(std::ostream& os); +}; + +class CallStackItem { +public: + EnvI& env; + CallStackItem(EnvI& env0, Expression* e); + CallStackItem(EnvI& env0, Id* ident, IntVal i); + ~CallStackItem(void); +}; + +/// Visitor for model items +class ItemVisitor { +public: + /// Enter model + bool enterModel(Model* m) { return true; } + /// Enter item + bool enter(Item* m) { return true; } + /// Visit include item + void vIncludeI(IncludeI*) {} + /// Visit variable declaration + void vVarDeclI(VarDeclI*) {} + /// Visit assign item + void vAssignI(AssignI*) {} + /// Visit constraint item + void vConstraintI(ConstraintI*) {} + /// Visit solve item + void vSolveI(SolveI*) {} + /// Visit output item + void vOutputI(OutputI*) {} + /// Visit function item + void vFunctionI(FunctionI*) {} +}; + +/// Iterator over items in a model and all its included models +template +class ItemIter { +protected: + I& iter; + +public: + ItemIter(I& iter0) : iter(iter0) {} + void run(Model* m) { + std::unordered_set seen; + std::vector models; + models.push_back(m); + seen.insert(m); + while (!models.empty()) { + Model* cm = models.back(); + models.pop_back(); + if (!iter.enterModel(cm)) continue; + std::vector includedModels; + for (unsigned int i = 0; i < cm->size(); i++) { + if ((*cm)[i]->removed()) continue; + if (!iter.enter((*cm)[i])) continue; + switch ((*cm)[i]->iid()) { + case Item::II_INC: + if (seen.find((*cm)[i]->cast()->m()) == seen.end()) { + includedModels.push_back((*cm)[i]->cast()->m()); + seen.insert((*cm)[i]->cast()->m()); + } + iter.vIncludeI((*cm)[i]->cast()); + break; + case Item::II_VD: + iter.vVarDeclI((*cm)[i]->cast()); + break; + case Item::II_ASN: + iter.vAssignI((*cm)[i]->cast()); + break; + case Item::II_CON: + iter.vConstraintI((*cm)[i]->cast()); + break; + case Item::II_SOL: + iter.vSolveI((*cm)[i]->cast()); + break; + case Item::II_OUT: + iter.vOutputI((*cm)[i]->cast()); + break; + case Item::II_FUN: + iter.vFunctionI((*cm)[i]->cast()); + break; + } + } + for (unsigned int i = static_cast(includedModels.size()); i--;) { + models.push_back(includedModels[i]); + } + } + } +}; + +/// Run iterator \a i over all items of model \a m +template +void iterItems(I& i, Model* m) { + ItemIter(i).run(m); +} + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/optimize.hh b/include/minizinc/optimize.hh new file mode 100644 index 0000000..860703b --- /dev/null +++ b/include/minizinc/optimize.hh @@ -0,0 +1,117 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_OPTIMIZE_HH__ +#define __MINIZINC_OPTIMIZE_HH__ + +#include +#include + +namespace MiniZinc { + +class VarOccurrences { +public: + typedef std::unordered_set Items; + IdMap _m; + IdMap idx; + + /// Add \a to the index + void add_idx(VarDeclI* i, int idx_i); + /// Add \a to the index + void add_idx(VarDecl* e, int idx_i); + /// Find index of \a vd + int find(VarDecl* vd); + /// Remove index of \a vd + void remove(VarDecl* vd); + + /// Add \a i to the dependencies of \a v + void add(VarDecl* v, Item* i); + + /// Remove \a i from map and return new number of occurrences + int remove(VarDecl* v, Item* i); + + /// Remove all occurrences from map and return new number of occurrences + void removeAllOccurrences(VarDecl* v); + + /// Return number of occurrences of \a v + int occurrences(VarDecl* v); + + /// Return number of constraint usages of \a v + int usages(VarDecl* v); + + /// Unify \a v0 and \a v1 (removing \a v0) + void unify(EnvI& env, Model* m, Id* id0, Id* id1); + + /// Clear all entries + void clear(void); +}; + +class CollectOccurrencesE : public EVisitor { +public: + VarOccurrences& vo; + Item* ci; + CollectOccurrencesE(VarOccurrences& vo0, Item* ci0) : vo(vo0), ci(ci0) {} + void vId(const Id& id) { + if (id.decl()) vo.add(id.decl(), ci); + } +}; + +class CollectOccurrencesI : public ItemVisitor { +public: + VarOccurrences& vo; + CollectOccurrencesI(VarOccurrences& vo0) : vo(vo0) {} + void vVarDeclI(VarDeclI* v); + void vConstraintI(ConstraintI* ci); + void vSolveI(SolveI* si); +}; + +class CollectDecls : public EVisitor { +public: + VarOccurrences& vo; + std::vector& vd; + Item* item; + CollectDecls(VarOccurrences& vo0, std::vector& vd0, Item* item0) + : vo(vo0), vd(vd0), item(item0) {} + + static bool varIsFree(VarDecl* vd) { + if (vd->e() == NULL || vd->ti()->domain() == NULL || vd->ti()->computedDomain()) { + return true; + } else { + /// TODO: test if id's domain is a superset of the right hand side + /// this currently only tests for equality, and for Boolean domains + if (Id* ident = vd->e()->dyn_cast()) { + if (Expression::equal(ident->decl()->ti()->domain(), vd->ti()->domain())) { + return true; + } + } else if (vd->e() == vd->ti()->domain()) { + return true; + } + } + return false; + } + + void vId(Id& id) { + if (id.decl() && vo.remove(id.decl(), item) == 0) { + if (varIsFree(id.decl())) { + vd.push_back(id.decl()); + } + } + } +}; + +bool isOutput(VarDecl* vd); + +/// Simplyfy models in \a env +void optimize(Env& env, bool chain_compression = true); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/optimize_constraints.hh b/include/minizinc/optimize_constraints.hh new file mode 100644 index 0000000..0712f19 --- /dev/null +++ b/include/minizinc/optimize_constraints.hh @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_OPTIMIZE_CONSTRAINTS_HH__ +#define __MINIZINC_OPTIMIZE_CONSTRAINTS_HH__ + +#include +#include + +namespace MiniZinc { + +class OptimizeRegistry { +public: + enum ConstraintStatus { CS_NONE, CS_OK, CS_FAILED, CS_ENTAILED, CS_REWRITE }; + typedef ConstraintStatus (*optimizer)(EnvI& env, Item* i, Call* c, Expression*& rewrite); + +protected: + ASTStringMap::t _m; + +public: + void reg(const ASTString& call, optimizer); + ConstraintStatus process(EnvI& env, Item* i, Call* c, Expression*& rewrite); + + static OptimizeRegistry& registry(void); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/options.hh b/include/minizinc/options.hh new file mode 100644 index 0000000..34d1e89 --- /dev/null +++ b/include/minizinc/options.hh @@ -0,0 +1,46 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_OPTIONS_HH__ +#define __MINIZINC_OPTIONS_HH__ + +#include + +namespace MiniZinc { +class Options { +protected: + std::unordered_map _options; + + Expression* getParam(const std::string& name) const; + +public: + void setIntParam(const std::string& name, KeepAlive e); + void setFloatParam(const std::string& name, KeepAlive e); + void setBoolParam(const std::string& name, KeepAlive e); + void setStringParam(const std::string& name, KeepAlive e); + void setIntParam(const std::string& name, long long int e); + void setFloatParam(const std::string& name, double e); + void setBoolParam(const std::string& name, bool e); + void setStringParam(const std::string& name, std::string e); + long long int getIntParam(const std::string& name) const; + long long int getIntParam(const std::string& name, long long int def) const; + double getFloatParam(const std::string& name) const; + double getFloatParam(const std::string& name, double def) const; + bool getBoolParam(const std::string& name) const; + bool getBoolParam(const std::string& name, bool def) const; + std::string getStringParam(const std::string& name) const; + std::string getStringParam(const std::string& name, std::string def) const; + bool hasParam(const std::string& name) const; + std::ostream& dump(std::ostream& os); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/output.hh b/include/minizinc/output.hh new file mode 100644 index 0000000..e455819 --- /dev/null +++ b/include/minizinc/output.hh @@ -0,0 +1,42 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_OUTPUT_HH__ +#define __MINIZINC_OUTPUT_HH__ + +#include + +namespace MiniZinc { + +/// Remove all output annotations from \a vd +void removeIsOutput(VarDecl* vd); + +/// Copy output item to FlatZinc model +void copyOutput(EnvI& e); + +/// Copy all dependent variable declarations +void outputVarDecls(EnvI& env, Item* ci, Expression* e); + +/// Create initial output model +void createOutput(EnvI& e, std::vector& deletedFlatVarDecls, + FlatteningOptions::OutputMode outputMode, bool outputObjective, + bool includeOutputItem); +/// Finalise output model after flattening is complete +void finaliseOutput(EnvI& e, std::vector& deletedFlatVarDecls); + +/// Remove all links to variables in flat model from output model in \a env +void cleanupOutput(EnvI& env); + +ArrayLit* createJSONOutput(EnvI& env, bool outputObjective, bool includeOutputItem); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/parser.hh b/include/minizinc/parser.hh new file mode 100644 index 0000000..f86660b --- /dev/null +++ b/include/minizinc/parser.hh @@ -0,0 +1,145 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_PARSER_HH__ +#define __MINIZINC_PARSER_HH__ + +// This is a workaround for a bug in flex that only shows up +// with the Microsoft C++ compiler +#if defined(_MSC_VER) +#define YY_NO_UNISTD_H +#ifdef __cplusplus +extern "C" int isatty(int); +#endif +#endif + +// The Microsoft C++ compiler marks certain functions as deprecated, +// so let's take the alternative definitions +#if defined(_MSC_VER) +#define strdup _strdup +#define fileno _fileno +#endif + +#if defined(_MSC_VER) +#pragma warning(disable : 4065) +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +struct ParseWorkItem { + Model* m; + IncludeI* ii; + std::string dirName; + std::string fileName; + bool isModelString; + ParseWorkItem(Model* m0, IncludeI* ii0, const std::string& dirName0, const std::string& fileName0, + bool isModelString0 = false) + : m(m0), ii(ii0), dirName(dirName0), fileName(fileName0), isModelString(isModelString0) {} +}; + +/// %State of the %MiniZinc parser +class ParserState { +public: + ParserState(const std::string& f, const std::string& b, std::ostream& err0, + std::vector& files0, std::map& seenModels0, + MiniZinc::Model* model0, bool isDatafile0, bool isFlatZinc0, bool parseDocComments0) + : filename(f.c_str()), + buf(b.c_str()), + pos(0), + length(static_cast(b.size())), + lineStartPos(0), + nTokenNextStart(1), + hadNewline(false), + files(files0), + seenModels(seenModels0), + model(model0), + isDatafile(isDatafile0), + isFlatZinc(isFlatZinc0), + parseDocComments(parseDocComments0), + hadError(false), + err(err0) {} + + const char* filename; + + void* yyscanner; + const char* buf; + unsigned int pos, length; + + int lineStartPos; + int nTokenNextStart; + bool hadNewline; + + std::vector& files; + std::map& seenModels; + MiniZinc::Model* model; + + bool isDatafile; + bool isFlatZinc; + bool parseDocComments; + bool hadError; + std::vector syntaxErrors; + std::ostream& err; + + std::string stringBuffer; + + void printCurrentLine(int firstCol, int lastCol) { + const char* eol_c = strchr(buf + lineStartPos, '\n'); + if (eol_c) { + if (eol_c == buf + lineStartPos) return; + err << std::string(buf + lineStartPos, eol_c - (buf + lineStartPos)); + } else { + err << buf + lineStartPos; + } + err << std::endl; + for (int i = 0; i < firstCol - 1; i++) err << " "; + for (int i = firstCol; i <= lastCol; i++) err << "^"; + err << std::endl; + } + + int fillBuffer(char* lexBuf, unsigned int lexBufSize) { + if (pos >= length) return 0; + int num = std::min(length - pos, lexBufSize); + memcpy(lexBuf, buf + pos, num); + pos += num; + return num; + } +}; + +Model* parse(Env& env, const std::vector& filename, + const std::vector& datafiles, const std::string& textModel, + const std::string& textModelName, const std::vector& includePaths, + bool ignoreStdlib, bool parseDocComments, bool verbose, std::ostream& err); + +Model* parseFromString(Env& env, const std::string& model, const std::string& filename, + const std::vector& includePaths, bool ignoreStdlib, + bool parseDocComments, bool verbose, std::ostream& err, + std::vector& syntaxErrors); + +Model* parseData(Env& env, Model* m, const std::vector& datafiles, + const std::vector& includePaths, bool ignoreStdlib, + bool parseDocComments, bool verbose, std::ostream& err); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/passes/compile_pass.hh b/include/minizinc/passes/compile_pass.hh new file mode 100644 index 0000000..9c9321a --- /dev/null +++ b/include/minizinc/passes/compile_pass.hh @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Kevin Leo + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_COMPILE_PASS_HH__ +#define __MINIZINC_COMPILE_PASS_HH__ + +#include +#include + +namespace MiniZinc { + +struct CompilePassFlags { + bool noMIPdomains; + bool verbose; + bool statistics; + bool optimize; + bool chain_compression; + bool newfzn; + bool werror; + bool model_check_only; + bool model_interface_only; + bool allow_multi_assign; +}; + +class CompilePass : public Pass { +private: + Env* env; + FlatteningOptions fopts; + CompilePassFlags compflags; + std::string library; + std::vector includePaths; + bool change_library; + bool ignore_unknown_ids; + +public: + CompilePass(Env* e, FlatteningOptions& opts, CompilePassFlags& cflags, + std::string globals_library, std::vector include_paths, bool change_lib, + bool ignore_unknown); + + Env* run(Env* env, std::ostream& log); + ~CompilePass(); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/passes/gecode_pass.hh b/include/minizinc/passes/gecode_pass.hh new file mode 100644 index 0000000..22381a0 --- /dev/null +++ b/include/minizinc/passes/gecode_pass.hh @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Kevin Leo + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GECODE_PASS_HH__ +#define __MINIZINC_GECODE_PASS_HH__ + +#include + +namespace MiniZinc { + +class GecodeOptions; + +class GecodePass : public Pass { + GecodeOptions* gopts; + +public: + GecodePass(GecodeOptions* gopts); + + Env* run(Env* e, std::ostream& log); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/pathfileprinter.hh b/include/minizinc/pathfileprinter.hh new file mode 100644 index 0000000..393d2d5 --- /dev/null +++ b/include/minizinc/pathfileprinter.hh @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_PATHPRINTER_HH +#define __MINIZINC_PATHPRINTER_HH + +#include + +#include + +namespace MiniZinc { + +class Model; + +class PathFilePrinter { + typedef std::pair NamePair; + typedef std::unordered_map NameMap; + + NameMap betternames; + +private: + std::ostream& os; + int constraint_index; + + void addBetterName(Id* id, std::string name, std::string path, bool overwrite); + +public: + PathFilePrinter(std::ostream& o, EnvI& envi); + void print(Model* m); + void print(Item* i); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/prettyprinter.hh b/include/minizinc/prettyprinter.hh new file mode 100644 index 0000000..7621abb --- /dev/null +++ b/include/minizinc/prettyprinter.hh @@ -0,0 +1,82 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Pierre Wilke + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_PRETTYPRINTER_HH__ +#define __MINIZINC_PRETTYPRINTER_HH__ + +#include + +#include + +namespace MiniZinc { + +class Document; +class ItemDocumentMapper; +class PrettyPrinter; + +class Printer { +private: + EnvI* env; + ItemDocumentMapper* ism; + PrettyPrinter* printer; + std::ostream& _os; + int _width; + bool _flatZinc; + + void init(void); + void p(Document* d); + void p(const Item* i); + +public: + Printer(std::ostream& os, int width = 80, bool flatZinc = true, EnvI* env = NULL); + ~Printer(void); + + void print(const Expression* e); + void print(const Item* i); + void print(const Model* m); + + static std::string escapeStringLit(const ASTString& s); +}; + +/// Output operator for expressions +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const Expression& e) { + std::basic_ostringstream s; + s.copyfmt(os); + s.width(0); + Printer p(s, 0); + p.print(&e); + return os << s.str(); +} + +/// Output operator for items +template +std::basic_ostream& operator<<(std::basic_ostream& os, const Item& i) { + std::basic_ostringstream s; + s.copyfmt(os); + s.width(0); + Printer p(s); + p.print(&i); + return os << s.str(); +} + +void ppFloatVal(std::ostream& os, const FloatVal& fv, bool hexFloat = false); + +} // namespace MiniZinc + +void debugprint(MiniZinc::Expression* e); +void debugprint(MiniZinc::Item* i); +void debugprint(MiniZinc::Model* m); +void debugprint(const MiniZinc::Location& l); + +#endif diff --git a/include/minizinc/process.hh b/include/minizinc/process.hh new file mode 100644 index 0000000..47bdb98 --- /dev/null +++ b/include/minizinc/process.hh @@ -0,0 +1,373 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_PROCESS_HH__ +#define __MINIZINC_PROCESS_HH__ + +#include + +const auto SolverInstance__ERROR = MiniZinc::SolverInstance::ERROR; // before windows.h +#ifdef _WIN32 +#define NOMINMAX +#include +#include +//#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +#ifdef _WIN32 + +template +void ReadPipePrint(HANDLE g_hCh, bool* _done, std::ostream* pOs, S2O* pSo, std::mutex* mtx) { + bool& done = *_done; + assert(pOs != 0 || pSo != 0); + while (!done) { + char buffer[5255]; + char nl_buffer[5255]; + DWORD count = 0; + BOOL bSuccess = ReadFile(g_hCh, buffer, sizeof(buffer) - 1, &count, NULL); + if (bSuccess && count > 0) { + int nl_count = 0; + for (int i = 0; i < count; i++) { + if (buffer[i] != 13) { + nl_buffer[nl_count++] = buffer[i]; + } + } + nl_buffer[nl_count] = 0; + std::lock_guard lck(*mtx); + if (pSo) pSo->feedRawDataChunk(nl_buffer); + if (pOs) (*pOs) << nl_buffer << std::flush; + } else { + if (pSo) pSo->feedRawDataChunk("\n"); // in case the last chunk had none + done = true; + } + } +} + +void TimeOut(HANDLE hProcess, bool* doneStdout, bool* doneStderr, int timeout, + std::timed_mutex* mtx); + +#endif + +template +class Process { +protected: + std::vector _fzncmd; + S2O* pS2Out; + int timelimit; + bool sigint; +#ifndef _WIN32 + static void handleInterrupt(int signal) { + if (signal == SIGINT) + hadInterrupt = true; + else + hadTerm = true; + } + static bool hadInterrupt; + static bool hadTerm; +#endif +public: + Process(std::vector& fzncmd, S2O* pso, int tl, bool si) + : _fzncmd(fzncmd), pS2Out(pso), timelimit(tl), sigint(si) { + assert(0 != pS2Out); + } + int run(void) { +#ifdef _WIN32 + // TODO: implement hard timelimits for windows + + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + HANDLE g_hChildStd_IN_Rd = NULL; + HANDLE g_hChildStd_IN_Wr = NULL; + HANDLE g_hChildStd_OUT_Rd = NULL; + HANDLE g_hChildStd_OUT_Wr = NULL; + HANDLE g_hChildStd_ERR_Rd = NULL; + HANDLE g_hChildStd_ERR_Wr = NULL; + + // Create a pipe for the child process's STDOUT. + if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) + std::cerr << "Stdout CreatePipe" << std::endl; + // Ensure the read handle to the pipe for STDOUT is not inherited. + if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) + std::cerr << "Stdout SetHandleInformation" << std::endl; + + // Create a pipe for the child process's STDERR. + if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) + std::cerr << "Stderr CreatePipe" << std::endl; + // Ensure the read handle to the pipe for STDERR is not inherited. + if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) + std::cerr << "Stderr SetHandleInformation" << std::endl; + + // Create a pipe for the child process's STDIN + if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) + std::cerr << "Stdin CreatePipe" << std::endl; + // Ensure the write handle to the pipe for STDIN is not inherited. + if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) + std::cerr << "Stdin SetHandleInformation" << std::endl; + + PROCESS_INFORMATION piProcInfo; + STARTUPINFO siStartInfo; + BOOL bSuccess = FALSE; + + // Set up members of the PROCESS_INFORMATION structure. + ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); + + // Set up members of the STARTUPINFO structure. + // This structure specifies the STDIN and STDOUT handles for redirection. + ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = g_hChildStd_ERR_Wr; + siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + std::string cmdline = FileUtils::combineCmdLine(_fzncmd); + char* cmdstr = strdup(cmdline.c_str()); + + BOOL processStarted = CreateProcess(NULL, + cmdstr, // command line + NULL, // process security attributes + NULL, // primary thread security attributes + TRUE, // handles are inherited + 0, // creation flags + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &piProcInfo); // receives PROCESS_INFORMATION + + if (!processStarted) { + std::stringstream ssm; + ssm << "Error occurred when executing FZN solver with command \"" << cmdstr << "\"."; + throw InternalError(ssm.str()); + } + + CloseHandle(piProcInfo.hThread); + delete cmdstr; + + // Stop ReadFile from blocking + CloseHandle(g_hChildStd_OUT_Wr); + CloseHandle(g_hChildStd_ERR_Wr); + // Just close the child's in pipe here + CloseHandle(g_hChildStd_IN_Rd); + bool doneStdout = false; + bool doneStderr = false; + // Threaded solution seems simpler than asyncronous pipe reading + std::mutex pipeMutex; + std::timed_mutex terminateMutex; + terminateMutex.lock(); + thread thrStdout(&ReadPipePrint, g_hChildStd_OUT_Rd, &doneStdout, nullptr, pS2Out, + &pipeMutex); + thread thrStderr(&ReadPipePrint, g_hChildStd_ERR_Rd, &doneStderr, &pS2Out->getLog(), + nullptr, &pipeMutex); + thread thrTimeout(TimeOut, piProcInfo.hProcess, &doneStdout, &doneStderr, timelimit, + &terminateMutex); + thrStdout.join(); + thrStderr.join(); + terminateMutex.unlock(); + thrTimeout.join(); + DWORD exitCode = 0; + if (GetExitCodeProcess(piProcInfo.hProcess, &exitCode) == FALSE) { + exitCode = 1; + } + CloseHandle(piProcInfo.hProcess); + + // Hard timeout: GenerateConsoleCtrlEvent() + + return exitCode; + } +#else + int pipes[3][2]; + pipe(pipes[0]); + pipe(pipes[1]); + pipe(pipes[2]); + + if (int childPID = fork()) { + close(pipes[0][0]); + close(pipes[1][1]); + close(pipes[2][1]); + close(pipes[0][1]); + + fd_set fdset; + FD_ZERO(&fdset); + + struct timeval starttime; + gettimeofday(&starttime, NULL); + + struct timeval timeout_orig; + timeout_orig.tv_sec = timelimit / 1000; + timeout_orig.tv_usec = (timelimit % 1000) * 1000; + struct timeval timeout = timeout_orig; + + hadInterrupt = false; + hadTerm = false; + struct sigaction sa; + struct sigaction old_sa_int; + struct sigaction old_sa_term; + sa.sa_handler = &handleInterrupt; + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + sigaction(SIGINT, &sa, &old_sa_int); + sigaction(SIGTERM, &sa, &old_sa_term); + + bool done = hadTerm || hadInterrupt; + bool timed_out = false; + while (!done) { + FD_SET(pipes[1][0], &fdset); + FD_SET(pipes[2][0], &fdset); + int sel = select(FD_SETSIZE, &fdset, NULL, NULL, timelimit == 0 ? NULL : &timeout); + if (sel == -1) { + if (errno != EINTR) { + // some error has happened + throw InternalError(std::string("Error in communication with solver: ") + + strerror(errno)); + } + } + if (timelimit != 0) { + timeval currentTime; + gettimeofday(¤tTime, NULL); + if (sel != 0) { + timeval elapsed; + elapsed.tv_sec = currentTime.tv_sec - starttime.tv_sec; + elapsed.tv_usec = currentTime.tv_usec - starttime.tv_usec; + if (elapsed.tv_usec < 0) { + elapsed.tv_sec--; + elapsed.tv_usec += 1000000; + } + // Reset timeout to original limit + timeout = timeout_orig; + // Subtract elapsed time + timeout.tv_usec = timeout.tv_usec - elapsed.tv_usec; + if (timeout.tv_usec < 0) { + timeout.tv_sec--; + timeout.tv_usec += 1000000; + } + timeout.tv_sec = timeout.tv_sec - elapsed.tv_sec; + } else { + timeout.tv_usec = 0; + timeout.tv_sec = 0; + } + if (hadTerm || hadInterrupt || timeout.tv_sec < 0 || + (timeout.tv_sec == 0 && timeout.tv_usec == 0)) { + timed_out = true; + if (sigint) { + kill(childPID, SIGINT); + timeout.tv_sec = 0; + timeout.tv_usec = 200000; + timeout_orig = timeout; + starttime = currentTime; + sigint = false; + } else { + kill(childPID, SIGTERM); + pS2Out->feedRawDataChunk("\n"); // in case last chunk did not end with \n + done = true; + } + } + } + + for (int i = 1; i <= 2; ++i) { + if (FD_ISSET(pipes[i][0], &fdset)) { + char buffer[1000]; + int count = read(pipes[i][0], buffer, sizeof(buffer) - 1); + if (count > 0) { + buffer[count] = 0; + if (1 == i) { + // cerr << "mzn-fzn: raw chunk stdout::: " << flush; + // cerr << buffer << flush; + pS2Out->feedRawDataChunk(buffer); + } else { + pS2Out->getLog() << buffer << std::flush; + } + } else if (1 == i) { + pS2Out->feedRawDataChunk("\n"); // in case last chunk did not end with \n + done = true; + } + } + } + } + + close(pipes[1][0]); + close(pipes[2][0]); + int exitStatus = timed_out ? 0 : 1; + int childStatus; + int pidStatus = waitpid(childPID, &childStatus, 0); + if (!timed_out && pidStatus > 0) { + if (WIFEXITED(childStatus)) { + exitStatus = WEXITSTATUS(childStatus); + } + } + sigaction(SIGINT, &old_sa_int, NULL); + sigaction(SIGTERM, &old_sa_term, NULL); + if (hadInterrupt) { + kill(getpid(), SIGINT); + } + if (hadTerm) { + kill(getpid(), SIGTERM); + } + return exitStatus; + } else { + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(STDIN_FILENO); + dup2(pipes[0][0], STDIN_FILENO); + dup2(pipes[1][1], STDOUT_FILENO); + dup2(pipes[2][1], STDERR_FILENO); + close(pipes[0][0]); + close(pipes[0][1]); + close(pipes[1][1]); + close(pipes[1][0]); + close(pipes[2][1]); + close(pipes[2][0]); + + std::vector cmd_line; + for (auto& iCmdl : _fzncmd) { + cmd_line.push_back(strdup(iCmdl.c_str())); + } + + char** argv = new char*[cmd_line.size() + 1]; + for (unsigned int i = 0; i < cmd_line.size(); i++) argv[i] = cmd_line[i]; + argv[cmd_line.size()] = 0; + + int status = execvp(argv[0], argv); // execvp only returns if an error occurs. + assert(status == -1); // the returned value will always be -1 + std::stringstream ssm; + ssm << "Error occurred when executing FZN solver with command \""; + for (auto& s : cmd_line) ssm << s << ' '; + ssm << "\"."; + throw InternalError(ssm.str()); + } + } +#endif +}; + +#ifndef _WIN32 +template +bool Process::hadInterrupt; +template +bool Process::hadTerm; +#endif + +} // namespace MiniZinc +#endif diff --git a/include/minizinc/reader.hh b/include/minizinc/reader.hh new file mode 100644 index 0000000..5136cdd --- /dev/null +++ b/include/minizinc/reader.hh @@ -0,0 +1,86 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Graeme Gange + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_READER_HH__ +#define __MINIZINC_READER_HH__ + +#include +#include +#include + +#include +#include +#include +#include + +namespace MiniZinc { +// Class MznReader handles option handling and model parsing. +// Essentiially the first half of the MznSolver object. +class MznReader { +private: + /// Solver configurations + SolverConfigs solver_configs; + + std::string executable_name; + std::ostream& os; + std::ostream& log; + +public: + enum OptionStatus { OPTION_OK, OPTION_ERROR, OPTION_FINISH }; + // Search paths + std::vector filenames; + std::vector datafiles; + std::vector includePaths; + + std::string std_lib_dir; + std::string globals_dir; + + /// global options + bool flag_verbose = false; + bool flag_statistics = false; + /* + bool flag_compiler_verbose=false; + bool flag_compiler_statistics=false; + int flag_overall_time_limit=0; + */ + + bool flag_ignoreStdlib = false; + bool flag_typecheck = true; + bool flag_werror = false; + bool flag_only_range_domains = false; + bool flag_allow_unbounded_vars = false; + bool flag_stdinInput = false; + + // Timer starttime; + +public: + MznReader(std::ostream& os = std::cout, std::ostream& log = std::cerr); + ~MznReader(); + + Model* read(const std::string& modelString = std::string(), + const std::string& modelName = std::string("stdin")); + OptionStatus processOptions(std::vector& argv); + bool get_flag_verbose() { return flag_verbose; } + void printUsage(); + +private: + bool processOption(int& i, std::vector& argv); + + void printHelp(const std::string& selectedSolver = std::string()); + void printStatistics(); + + bool get_flag_statistics() { return flag_statistics; } +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solns2out.hh b/include/minizinc/solns2out.hh new file mode 100644 index 0000000..bf55fe6 --- /dev/null +++ b/include/minizinc/solns2out.hh @@ -0,0 +1,191 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_SOLNS2OUT_H__ +#define __MINIZINC_SOLNS2OUT_H__ + +#include +#include +#include +#include +#include // temp., TODO +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +/// Class handling fzn solver's output +/// could facilitate exhange of raw/final outputs in a portfolio +class Solns2Out { +protected: + std::unique_ptr pEnv_guard; + Env* pEnv = 0; + Model* pOutput = 0; + + typedef std::pair DE; + std::unordered_map declmap; + Expression* outputExpr = NULL; + std::string checkerModel; + bool fNewSol2Print = false; // should be set for evalOutput to work + +public: + std::string solution; + std::string comments; + int nLinesIgnore = 0; + + struct Options { + std::string flag_output_file; + bool flag_output_comments = true; + bool flag_output_flush = true; + bool flag_output_time = false; + int flag_ignore_lines = 0; + bool flag_unique = 1; + bool flag_canonicalize = 0; + bool flag_standaloneSolns2Out = false; + std::string flag_output_noncanonical; + std::string flag_output_raw; + int flag_number_output = -1; + /// Default values, also used for input + const char* const solution_separator_00 = "----------"; + const char* const unsatisfiable_msg_00 = "=====UNSATISFIABLE====="; + const char* const unbounded_msg_00 = "=====UNBOUNDED====="; + const char* const unsatorunbnd_msg_00 = "=====UNSATorUNBOUNDED====="; + const char* const unknown_msg_00 = "=====UNKNOWN====="; + const char* const error_msg_00 = "=====ERROR====="; + const char* const search_complete_msg_00 = "=========="; + /// Output values + std::string solution_separator = solution_separator_00; + std::string solution_comma = ""; + std::string unsatisfiable_msg = unsatisfiable_msg_00; + std::string unbounded_msg = unbounded_msg_00; + std::string unsatorunbnd_msg = unsatorunbnd_msg_00; + std::string unknown_msg = unknown_msg_00; + std::string error_msg = error_msg_00; + std::string search_complete_msg = search_complete_msg_00; + } _opt; + +public: + ~Solns2Out(); + Solns2Out(std::ostream& os, std::ostream& log, const std::string& stdlibDir); + + bool processOption(int& i, std::vector& argv); + void printHelp(std::ostream&); + + /// The output model (~.ozn) can be passed in 1 way in this base class: + /// passing Env* containing output() + bool initFromEnv(Env* pE); + + /// Then, variable assignments can be passed either as text + /// or put directly into envi()->output() ( latter done externally + /// by e.g. SolverInstance::assignSolutionToOutput() ) + /// In the 1st case, (part of) the assignment text is passed as follows, + /// original end-of-lines need to be there as well + bool feedRawDataChunk(const char*); + + SolverInstance::Status status = SolverInstance::UNKNOWN; + bool fStatusPrinted = false; + /// Should be called when entering new solution into the output model. + /// Default assignSolutionToOutput() does it by using findOutputVar(). + void declNewOutput(); + + /// This can be used by assignSolutionToOutput() + DE& findOutputVar(ASTString); + + /// In the other case, + /// the evaluation procedures print output/status to os + /// returning false means need to stop (error/ too many solutions) + /// Solution validation here TODO + /// Note that --canonicalize delays output + /// until ... exit, eof, ?? TODO + /// These functions should only be called explicitly + /// from SolverInstance + bool evalOutput(const std::string& s_ExtraInfo = ""); + /// This means the solver exits + bool evalStatus(SolverInstance::Status status); + + void printStatistics(std::ostream&); + + Env* getEnv() const { return pEnv; } + Model* getModel() const { + assert(getEnv()->output()); + return getEnv()->output(); + } + /// Get the primary output stream + /// First call restores stdout + std::ostream& getOutput(); + /// Get the secondary output stream + std::ostream& getLog(); + +private: + Timer starttime; + + std::unique_ptr pOut; // file output + std::unique_ptr pOfs_non_canon; + std::unique_ptr pOfs_raw; + int nSolns = 0; + std::set sSolsCanon; + std::string line_part; // non-finished line from last chunk + + /// Initialise from ozn file + void initFromOzn(const std::string& filename); + +protected: + std::ostream& os; + std::ostream& log; + std::vector includePaths; + std::string stdlibDir; + + // Basically open output + void init(); + void createOutputMap(); + std::map mapInputStatus; + void createInputMap(); + void restoreDefaults(); + /// Parsing fznsolver's complete raw text output + void parseAssignments(std::string&); + /// Checking solution against checker model + void checkSolution(std::ostream& os); + bool __evalOutput(std::ostream& os); + bool __evalOutputFinal(bool flag_flush); + bool __evalStatusMsg(SolverInstance::Status status); +}; + +// Passthrough Solns2Out class +class Solns2Log { +private: + std::ostream& _log; + std::ostream& _err_log; + +public: + Solns2Log(std::ostream& log, std::ostream& errLog) : _log(log), _err_log(errLog) {} + bool feedRawDataChunk(const char* data) { + _log << data << std::flush; + return true; + } + std::ostream& getLog(void) { return _err_log; } +}; + +} // namespace MiniZinc + +#endif // __MINIZINC_SOLNS2OUT_H__ diff --git a/include/minizinc/solver.hh b/include/minizinc/solver.hh new file mode 100644 index 0000000..015429f --- /dev/null +++ b/include/minizinc/solver.hh @@ -0,0 +1,202 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_SOLVER_HH__ +#define __MINIZINC_SOLVER_HH__ + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace MiniZinc { + +class SolverInitialiser { +public: + SolverInitialiser(void); +}; + +class SolverFactory; + +/// SolverRegistry is a storage for all SolverFactories in linked modules +class SolverRegistry { +public: + void addSolverFactory(SolverFactory*); + void removeSolverFactory(SolverFactory*); + typedef std::vector SFStorage; + const SFStorage& getSolverFactories() const { return sfstorage; } + +private: + SFStorage sfstorage; +}; // SolverRegistry + +/// this function returns the global SolverRegistry object +SolverRegistry* getGlobalSolverRegistry(); + +/// Representation of flags that can be passed to solvers +class MZNFZNSolverFlag { +public: + /// The type of the solver flag + enum FlagType { FT_ARG, FT_NOARG } t; + /// The name of the solver flag + std::string n; + +protected: + MZNFZNSolverFlag(const FlagType& t0, const std::string& n0) : t(t0), n(n0) {} + +public: + /// Create flag that has an argument + static MZNFZNSolverFlag arg(const std::string& n) { return MZNFZNSolverFlag(FT_ARG, n); } + /// Create flag that has no argument + static MZNFZNSolverFlag noarg(const std::string& n) { return MZNFZNSolverFlag(FT_NOARG, n); } + /// Create solver flag from standard flag + static MZNFZNSolverFlag std(const std::string& n); + /// Create solver flag from extra flag with name \a n and type \a t + static MZNFZNSolverFlag extra(const std::string& n, const std::string& t); +}; + +/// SolverFactory's descendants create, store and destroy SolverInstances +/// A SolverFactory stores all Instances itself and upon module exit, +/// destroys them and de-registers itself from the global SolverRegistry +/// An instance of SolverFactory's descendant can be created directly +/// or by one of the specialized createF_...() functions +class SolverFactory { +protected: + /// doCreateSI should be implemented to actually allocate a SolverInstance using new() + virtual SolverInstanceBase* doCreateSI(std::ostream&, SolverInstanceBase::Options* opt) = 0; + typedef std::vector> SIStorage; + SIStorage sistorage; + +protected: + SolverFactory() { getGlobalSolverRegistry()->addSolverFactory(this); } + +public: + virtual ~SolverFactory() { getGlobalSolverRegistry()->removeSolverFactory(this); } + +public: + /// Create solver-specific options object + virtual SolverInstanceBase::Options* createOptions(void) = 0; + /// Function createSI also adds each SI to the local storage + SolverInstanceBase* createSI(std::ostream& log, SolverInstanceBase::Options* opt); + /// also providing a manual destroy function. + /// there is no need to call it upon overall finish - that is taken care of + void destroySI(SolverInstanceBase* pSI); + +public: + /// Process an item in the command line. + /// Leaving this now like this because this seems simpler. + /// We can also pass options internally between modules in this way + /// and it only needs 1 format + virtual bool processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv) { + return false; + } + + virtual std::string getDescription(SolverInstanceBase::Options* opt = NULL) = 0; + virtual std::string getVersion(SolverInstanceBase::Options* opt = NULL) = 0; + virtual std::string getId(void) = 0; + virtual void printHelp(std::ostream&) {} +}; // SolverFactory + +// Class MznSolver coordinates flattening and solving. +class MznSolver { +private: + SolverInitialiser _solver_init; + enum OptionStatus { OPTION_OK, OPTION_ERROR, OPTION_FINISH }; + /// Solver configurations + SolverConfigs solver_configs; + Constraint* output = nullptr; + std::vector bs; + std::vector data_files; + Env in_out_defs; + SolverInstanceBase* si = 0; + SolverInstanceBase::Options* si_opt = 0; + SolverFactory* sf = 0; + bool is_mzn2fzn = 0; + + std::string executable_name; + std::string file; + std::string solver_str; + std::ostream& os; + std::ostream& log; + SolverInstance::Status interpreter_status = SolverInstance::UNKNOWN; + // Incremental interface + std::vector> def_stack = {{nullptr, 0}}; + double flatten_time = 0; + +public: + bool output_dict = false; + Interpreter* interpreter = nullptr; + Solns2Out s2out; + // name -> + std::unordered_map> resolve_call; + + /// global options + bool flag_verbose = false; + bool flag_statistics = false; + bool flag_compiler_verbose = false; + bool flag_compiler_statistics = false; + bool flag_is_solns2out = false; + int flag_overall_time_limit = 0; + +public: + MznSolver(std::vector args = {}); + ~MznSolver(); + + std::pair run(); + OptionStatus processOptions(std::vector& argv); + SolverFactory* getSF() { + assert(sf); + return sf; + } + SolverInstanceBase::Options* getSI_OPT() { + assert(si_opt); + return si_opt; + } + bool get_flag_verbose() { return flag_verbose; /*getFlt()->get_flag_verbose();*/ } + void printUsage(); + std::string printSolution(SolverInstance::Status); + + void pushToSolver(); + void popFromSolver(); + std::pair solve(); + +private: + void addDefinitions(); + Val eval_val(EnvI& env, Expression* e); + void printHelp(const std::string& selectedSolver = std::string()); + /// Flatten model + void flatten(const std::string& filename = std::string(), + const std::string& modelName = std::string("stdin")); + size_t getNSolvers() { return getGlobalSolverRegistry()->getSolverFactories().size(); } + /// If building a flattening exe only. + bool ifMzn2Fzn(); + bool ifSolns2out(); + void addSolverInterface(); + void addSolverInterface(SolverFactory* sf); + void printStatistics(); + + SolverInstance::Status getFltStatus() { return interpreter_status; } + SolverInstanceBase* getSI() { + assert(si); + return si; + } + bool get_flag_statistics() { return flag_statistics; } +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solver_config.hh b/include/minizinc/solver_config.hh new file mode 100644 index 0000000..634d95c --- /dev/null +++ b/include/minizinc/solver_config.hh @@ -0,0 +1,292 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_SOLVER_CONFIG_HH__ +#define __MINIZINC_SOLVER_CONFIG_HH__ + +#include + +#include +#include +#include + +namespace MiniZinc { + +/** + * \brief Configuration data for individual MiniZinc solvers + */ +class SolverConfig { +public: + /// Extra command line flags supported by solver + struct ExtraFlag { + std::string flag; + std::string description; + std::string flag_type; + std::string default_value; + ExtraFlag(const std::string& f, const std::string& d, const std::string& t = "bool", + const std::string& v = "false") + : flag(f), description(d), flag_type(t), default_value(v) {} + }; + +protected: + /// The configuration file for this solver (or empty string for built-in solvers) + std::string _configFile; + /// The unique identifier for the solver + std::string _id; + /// Name of the solver (used for output) + std::string _name; + /// The path to the executable + std::string _executable; + /// The path to the executable, after resolving + std::string _executable_resolved; + /// The path to the solver's MiniZinc library + std::string _mznlib; + /// The path to the solver's MiniZinc library, after resolving + std::string _mznlib_resolved; + /// Version string + std::string _version; + /// MiniZinc library version + int _mznlibVersion = 1; + /// Short description + std::string _description; + /// Contact email + std::string _contact; + /// URL for more information + std::string _website; + /// Whether solver supports MiniZinc input + bool _supportsMzn = false; + /// Whether solver supports FlatZinc input + bool _supportsFzn = true; + /// Whether solver supports NL input + bool _supportsNL = false; + /// Whether solver requires solutions2out processing + bool _needsSolns2Out = true; + /// Whether solver is a GUI application + bool _isGUIApplication = false; + /// Whether solver needs path to minizinc executable (passed as --minizinc-exe) + bool _needsMznExecutable = false; + /// Whether solver needs path to MiniZinc standard library (passed as --stdlib-dir) + bool _needsStdlibDir = false; + /// Whether solver needs path to symbol table (paths file) (passed as --paths) + bool _needsPathsFile = false; + /// Supported standard command line flags + std::vector _stdFlags; + /// Supported extra command line flags (flag and description) + std::vector _extraFlags; + /// Required command line flags + std::vector _requiredFlags; + /// Default command line flags (imported from global or user configuration) + std::vector _defaultFlags; + /// Tags + std::vector _tags; + +public: + /// Load solver configuration from \a filename + static SolverConfig load(std::string filename); + /// Default constructor + SolverConfig() {} + /// Constructor + SolverConfig(const std::string& id, const std::string& version) : _id(id), _version(version) {} + /// Return identifier + std::string id(void) const { return _id; } + /// Return version string + std::string version(void) const { return _version; } + + /// Return configuration file name + std::string configFile(void) const { return _configFile; } + /// Set configuration file name + void configFile(const std::string& s) { _configFile = s; } + + /// Return name + std::string name(void) const { return _name; } + // Set name + void name(const std::string& s) { _name = s; } + + /// Return executable path + std::string executable(void) const { return _executable; } + /// Set executable path + void executable(const std::string& s) { _executable = s; } + + /// Return resolved executable path + std::string executable_resolved(void) const { return _executable_resolved; } + + /// Return MiniZinc library path + std::string mznlib(void) const { return _mznlib; } + /// Set MiniZinc library path + void mznlib(const std::string& s) { _mznlib = s; } + + /// Return resolved MiniZinc library path + std::string mznlib_resolved(void) const { return _mznlib_resolved; } + + /// Return required MiniZinc library version + int mznlibVersion(void) const { return _mznlibVersion; } + /// Set required MiniZinc library version + void mznlibVersion(int i) { _mznlibVersion = i; } + + /// Whether solver supports MiniZinc input + bool supportsMzn(void) const { return _supportsMzn; } + /// Set whether solver supports MiniZinc input + void supportsMzn(bool b) { _supportsMzn = b; } + + /// Whether solver supports FlatZinc input + bool supportsFzn(void) const { return _supportsFzn; } + /// Set whether solver supports FlatZinc input + void supportsFzn(bool b) { _supportsFzn = b; } + + /// Whether solver supports NL input + bool supportsNL(void) const { return _supportsNL; } + /// Set whether solver supports NL input + void supportsNL(bool b) { _supportsNL = b; } + + /// Whether solver requires solutions2out processing + bool needsSolns2Out(void) const { return _needsSolns2Out; } + /// Set whether solver requires solutions2out processing + void needsSolns2Out(bool b) { _needsSolns2Out = b; } + + /// Whether solver is a GUI application + bool isGUIApplication(void) const { return _isGUIApplication; } + /// Set whether solver is a GUI application + void isGUIApplication(bool b) { _isGUIApplication = b; } + + /// Whether solver needs path to minizinc executable (passed as --minizinc-exe) + bool needsMznExecutable(void) const { return _needsMznExecutable; } + /// Set whether solver needs path to minizinc executable + void needsMznExecutable(bool b) { _needsMznExecutable = b; } + + /// Whether solver needs path to MiniZinc standard library (passed as --stdlib-dir) + bool needsStdlibDir(void) const { return _needsStdlibDir; } + /// Set whether solver needs path to MiniZinc standard library + void needsStdlibDir(bool b) { _needsStdlibDir = b; } + + /// Whether solver needs path to symbol table (paths file) (passed as --paths) + bool needsPathsFile(void) const { return _needsPathsFile; } + /// Set whether solver needs path to symbol table (paths file) + void needsPathsFile(bool b) { _needsPathsFile = b; } + + /// Return short description + std::string description(void) const { return _description; } + /// Set short description + void description(const std::string& s) { _description = s; } + + /// Return contact email + std::string contact(void) const { return _contact; } + /// Set contact email + void contact(const std::string& s) { _contact = s; } + + /// Return web site URL + std::string website(void) const { return _website; } + /// Set web site URL + void website(const std::string& s) { _website = s; } + + /// Return supported standard command line flags + const std::vector& stdFlags(void) const { return _stdFlags; } + /// Set supported standard command line flags + void stdFlags(const std::vector& f) { _stdFlags = f; } + + /// Return supported extra command line flags + const std::vector& extraFlags(void) const { return _extraFlags; } + /// Set supported extra command line flags + void extraFlags(const std::vector& f) { _extraFlags = f; } + + /// Return supported required command line flags + const std::vector& requiredFlags(void) const { return _requiredFlags; } + /// Set supported required command line flags + void requiredFlags(const std::vector& f) { _requiredFlags = f; } + + /// Return default command line flags + const std::vector& defaultFlags(void) const { return _defaultFlags; } + /// Set default command line flags + void defaultFlags(const std::vector& f) { _defaultFlags = f; } + + /// Return tags + const std::vector& tags(void) const { return _tags; } + /// Set tags + void tags(const std::vector& t) { _tags = t; } + + /// Test equality + bool operator==(const SolverConfig& sc) const { + return _id == sc.id() && _version == sc.version(); + } +}; + +/// A container for solver configurations +class SolverConfigs { +protected: + /// The solvers + std::vector _solvers; + typedef std::unordered_map > TagMap; + /// Mapping tags to vectors of solvers (indexed into _solvers) + TagMap _tags; + /// The default solver + std::string _defaultSolver; + /// The MiniZinc library directory + std::string _mznlibDir; + /// The solver configurations path + std::vector _solver_path; + typedef std::unordered_map DefaultMap; + /// Mapping from tag to default solver for that tag + DefaultMap _tagDefault; + typedef std::unordered_map > SolverDefaultMap; + /// Mapping from solver id to default options for that solver + SolverDefaultMap _solverDefaultOptions; + /// Add new solver configuration \a sc + void addConfig(const SolverConfig& sc); + +public: + /** \brief Constructor loading configurations from \a solverpath + * + * Configuration files must be called config.msc and the path + * uses platform specific separators (: on Unix-like systems, ; on Windows). + */ + SolverConfigs(std::ostream& log); + /// Return configuration for solver \a s + /// The string can be a comma separated list of tags, in which case a + /// solver that matches all tags will be returned. The tag can also be + /// a solver id. A concrete version can be requested using @. + /// Examples: + /// config("gecode@6.1.0") would request a gecode solver of version 6.1.0 + /// config("mip,internal") would request a MIP solver that uses the internal API + /// config("org.minizinc.mip.coin-bc@2.9/1.16 would request a specific version of OSICBC + const SolverConfig& config(const std::string& s); + /// Return list of all solver ids + std::vector solvers(void) const; + /// Return search path for solver configs + std::vector solverConfigsPath(void) const; + /// Return JSON list of all solver configurations + std::string solverConfigsJSON(void) const; + /// Add a built-in solver + static void registerBuiltinSolver(const SolverConfig& sc); + + /// Default solver + const std::string& defaultSolver(void) const { return _defaultSolver; } + /// Default solver for tag \a t + const std::string& defaultSolver(const std::string& t) { + static std::string noDefault; + auto it = _tagDefault.find(t); + return it == _tagDefault.end() ? noDefault : it->second; + } + /// MiniZinc library directory + const std::string& mznlibDir(void) const { return _mznlibDir; } +}; + +/// An exception thrown when encountering an error in a solver configuration +class ConfigException : public Exception { +public: + /// Construct with message \a msg + ConfigException(const std::string& msg) : Exception(msg) {} + /// Destructor + ~ConfigException(void) throw() {} + /// Return description + virtual const char* what(void) const throw() { return "MiniZinc: configuration error"; } +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solver_instance.hh b/include/minizinc/solver_instance.hh new file mode 100644 index 0000000..8a24ca6 --- /dev/null +++ b/include/minizinc/solver_instance.hh @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_SOLVER_INSTANCE_HH__ +#define __MINIZINC_SOLVER_INSTANCE_HH__ + +namespace MiniZinc { + +class SolverInstanceBase; + +#ifdef ERROR // MICROsoft. +#undef ERROR +#endif +class SolverInstance { +protected: + SolverInstanceBase* _si; + +public: + enum Status { + OPT, // For SAT problems this means "search complete" + SAT, + UNSAT, + UNBND, + UNSATorUNBND, + UNKNOWN, + ERROR, + NONE + }; + enum StatusReason { SR_OK = -5, SR_TIME, SR_MEMORY, SR_LIMIT, SR_ERROR }; +}; + +const SolverInstance::Status SolverInstance__ERROR = SolverInstance::ERROR; // just in case... + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solver_instance_base.hh b/include/minizinc/solver_instance_base.hh new file mode 100644 index 0000000..a57832f --- /dev/null +++ b/include/minizinc/solver_instance_base.hh @@ -0,0 +1,171 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_SOLVER_INSTANCE_BASE_HH__ +#define __MINIZINC_SOLVER_INSTANCE_BASE_HH__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace MiniZinc { + +/// An abstract SI +class SolverInstanceBase { +protected: + Env* _env = nullptr; + Solns2Out* pS2Out = 0; + std::ostream& _log; + +public: + /// Options base class + /// A sub-class will be provided by each concrete SolverInstance + class Options { + public: + bool verbose = false; + bool printStatistics = false; + }; + + std::unique_ptr _options; + + typedef SolverInstance::Status Status; + typedef SolverInstance::StatusReason StatusReason; + Status _status; + StatusReason _status_reason; + + SolverInstanceBase(std::ostream& log, Options* options) + : _log(log), + _options(options), + _status(SolverInstance::UNKNOWN), + _status_reason(SolverInstance::SR_OK) {} + virtual ~SolverInstanceBase() {} + + /// Set/get the environment: + virtual Env* getEnv() const { + assert(_env); + return _env; + } + virtual Env& env(void) const { return *getEnv(); } + + Solns2Out* getSolns2Out() const { + assert(pS2Out); + return pS2Out; + } + void setSolns2Out(Solns2Out* s2o) { pS2Out = s2o; } + + virtual void printSolution(); + // virtual void printSolution(ostream& ); // deprecated + /// print statistics in form of comments + virtual void printStatistics(bool fLegend = 0) {} + virtual void printStatisticsLine(bool fLegend = 0) {} + + /// find the next solution + virtual Status next(void) = 0; + /// generate the solver-instance-representation from the flatzinc model + virtual void processFlatZinc(void) = 0; + /// clean up input model & flatzinc + void cleanupForNonincrementalSolving() { getEnv()->envi().cleanupExceptOutput(); } + /// solve the problem instance (according to the solve specification in the flatzinc model) + virtual Status solve(void); + /// return reason for status given by solve + virtual StatusReason reason(void) { return _status_reason; } + virtual Status status(void) { return _status; } + + virtual void addFunction(FunctionI* fi){}; + virtual void addConstraint(const std::vector& bs, Constraint* c) = 0; + virtual void addVariable(Variable* var, bool isOutput) = 0; + virtual Val getSolutionValue(Variable* var) = 0; + + /// reset the model to its core (removing temporary cts) and the solver to the root node of the + /// search + void reset(void); + /// reset the solver to the root node of the search TODO: difference between reset() and + /// resetSolver()? + virtual void resetSolver(void) = 0; + /// reset the solver and add temporary constraints given by the iterator + virtual void resetWithConstraints(Model::iterator begin, Model::iterator end); + /// add permanent constraints given by the iterator to the solver instance + virtual void processPermanentConstraints(Model::iterator begin, Model::iterator end); + +protected: + /// flatten the search annotations, pushing them into the vector \a out + void flattenSearchAnnotations(const Annotation& ann, std::vector& out); + +private: + SolverInstanceBase(const SolverInstanceBase&); + SolverInstanceBase& operator=(const SolverInstanceBase&); +}; + +/// This implements a solver which is linked and returns its solution by assignSolutionToOutput() +class SolverInstanceBase2 : public SolverInstanceBase { +protected: + virtual Expression* getSolutionValue(Id* id) = 0; + +public: + /// Assign output for all vars: need public for callbacks + // Default impl requires a Solns2Out object set up + virtual void assignSolutionToOutput(); + /// Print solution to setup dest + virtual void printSolution(); + +protected: + std::vector + _varsWithOutput; // this is to extract fzn vars. Identical to output()? TODO + +public: + SolverInstanceBase2(std::ostream& log, SolverInstanceBase::Options* opt) + : SolverInstanceBase(log, opt) {} +}; + +typedef void (*poster)(SolverInstanceBase&, const Constraint* call); +class Registry { +protected: + std::unordered_map _registry; + SolverInstanceBase& _base; + +public: + Registry(SolverInstanceBase& base) : _base(base) {} + void add(const std::string& name, poster p); + void post(std::string name, Constraint* c); + void cleanup() { _registry.clear(); } +}; + +/// Finally, this class also stores a mapping VarDecl->SolverVar and a constraint transformer +/// It is a template holding parameterized VarId and Statistics, so cannot have members defined in a +/// .cpp +template +class SolverInstanceImpl : public SolverInstanceBase2 { +public: + typename Solver::Statistics _statistics; + virtual Statistics& getStatistics() { return _statistics; } + + typedef typename Solver::Variable VarId; + +protected: + // IdMap _variableMap; // this to find solver's variables given an Id + std::vector> _variableMap; + Registry _constraintRegistry; + +public: + SolverInstanceImpl(std::ostream& log, SolverInstanceBase::Options* opt) + : SolverInstanceBase2(log, opt), _variableMap(1), _constraintRegistry(*this) {} +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/MIP/MIP_cplex_solverfactory.hh b/include/minizinc/solvers/MIP/MIP_cplex_solverfactory.hh new file mode 100644 index 0000000..872cd6d --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_cplex_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MIP_CPLEX_SOLVERFACTORY_HH__ +#define __MINIZINC_MIP_CPLEX_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class Cplex_SolverFactoryInitialiser { +public: + Cplex_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/MIP/MIP_cplex_wrap.hh b/include/minizinc/solvers/MIP/MIP_cplex_wrap.hh new file mode 100644 index 0000000..e242f6c --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_cplex_wrap.hh @@ -0,0 +1,212 @@ + +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MIP_CPLEX_WRAPPER_H__ +#define __MIP_CPLEX_WRAPPER_H__ + +#include +#include +#include + +#include // add -DCPLEX_STUDIO_DIR=/opt/ibm/ILOG/CPLEX_Studio1261 to the 1st call of cmake + +class MIP_cplex_wrapper : public MIP_wrapper { + CPXENVptr env = 0; + CPXLPptr lp = 0; + + int status; + char cplex_buffer[CPXMESSAGEBUFSIZE]; + char cplex_status_buffer[CPXMESSAGEBUFSIZE]; + + std::vector x; + +#ifdef CPLEX_PLUGIN + void* _cplex_dll; +#endif + +public: + class Options : public MiniZinc::SolverInstanceBase::Options { + public: + int nMIPFocus = 0; + int nThreads = 1; + std::string sExportModel; + int nTimeout = -1; + long int nSolLimit = -1; + double nWorkMemLimit = -1; + std::string sReadParams; + std::string sWriteParams; + bool flag_all_solutions = false; + + double absGap = -1; + double relGap = 1e-8; + double intTol = 1e-8; + double objDiff = 1.0; + std::string sCPLEXDLL; + bool processOption(int& i, std::vector& argv); + static void printHelp(std::ostream&); + }; + +private: + Options* options = nullptr; + +public: + MIP_cplex_wrapper(Options* opt) : options(opt) { openCPLEX(); } + virtual ~MIP_cplex_wrapper() { closeCPLEX(); } + + static std::string getDescription(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getVersion(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getId(void); + static std::string getName(void); + static std::vector getTags(void); + static std::vector getStdFlags(void); + static std::string needDllFlag(void); + + // Statistics& getStatistics() { return _statistics; } + + // IloConstraintArray *userCuts, *lazyConstraints; + + /// derived should overload and call the ancestor + // virtual void cleanup(); + void checkDLL(); + void openCPLEX(); + void closeCPLEX(); + + /// actual adding new variables to the solver + virtual void doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, + std::string* names); + + /// adding a linear constraint + virtual void addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask = MaskConsType_Normal, std::string rowName = ""); + virtual void setVarBounds(int iVar, double lb, double ub); + virtual void setVarLB(int iVar, double lb); + virtual void setVarUB(int iVar, double ub); + /// Indicator constraint: x[iBVar]==bVal -> lin constr + virtual void addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, double* rmatval, + LinConType sense, double rhs, std::string rowName = ""); + virtual bool addWarmStart(const std::vector& vars, const std::vector vals); + /// adding an implication + // virtual void addImpl() = 0; + virtual void setObjSense(int s); // +/-1 for max/min + + virtual double getInfBound() { return CPX_INFBOUND; } + + virtual int getNCols() { return dll_CPXgetnumcols(env, lp); } + virtual int getNRows() { return dll_CPXgetnumrows(env, lp); } + + // void setObjUB(double ub) { objUB = ub; } + // void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0 + + virtual void solve(); + + /// OUTPUT: + virtual const double* getValues() { return output.x; } + virtual double getObjValue() { return output.objVal; } + virtual double getBestBound() { return output.bestBound; } + virtual double getCPUTime() { return output.dCPUTime; } + + virtual Status getStatus() { return output.status; } + virtual std::string getStatusName() { return output.statusName; } + + virtual int getNNodes() { return output.nNodes; } + virtual int getNOpen() { return output.nOpenNodes; } + + // virtual int getNNodes() = 0; + // virtual double getTime() = 0; + + // CPLEX API + + int (*dll_CPXaddfuncdest)(CPXCENVptr, CPXCHANNELptr, void*, + void (*msgfunction)(void*, const char*)); + int (*dll_CPXaddindconstr)(CPXCENVptr, CPXLPptr, int, int, int, double, int, int const*, + double const*, char const*); + int (*dll_CPXaddlazyconstraints)(CPXCENVptr env, CPXLPptr lp, int rcnt, int nzcnt, + double const* rhs, char const* sense, int const* rmatbeg, + int const* rmatind, double const* rmatval, char** rowname); + int (*dll_CPXaddmipstarts)(CPXCENVptr env, CPXLPptr lp, int mcnt, int nzcnt, int const* beg, + int const* varindices, double const* values, int const* effortlevel, + char** mipstartname); + int (*dll_CPXaddrows)(CPXCENVptr env, CPXLPptr lp, int ccnt, int rcnt, int nzcnt, + double const* rhs, char const* sense, int const* rmatbeg, + int const* rmatind, double const* rmatval, char** colname, char** rowname); + int (*dll_CPXaddusercuts)(CPXCENVptr env, CPXLPptr lp, int rcnt, int nzcnt, double const* rhs, + char const* sense, int const* rmatbeg, int const* rmatind, + double const* rmatval, char** rowname); + int (*dll_CPXchgbds)(CPXCENVptr env, CPXLPptr lp, int cnt, int const* indices, char const* lu, + double const* bd); + int (*dll_CPXchgmipstarts)(CPXCENVptr env, CPXLPptr lp, int mcnt, int const* mipstartindices, + int nzcnt, int const* beg, int const* varindices, double const* values, + int const* effortlevel); + int (*dll_CPXchgobjsen)(CPXCENVptr env, CPXLPptr lp, int maxormin); + + int (*dll_CPXcloseCPLEX)(CPXENVptr* env_p); + + CPXLPptr (*dll_CPXcreateprob)(CPXCENVptr env, int* status_p, char const* probname_str); + + int (*dll_CPXcutcallbackadd)(CPXCENVptr env, void* cbdata, int wherefrom, int nzcnt, double rhs, + int sense, int const* cutind, double const* cutval, int purgeable); + + int (*dll_CPXfreeprob)(CPXCENVptr env, CPXLPptr* lp_p); + int (*dll_CPXgetbestobjval)(CPXCENVptr env, CPXCLPptr lp, double* objval_p); + int (*dll_CPXgetcallbackincumbent)(CPXCENVptr env, void* cbdata, int wherefrom, double* x, + int begin, int end); + int (*dll_CPXgetcallbackinfo)(CPXCENVptr env, void* cbdata, int wherefrom, int whichinfo, + void* result_p); + int (*dll_CPXgetcallbacknodeinfo)(CPXCENVptr env, void* cbdata, int wherefrom, int nodeindex, + int whichinfo, void* result_p); + int (*dll_CPXgetcallbacknodex)(CPXCENVptr env, void* cbdata, int wherefrom, double* x, int begin, + int end); + int (*dll_CPXgetchannels)(CPXCENVptr env, CPXCHANNELptr* cpxresults_p, + CPXCHANNELptr* cpxwarning_p, CPXCHANNELptr* cpxerror_p, + CPXCHANNELptr* cpxlog_p); + int (*dll_CPXgetdettime)(CPXCENVptr env, double* dettimestamp_p); + CPXCCHARptr (*dll_CPXgeterrorstring)(CPXCENVptr env, int errcode, char* buffer_str); + int (*dll_CPXgetmipstartindex)(CPXCENVptr env, CPXCLPptr lp, char const* lname_str, int* index_p); + int (*dll_CPXgetnodecnt)(CPXCENVptr env, CPXCLPptr lp); + int (*dll_CPXgetnodeleftcnt)(CPXCENVptr env, CPXCLPptr lp); + int (*dll_CPXgetnumcols)(CPXCENVptr env, CPXCLPptr lp); + int (*dll_CPXgetnumrows)(CPXCENVptr env, CPXCLPptr lp); + int (*dll_CPXgetobjsen)(CPXCENVptr env, CPXCLPptr lp); + int (*dll_CPXgetobjval)(CPXCENVptr env, CPXCLPptr lp, double* objval_p); + int (*dll_CPXgetsolnpoolnumsolns)(CPXCENVptr env, CPXCLPptr lp); + int (*dll_CPXgetstat)(CPXCENVptr env, CPXCLPptr lp); + CPXCHARptr (*dll_CPXgetstatstring)(CPXCENVptr env, int statind, char* buffer_str); + int (*dll_CPXgettime)(CPXCENVptr env, double* timestamp_p); + int (*dll_CPXgetx)(CPXCENVptr env, CPXCLPptr lp, double* x, int begin, int end); + int (*dll_CPXmipopt)(CPXCENVptr env, CPXLPptr lp); + int (*dll_CPXnewcols)(CPXCENVptr env, CPXLPptr lp, int ccnt, double const* obj, double const* lb, + double const* ub, char const* xctype, char** colname); + CPXENVptr (*dll_CPXopenCPLEX)(int* status_p); + int (*dll_CPXreadcopyparam)(CPXENVptr env, char const* filename_str); + int (*dll_CPXsetdblparam)(CPXENVptr env, int whichparam, double newvalue); + int (*dll_CPXsetinfocallbackfunc)(CPXENVptr env, int (*callback)(CPXCENVptr, void*, int, void*), + void* cbhandle); + int (*dll_CPXsetintparam)(CPXENVptr env, int whichparam, CPXINT newvalue); + int (*dll_CPXsetlazyconstraintcallbackfunc)(CPXENVptr env, + int (*lazyconcallback)(CALLBACK_CUT_ARGS), + void* cbhandle); + int (*dll_CPXsetusercutcallbackfunc)(CPXENVptr env, int (*cutcallback)(CALLBACK_CUT_ARGS), + void* cbhandle); + CPXCCHARptr (*dll_CPXversion)(CPXCENVptr env); + int (*dll_CPXwriteparam)(CPXCENVptr env, char const* filename_str); + + int (*dll_CPXwriteprob)(CPXCENVptr env, CPXCLPptr lp, char const* filename_str, + char const* filetype_str); + +protected: + void wrap_assert(bool, std::string, bool fTerm = true); + + /// Need to consider the 100 status codes in CPLEX and change with every version? TODO + Status convertStatus(int cplexStatus); +}; + +#endif // __MIP_CPLEX_WRAPPER_H__ diff --git a/include/minizinc/solvers/MIP/MIP_gurobi_solverfactory.hh b/include/minizinc/solvers/MIP/MIP_gurobi_solverfactory.hh new file mode 100644 index 0000000..15eed3a --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_gurobi_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MIP_GUROBI_SOLVERFACTORY_HH__ +#define __MINIZINC_MIP_GUROBI_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class Gurobi_SolverFactoryInitialiser { +public: + Gurobi_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/MIP/MIP_gurobi_wrap.hh b/include/minizinc/solvers/MIP/MIP_gurobi_wrap.hh new file mode 100644 index 0000000..be126f8 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_gurobi_wrap.hh @@ -0,0 +1,228 @@ + +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MIP_GUROBI_WRAPPER_H__ +#define __MIP_GUROBI_WRAPPER_H__ + +#include +#include + +extern "C" { +#include // need GUROBI_HOME defined +} + +class MIP_gurobi_wrapper : public MIP_wrapper { + GRBenv* env = 0; + GRBmodel* model = 0; +#ifdef GUROBI_PLUGIN + void* gurobi_dll; +#endif + int error; + std::string gurobi_buffer; // [GRB_MESSAGEBUFSIZE]; + std::string gurobi_status_buffer; // [GRB_MESSAGEBUFSIZE]; + + std::vector x; + +public: + class Options : public MiniZinc::SolverInstanceBase::Options { + public: + int nMIPFocus = 0; + int nFreeSearch = 1; + int nThreads = 1; + std::string sExportModel; + int nTimeout1000 = -1; + int nTimeoutFeas1000 = -1; + long int nSolLimit = -1; + double nWorkMemLimit = -1; + std::string sReadParams; + std::string sWriteParams; + bool flag_all_solutions = false; + + double absGap = -1; + double relGap = 1e-8; + double feasTol = 1e-8; + double intTol = 1e-8; + double objDiff = 1.0; + std::string sGurobiDLL; + bool processOption(int& i, std::vector& argv); + static void printHelp(std::ostream&); + }; + +private: + Options* options = nullptr; + +public: + void(__stdcall* dll_GRBversion)(int*, int*, int*); + + int(__stdcall* dll_GRBaddconstr)(GRBmodel* model, int numnz, int* cind, double* cval, char sense, + double rhs, const char* constrname); + + int(__stdcall* dll_GRBaddgenconstrIndicator)(GRBmodel* model, const char* name, int binvar, + int binval, int nvars, const int* ind, + const double* val, char sense, double rhs); + + int(__stdcall* dll_GRBaddvars)(GRBmodel* model, int numvars, int numnz, int* vbeg, int* vind, + double* vval, double* obj, double* lb, double* ub, char* vtype, + char** varnames); + + int(__stdcall* dll_GRBcbcut)(void* cbdata, int cutlen, const int* cutind, const double* cutval, + char cutsense, double cutrhs); + + int(__stdcall* dll_GRBcbget)(void* cbdata, int where, int what, void* resultP); + + int(__stdcall* dll_GRBcblazy)(void* cbdata, int lazylen, const int* lazyind, + const double* lazyval, char lazysense, double lazyrhs); + + void(__stdcall* dll_GRBfreeenv)(GRBenv* env); + + int(__stdcall* dll_GRBfreemodel)(GRBmodel* model); + + int(__stdcall* dll_GRBgetdblattr)(GRBmodel* model, const char* attrname, double* valueP); + + int(__stdcall* dll_GRBgetdblattrarray)(GRBmodel* model, const char* attrname, int first, int len, + double* values); + + GRBenv*(__stdcall* dll_GRBgetenv)(GRBmodel* model); + + const char*(__stdcall* dll_GRBgeterrormsg)(GRBenv* env); + + int(__stdcall* dll_GRBgetintattr)(GRBmodel* model, const char* attrname, int* valueP); + + int(__stdcall* dll_GRBloadenv)(GRBenv** envP, const char* logfilename); + + int(__stdcall* dll_GRBnewmodel)(GRBenv* env, GRBmodel** modelP, const char* Pname, int numvars, + double* obj, double* lb, double* ub, char* vtype, + char** varnames); + + int(__stdcall* dll_GRBoptimize)(GRBmodel* model); + + int(__stdcall* dll_GRBreadparams)(GRBenv* env, const char* filename); + + int(__stdcall* dll_GRBsetcallbackfunc)(GRBmodel* model, int(__stdcall* cb)(CB_ARGS), + void* usrdata); + + int(__stdcall* dll_GRBsetdblparam)(GRBenv* env, const char* paramname, double value); + + int(__stdcall* dll_GRBsetintparam)(GRBenv* env, const char* paramname, int value); + + int(__stdcall* dll_GRBsetintattr)(GRBmodel* model, const char* attrname, int newvalue); + + int(__stdcall* dll_GRBsetdblattrelement)(GRBmodel* model, const char* attrname, int iv, double v); + + int(__stdcall* dll_GRBsetintattrlist)(GRBmodel* model, const char* attrname, int len, int* ind, + int* newvalues); + + int(__stdcall* dll_GRBsetdblattrlist)(GRBmodel* model, const char* attrname, int len, int* ind, + double* newvalues); + + int(__stdcall* dll_GRBsetstrparam)(GRBenv* env, const char* paramname, const char* value); + + void(__stdcall* dll_GRBterminate)(GRBmodel* model); + + int(__stdcall* dll_GRBupdatemodel)(GRBmodel* model); + + int(__stdcall* dll_GRBwrite)(GRBmodel* model, const char* filename); + + int(__stdcall* dll_GRBwriteparams)(GRBenv* env, const char* filename); + + int(__stdcall* dll_GRBgetintparam)(GRBenv* env, const char* paramname, int* valueP); + +public: + MIP_gurobi_wrapper(Options* opt) : options(opt) { + if (opt) openGUROBI(); + } + virtual ~MIP_gurobi_wrapper() { closeGUROBI(); } + + static std::string getDescription(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getVersion(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getId(void); + static std::string getName(void); + static std::vector getTags(void); + static std::vector getStdFlags(void); + static std::string needDllFlag(void); + // Statistics& getStatistics() { return _statistics; } + + // IloConstraintArray *userCuts, *lazyConstraints; + + /// derived should overload and call the ancestor + // virtual void cleanup(); + + void checkDLL(); + void openGUROBI(); + void closeGUROBI(); + + /// actual adding new variables to the solver + virtual void doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, + std::string* names); + + /// adding a linear constraint + virtual void addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask = MaskConsType_Normal, std::string rowName = ""); + virtual void setVarBounds(int iVar, double lb, double ub); + virtual void setVarLB(int iVar, double lb); + virtual void setVarUB(int iVar, double ub); + /// Indicator constraint: x[iBVar]==bVal -> lin constr + virtual void addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, double* rmatval, + LinConType sense, double rhs, std::string rowName = ""); + virtual int getFreeSearch(); + virtual bool addSearch(const std::vector& vars, const std::vector pri); + virtual bool addWarmStart(const std::vector& vars, const std::vector vals); + int nRows = 0; // to count rows in order tp notice lazy constraints + std::vector nLazyIdx; + std::vector nLazyValue; + /// adding an implication + // virtual void addImpl() = 0; + virtual void setObjSense(int s); // +/-1 for max/min + + virtual double getInfBound() { return GRB_INFINITY; } + + virtual int getNCols() { + dll_GRBupdatemodel(model); + int cols; + error = dll_GRBgetintattr(model, GRB_INT_ATTR_NUMVARS, &cols); + return cols; + } + virtual int getNRows() { + dll_GRBupdatemodel(model); + int cols; + error = dll_GRBgetintattr(model, GRB_INT_ATTR_NUMCONSTRS, &cols); + return cols; + } + + // void setObjUB(double ub) { objUB = ub; } + // void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0 + + virtual void solve(); + + /// OUTPUT: + virtual const double* getValues() { return output.x; } + virtual double getObjValue() { return output.objVal; } + virtual double getBestBound() { return output.bestBound; } + virtual double getCPUTime() { return output.dCPUTime; } + + virtual Status getStatus() { return output.status; } + virtual std::string getStatusName() { return output.statusName; } + + virtual int getNNodes() { return output.nNodes; } + virtual int getNOpen() { return output.nOpenNodes; } + + // virtual int getNNodes() = 0; + // virtual double getTime() = 0; + +protected: + void wrap_assert(bool, std::string, bool fTerm = true); + + /// Need to consider the 100 status codes in GUROBI and change with every version? TODO + Status convertStatus(int gurobiStatus); +}; + +#endif // __MIP_GUROBI_WRAPPER_H__ diff --git a/include/minizinc/solvers/MIP/MIP_osicbc_solverfactory.hh b/include/minizinc/solvers/MIP/MIP_osicbc_solverfactory.hh new file mode 100644 index 0000000..187fa02 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_osicbc_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MIP_OSICBC_SOLVERFACTORY_HH__ +#define __MINIZINC_MIP_OSICBC_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class OSICBC_SolverFactoryInitialiser { +public: + OSICBC_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/MIP/MIP_osicbc_wrap.hh b/include/minizinc/solvers/MIP/MIP_osicbc_wrap.hh new file mode 100644 index 0000000..c37e562 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_osicbc_wrap.hh @@ -0,0 +1,157 @@ + +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MIP_OSICBC_WRAPPER_H__ +#define __MIP_OSICBC_WRAPPER_H__ + +#include +#include +// CMakeLists.txt needs OSICBC_HOME defined +// #include +// #include +// #include +// #include +// #include +// #include +#include +#include +// #include + +class MIP_osicbc_wrapper : public MIP_wrapper { + // OsiCbcSolverInterface osi; // deprecated in Cbc 2.9.6 + OsiClpSolverInterface osi; + // CoinPackedMatrix* matrix = 0; + int error; + std::string osicbc_buffer; // [CBC_MESSAGEBUFSIZE]; + // string osicbc_status_buffer; // [CBC_MESSAGEBUFSIZE]; + + std::vector x; + + // To add constraints: + // vector rowStarts, columns; + std::vector rows; + std::vector // element, + rowlb, rowub; + + std::unordered_map warmstart; // this accumulates warmstart infos + +public: + class Options : public MiniZinc::SolverInstanceBase::Options { + public: + int nThreads = 1; + std::string sExportModel; + int nTimeout = 0; + long int nSolLimit = -1; + double nWorkMemLimit = -1; + std::string sReadParams; + std::string sWriteParams; + bool flag_all_solutions = false; + + double absGap = -1; + double relGap = 1e-8; + double intTol = 1e-8; + double objDiff = 1.0; + + std::string cbc_cmdOptions; + + bool processOption(int& i, std::vector& argv); + static void printHelp(std::ostream&); + }; + +private: + Options* options = nullptr; + +public: + MIP_osicbc_wrapper(Options* opt) : options(opt) { openOSICBC(); } + virtual ~MIP_osicbc_wrapper() { closeOSICBC(); } + + static std::string getDescription(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getVersion(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getId(void); + static std::string getName(void); + static std::vector getTags(void); + static std::vector getStdFlags(void); + static std::string needDllFlag(void) { return ""; } + + void printVersion(std::ostream&); + void printHelp(std::ostream&); + // Statistics& getStatistics() { return _statistics; } + + // IloConstraintArray *userCuts, *lazyConstraints; + + /// derived should overload and call the ancestor + // virtual void cleanup(); + void openOSICBC() {} + void closeOSICBC() {} + + /// actual adding new variables to the solver + virtual void doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, + std::string* names); + + void addPhase1Vars() { + if (fVerbose) + std::cerr << " MIP_osicbc_wrapper: delaying physical addition of variables..." << std::endl; + } + + /// adding a linear constraint + virtual void addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask = MaskConsType_Normal, std::string rowName = ""); + /// adding an implication + // virtual void addImpl() = 0; + + virtual bool addWarmStart(const std::vector& vars, const std::vector vals); + + virtual void setObjSense(int s); // +/-1 for max/min + + virtual double getInfBound() { return osi.getInfinity(); } + + virtual int getNCols() { + int nc = osi.getNumCols(); + return nc ? nc : colLB.size(); + } + virtual int getNColsModel() { return osi.getNumCols(); } + virtual int getNRows() { + if (rowlb.size()) return rowlb.size(); + return osi.getNumRows(); + } + + // void setObjUB(double ub) { objUB = ub; } + // void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0 + + virtual void solve(); + + /// OUTPUT: + virtual const double* getValues() { return output.x; } + virtual double getObjValue() { return output.objVal; } + virtual double getBestBound() { return output.bestBound; } + virtual double getCPUTime() { return output.dCPUTime; } + + virtual Status getStatus() { return output.status; } + virtual std::string getStatusName() { return output.statusName; } + + virtual int getNNodes() { return output.nNodes; } + virtual int getNOpen() { return output.nOpenNodes; } + + // virtual int getNNodes() = 0; + // virtual double getTime() = 0; + +protected: + // OsiSolverInterface& getOsiSolver(void) { return osi; } + + void wrap_assert(bool, std::string, bool fTerm = true); + + /// Need to consider the 100 status codes in OSICBC and change with every version? TODO + Status convertStatus(CbcModel* pModel); + Status convertStatus(); +}; + +#endif // __MIP_OSICBC_WRAPPER_H__ diff --git a/include/minizinc/solvers/MIP/MIP_scip_solverfactory.hh b/include/minizinc/solvers/MIP/MIP_scip_solverfactory.hh new file mode 100644 index 0000000..75677b0 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_scip_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MIP_SCIP_SOLVERFACTORY_HH__ +#define __MINIZINC_MIP_SCIP_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class SCIP_SolverFactoryInitialiser { +public: + SCIP_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/MIP/MIP_scip_wrap.hh b/include/minizinc/solvers/MIP/MIP_scip_wrap.hh new file mode 100644 index 0000000..d5d0816 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_scip_wrap.hh @@ -0,0 +1,156 @@ + +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MIP_SCIP_WRAPPER_H__ +#define __MIP_SCIP_WRAPPER_H__ + +#include +#include + +#include +#include + +class MIP_scip_wrapper : public MIP_wrapper { + SCIP* scip = 0; + // SCIP_Retcode retcode = SCIP_OKAY; + // char scip_buffer[SCIP_MESSAGEBUFSIZE]; + // char scip_status_buffer[SCIP_MESSAGEBUFSIZE]; + + std::vector scipVars; + virtual SCIP_RETCODE delSCIPVars(); + + std::vector x; + +public: + class Options : public MiniZinc::SolverInstanceBase::Options { + public: + int nThreads = 1; + std::string sExportModel; + int nTimeout = -1; + double nWorkMemLimit = -1; + std::string sReadParams; + std::string sWriteParams; + bool flag_all_solutions = false; + + double absGap = -1; + double relGap = 1e-8; + double intTol = 1e-8; + double objDiff = 1.0; + + bool processOption(int& i, std::vector& argv); + static void printHelp(std::ostream&); + }; + +private: + Options* options = nullptr; + +public: + MIP_scip_wrapper(Options* opt) : options(opt) { wrap_assert(openSCIP()); } + virtual ~MIP_scip_wrapper() { + wrap_assert(delSCIPVars()); + wrap_assert(closeSCIP()); + } + + static std::string getDescription(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getVersion(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getId(void); + static std::string getName(void); + static std::vector getTags(void); + static std::vector getStdFlags(void); + static std::string needDllFlag(void); + + bool processOption(int& i, int argc, const char** argv); + void printVersion(std::ostream&); + void printHelp(std::ostream&); + // Statistics& getStatistics() { return _statistics; } + + // IloConstraintArray *userCuts, *lazyConstraints; + + /// derived should overload and call the ancestor + // virtual void cleanup(); + SCIP_RETCODE openSCIP(); + SCIP_RETCODE closeSCIP(); + + /// actual adding new variables to the solver + virtual void doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, + std::string* names) { + wrap_assert(doAddVars_SCIP(n, obj, lb, ub, vt, names)); + } + virtual SCIP_RETCODE doAddVars_SCIP(size_t n, double* obj, double* lb, double* ub, VarType* vt, + std::string* names); + virtual void setVarBounds(int iVar, double lb, double ub); + virtual void setVarLB(int iVar, double lb); + virtual void setVarUB(int iVar, double ub); + + /// adding a linear constraint + virtual void addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask = MaskConsType_Normal, std::string rowName = "") { + wrap_assert(addRow_SCIP(nnz, rmatind, rmatval, sense, rhs, mask, rowName)); + } + virtual SCIP_RETCODE addRow_SCIP(int nnz, int* rmatind, double* rmatval, LinConType sense, + double rhs, int mask = MaskConsType_Normal, + std::string rowName = ""); + /// adding an implication + // virtual void addImpl() = 0; + /// Indicator constraint: x[iBVar]==bVal -> lin constr + virtual void addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, double* rmatval, + LinConType sense, double rhs, std::string rowName = ""); + /// Bounds disj for SCIP + virtual void addBoundsDisj(int n, double* fUB, double* bnd, int* vars, int nF, double* fUBF, + double* bndF, int* varsF, std::string rowName = ""); + + /// Cumulative, currently SCIP only + virtual void addCumulative(int nnz, int* rmatind, double* d, double* r, double b, + std::string rowName = ""); + virtual void setObjSense(int s) { // +/-1 for max/min + wrap_assert(setObjSense_SCIP(s)); + } + virtual SCIP_RETCODE setObjSense_SCIP(int s); + + virtual double getInfBound() { return SCIPinfinity(scip); } + + virtual int getNCols() { return SCIPgetNVars(scip); } + virtual int getNRows() { return SCIPgetNConss(scip); } + + // void setObjUB(double ub) { objUB = ub; } + // void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0 + + virtual void solve() { wrap_assert(solve_SCIP()); } + virtual SCIP_RETCODE solve_SCIP(); + + /// OUTPUT: + virtual const double* getValues() { return output.x; } + virtual double getObjValue() { return output.objVal; } + virtual double getBestBound() { return output.bestBound; } + virtual double getCPUTime() { return output.dCPUTime; } + + virtual Status getStatus() { return output.status; } + virtual std::string getStatusName() { return output.statusName; } + + virtual int getNNodes() { return output.nNodes; } + virtual int getNOpen() { return output.nOpenNodes; } + + // virtual int getNNodes() = 0; + // virtual double getTime() = 0; + +protected: + void wrap_assert(SCIP_RETCODE, std::string = "", bool fTerm = true); + + /// Need to consider the 100 status codes in SCIP and change with every version? TODO + Status convertStatus(SCIP_STATUS scipStatus); + +public: + /// Default MZN library for SCIP + static std::string getMznLib(); +}; + +#endif // __MIP_SCIP_WRAPPER_H__ diff --git a/include/minizinc/solvers/MIP/MIP_solverinstance.hh b/include/minizinc/solvers/MIP/MIP_solverinstance.hh new file mode 100644 index 0000000..dedf4a4 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_solverinstance.hh @@ -0,0 +1,155 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MIP_SOLVER_INSTANCE_H__ +#define __MINIZINC_MIP_SOLVER_INSTANCE_H__ + +#include +#include +#include + +namespace MiniZinc { + +// can be redefined as compilation parameter +#ifndef GETMIPWRAPPER +#define GETMIPWRAPPER MIP_WrapperFactory::GetDefaultMIPWrapper() +#endif + +class MIP_solver { +public: + typedef MIP_wrapper::VarId Variable; + typedef MiniZinc::Statistics Statistics; +}; + +/// Generic cut generator +/// Callback should be able to produce previously generated cuts again if needed [Gurobi] +class CutGen { +public: + virtual ~CutGen() {} + /// Say what type of cuts + virtual int getMask() = 0; + /// Adds new cuts to the 2nd parameter + virtual void generate(const MIP_wrapper::Output&, MIP_wrapper::CutInput&) = 0; + virtual void print(std::ostream&) {} +}; + +/// XBZ cut generator +class XBZCutGen : public CutGen { + XBZCutGen() {} + MIP_wrapper* pMIP = 0; + +public: + XBZCutGen(MIP_wrapper* pw) : pMIP(pw) {} + std::vector varX, varB; + /// Say what type of cuts + virtual int getMask() { return MIP_wrapper::MaskConsType_Usercut; } + MIP_wrapper::VarId varZ; + void generate(const MIP_wrapper::Output&, MIP_wrapper::CutInput&); + void print(std::ostream&); +}; + +/// SEC cut generator for circuit +class SECCutGen : public CutGen { + SECCutGen() {} + MIP_wrapper* pMIP = 0; + +public: + SECCutGen(MIP_wrapper* pw) : pMIP(pw) {} + /// Say what type of cuts + virtual int getMask() { + return MIP_wrapper::MaskConsType_Lazy | MIP_wrapper::MaskConsType_Usercut; + } + std::vector varXij; + int nN = 0; // N nodes + /// returns error message if fails + std::string validate() const; + void generate(const MIP_wrapper::Output&, MIP_wrapper::CutInput&); + void print(std::ostream&); +}; + +template +class MIP_solverinstance : public SolverInstanceImpl { + using SolverInstanceBase::_log; + +protected: + const std::unique_ptr mip_wrap; + std::vector > cutGenerators; + +public: + void registerCutGenerator(std::unique_ptr&& pCG) { + getMIPWrapper()->cbui.cutMask |= pCG->getMask(); + cutGenerators.push_back(move(pCG)); + } + +public: + double lastIncumbent; + double dObjVarLB = -1e300, dObjVarUB = 1e300; + +public: + MIP_solverinstance(Env& env, std::ostream& log, typename MIPWrapper::Options* opt) + : SolverInstanceImpl(env, log, opt), mip_wrap(new MIPWrapper(opt)) { + assert(mip_wrap.get()); + registerConstraints(); + } + virtual MIP_wrapper* getMIPWrapper() const { return mip_wrap.get(); } + + virtual Status next(void) { + assert(0); + return SolverInstance::UNKNOWN; + } + virtual void processFlatZinc(void); + // TODO: + void addDefinition(const std::vector& bs, Definition* def){}; + virtual void processWarmstartAnnotations(const Annotation& ann); + virtual void processSearchAnnotations(const Annotation& ann); + virtual Status solve(void); + virtual void resetSolver(void) {} + + virtual void genCuts(const MIP_wrapper::Output&, MIP_wrapper::CutInput&, bool fMIPSol); + + // void assignSolutionToOutput(); // needs to be public for the callback? + virtual void printStatistics(bool fLegend = 0); + virtual void printStatisticsLine(bool fLegend = 0); + +public: + /// creates a var for a literal, if necessary + VarId exprToVar(Expression* e); + void exprToArray(Expression* e, std::vector& vals); + void exprToVarArray(Expression* e, std::vector& vars); + std::pair exprToConstEasy(Expression* e); + double exprToConst(Expression* e); + + Expression* getSolutionValue(Id* id); + + void registerConstraints(void); +}; // MIP_solverinstance + +template +class MIP_SolverFactory : public SolverFactory { +public: + MIP_SolverFactory(void); + SolverInstanceBase::Options* createOptions(void) { return new typename MIPWrapper::Options; } + SolverInstanceBase* doCreateSI(Env& env, std::ostream& log, SolverInstanceBase::Options* opt) { + return new MIP_solverinstance(env, log, + static_cast(opt)); + } + bool processOption(SolverInstanceBase::Options* opt, int& i, std::vector& argv); + std::string getDescription(SolverInstanceBase::Options* opt = NULL); + std::string getVersion(SolverInstanceBase::Options* opt = NULL); + std::string getId(); + void printHelp(std::ostream& os) { MIPWrapper::Options::printHelp(os); } +}; + +} // namespace MiniZinc + +#include + +#endif // __MINIZINC_MIP_SOLVER_INSTANCE_H__ diff --git a/include/minizinc/solvers/MIP/MIP_solverinstance.hpp b/include/minizinc/solvers/MIP/MIP_solverinstance.hpp new file mode 100644 index 0000000..9718eea --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_solverinstance.hpp @@ -0,0 +1,944 @@ +#include + +namespace MiniZinc { + +template +MIP_SolverFactory::MIP_SolverFactory(void) { + std::vector requiredFlags; + std::string dllFlag = MIPWrapper::needDllFlag(); + if (dllFlag.size()) requiredFlags.push_back(dllFlag); + SolverConfig sc(getId(), MIPWrapper::getVersion()); + sc.name(MIPWrapper::getName()); + sc.mznlib(MIPWrapper::getMznLib()); + sc.mznlibVersion(1); + sc.supportsMzn(true); + sc.description("MiniZinc MIP solver plugin"); + sc.requiredFlags(requiredFlags); + sc.tags(MIPWrapper::getTags()); + sc.stdFlags(MIPWrapper::getStdFlags()); + SolverConfigs::registerBuiltinSolver(sc); +} + +template +bool MIP_SolverFactory::processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv) { + if (argv[i] == "--verbose-solving") { + opt->verbose = true; + return true; + } else if (argv[i] == "--solver-statistics") { + opt->printStatistics = true; + return true; + } else { + return static_cast(*opt).processOption(i, argv); + } +} + +template +std::string MIP_SolverFactory::getDescription(SolverInstanceBase::Options* opt) { + std::string v = + "MIP solver plugin, compiled " __DATE__ ", using: " + MIPWrapper::getDescription(opt); + return v; +} + +template +std::string MIP_SolverFactory::getVersion(SolverInstanceBase::Options* opt) { + return MIPWrapper::getVersion(opt); +} + +template +std::string MIP_SolverFactory::getId() { + return "org.minizinc.mip." + MIPWrapper::getId(); +} + +template +MIP_solver::Variable MIP_solverinstance::exprToVar(Expression* arg) { + if (Id* ident = arg->dyn_cast()) { + return _variableMap.get(ident->decl()->id()); + } else + return mip_wrap->addLitVar(exprToConst(arg)); +} + +template +void MIP_solverinstance::exprToVarArray(Expression* arg, std::vector& vars) { + ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); + vars.clear(); + vars.reserve(al->size()); + for (unsigned int i = 0; i < al->size(); i++) vars.push_back(exprToVar((*al)[i])); +} + +template +std::pair MIP_solverinstance::exprToConstEasy(Expression* e) { + std::pair res{0.0, true}; + if (IntLit* il = e->dyn_cast()) { + res.first = (static_cast(il->v().toInt())); + } else if (FloatLit* fl = e->dyn_cast()) { + res.first = (fl->v().toDouble()); + } else if (BoolLit* bl = e->dyn_cast()) { + res.first = (bl->v()); + } else { + res.second = false; + } + return res; +} + +template +double MIP_solverinstance::exprToConst(Expression* e) { + const auto e2ce = exprToConstEasy(e); + if (!e2ce.second) { + std::ostringstream oss; + oss << "ExprToConst: expected a numeric/bool literal, getting " << *e; + throw InternalError(oss.str()); + } + return e2ce.first; +} + +template +void MIP_solverinstance::exprToArray(Expression* arg, std::vector& vals) { + ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); + vals.clear(); + vals.reserve(al->size()); + for (unsigned int i = 0; i < al->size(); i++) { + vals.push_back(exprToConst((*al)[i])); + } +} + +template +void MIP_solverinstance::processSearchAnnotations(const Annotation& ann) { + if (1 == getMIPWrapper()->getFreeSearch()) return; + std::vector aAnns; + flattenSearchAnnotations(ann, aAnns); + std::vector vars; + std::vector aPri; // priorities + int nArrayAnns = 0; + for (auto iA = 0; iA < aAnns.size(); ++iA) { + const auto& pE = aAnns[iA]; + if (pE->isa()) { + Call* pC = pE->cast(); + const auto cId = pC->id().str(); + if ("int_search" == cId || "float_search" == cId) { + ArrayLit* alV = nullptr; + if (!pC->n_args() || nullptr == (alV = eval_array_lit(_env.envi(), pC->arg(0)))) { + std::cerr << " SEARCH ANN: '" << (*pC) << "' is unknown. " << std::endl; + continue; + } + ++nArrayAnns; + for (unsigned int i = 0; i < alV->size(); i++) { + if (Id* ident = (*alV)[i]->dyn_cast()) { + vars.push_back(exprToVar(ident)); + aPri.push_back(static_cast(aAnns.size()) - iA); // level search by default + } // else ignore + } + } + } + } + if (vars.size()) { + if (2 == getMIPWrapper()->getFreeSearch()) { + for (int i = 0; i < vars.size(); ++i) + aPri[i] = 1; // vars.size()-i; // descending + } + if (!getMIPWrapper()->addSearch(vars, aPri)) + std::cerr << "\nWARNING: MIP backend seems to ignore search strategy." << std::endl; + else + std::cerr << " MIP: added " << vars.size() << " variable branching priorities from " + << nArrayAnns << " arrays." << std::endl; + } +} + +template +void MIP_solverinstance::processWarmstartAnnotations(const Annotation& ann) { + int nVal = 0; + for (ExpressionSetIter i = ann.begin(); i != ann.end(); ++i) { + Expression* e = *i; + if (e->isa()) { + Call* c = e->cast(); + if (c->id().str() == "warm_start_array" || c->id().str() == "seq_search") { + ArrayLit* anns = c->arg(0)->cast(); + for (unsigned int i = 0; i < anns->size(); i++) { + Annotation subann; + subann.add((*anns)[i]); + processWarmstartAnnotations(subann); + } + } else if (c->id().str() == "warm_start") { + MZN_ASSERT_HARD_MSG(c->n_args() >= 2, "ERROR: warm_start needs 2 array args"); + std::vector coefs; + std::vector vars; + + /// Process coefs & vars together to eliminate literals (problem with Gurobi's + /// updatemodel()'s) + ArrayLit* alC = eval_array_lit(_env.envi(), c->arg(1)); + MZN_ASSERT_HARD_MSG(0 != alC, "ERROR: warm_start needs 2 array args"); + coefs.reserve(alC->size()); + ArrayLit* alV = eval_array_lit(_env.envi(), c->arg(0)); + MZN_ASSERT_HARD_MSG(0 != alV, "ERROR: warm_start needs 2 array args"); + vars.reserve(alV->size()); + for (unsigned int i = 0; i < alV->size() && i < alC->size(); i++) { + const auto e2c = exprToConstEasy((*alC)[i]); + /// Check if it is not an opt int etc. and a proper variable + if (e2c.second) + if (Id* ident = (*alV)[i]->dyn_cast()) { + coefs.push_back(e2c.first); + vars.push_back(exprToVar(ident)); + } // else ignore + } + assert(coefs.size() == vars.size()); + nVal += static_cast(coefs.size()); + if (coefs.size() && !getMIPWrapper()->addWarmStart(vars, coefs)) { + std::cerr << "\nWARNING: MIP backend seems to ignore warm starts" << std::endl; + return; + } + } + } + } + if (nVal && getMIPWrapper()->fVerbose) { + std::cerr << " MIP: added " << nVal << " MIPstart values..." << std::flush; + } +} + +template +void MIP_solverinstance::processFlatZinc(void) { + mip_wrap->fVerbose = _options->verbose; + + SolveI* solveItem = getEnv()->flat()->solveItem(); + VarDecl* objVd = NULL; + + if (solveItem->st() != SolveI::SolveType::ST_SAT) { + if (Id* id = solveItem->e()->dyn_cast()) { + objVd = id->decl(); + } else { + std::cerr << "Objective must be Id: " << solveItem->e() << std::endl; + throw InternalError("Objective must be Id"); + } + } + + for (VarDeclIterator it = getEnv()->flat()->begin_vardecls(); + it != getEnv()->flat()->end_vardecls(); ++it) { + if (it->removed()) { + continue; + } + VarDecl* vd = it->e(); + if (!vd->ann().isEmpty()) { + if (vd->ann().containsCall(constants().ann.output_array.aststr()) || + vd->ann().contains(constants().ann.output_var)) { + _varsWithOutput.push_back(vd); + // std::cerr << (*vd); + // if ( vd->e() ) + // cerr << " = " << (*vd->e()); + // cerr << endl; + } + } + if (vd->type().dim() == 0 && it->e()->type().isvar() && !it->removed()) { + MiniZinc::TypeInst* ti = it->e()->ti(); + MIP_wrapper::VarType vType = MIP_wrapper::VarType::REAL; // fInt = false; + if (ti->type().isvarint() || ti->type().isint()) + vType = MIP_wrapper::VarType::INT; + else if (ti->type().isvarbool() || ti->type().isbool()) { + vType = MIP_wrapper::VarType::BINARY; + } else if (ti->type().isvarfloat() || ti->type().isfloat()) { + } else { + std::stringstream ssm; + ssm << "This type of var is not handled by MIP: " << *it << std::endl; + ssm << " VarDecl flags (ti, bt, st, ot): " << ti->type().ti() << ti->type().bt() + << ti->type().st() << ti->type().ot() << ", dim == " << ti->type().dim() + << "\nRemove the variable or add a constraint so it is redefined." << std::endl; + throw InternalError(ssm.str()); + } + double lb = 0.0, ub = 1.0; // for bool + if (ti->domain()) { + if (MIP_wrapper::VarType::REAL == vType) { + FloatBounds fb = compute_float_bounds(getEnv()->envi(), it->e()->id()); + if (fb.valid) { + lb = fb.l.toDouble(); + ub = fb.u.toDouble(); + } else { + lb = 1.0; + ub = 0.0; + } + } else if (MIP_wrapper::VarType::INT == vType) { + IntBounds ib = compute_int_bounds(getEnv()->envi(), it->e()->id()); + if (ib.valid) { // Normally should be + lb = static_cast(ib.l.toInt()); + ub = static_cast(ib.u.toInt()); + } else { + lb = 1; + ub = 0; + } + } + } else if (MIP_wrapper::VarType::BINARY != vType) { + lb = -getMIPWrapper()->getInfBound(); // if just 1 bound inf, using MZN's default? TODO + ub = -lb; + } + + // IntSetVal* dom = eval_intset(env,vdi->e()->ti()->domain()); + // if (dom->size() > 1) + // throw runtime_error("MIP_solverinstance: domains with holes ! supported, use + // --MIPdomains"); + + VarId res; + Id* id = it->e()->id(); + MZN_ASSERT_HARD(id == id->decl()->id()); // Assume all unified + MZN_ASSERT_HARD(it->e() == id->decl()); // Assume all unified + double obj = vd == objVd ? 1.0 : 0.0; + auto decl00 = follow_id_to_decl(it->e()); + MZN_ASSERT_HARD(decl00->isa()); + { + auto vd00 = decl00->dyn_cast(); + if (0 != vd00->e()) { + // Should be a const + auto dRHS = exprToConst(vd00->e()); + lb = std::max(lb, dRHS); + ub = std::min(ub, dRHS); + } + if (it->e() != vd00) { // A different vardecl + res = exprToVar(vd00->id()); // Assume FZN is sorted. + MZN_ASSERT_HARD(!getMIPWrapper()->fPhase1Over); // Still can change colUB, colObj + /// Tighten the ini-expr's bounds + lb = getMIPWrapper()->colLB.at(res) = std::max(getMIPWrapper()->colLB.at(res), lb); + ub = getMIPWrapper()->colUB.at(res) = std::min(getMIPWrapper()->colUB.at(res), ub); + if (0.0 != obj) { + getMIPWrapper()->colObj.at(res) = obj; + } + } else { + res = getMIPWrapper()->addVar(obj, lb, ub, vType, id->str().c_str()); + } + } + /// Test infeasibility + if (lb > ub) { + _status = SolverInstance::UNSAT; + if (getMIPWrapper()->fVerbose) + std::cerr << " VarDecl '" << *(it->e()) << "' seems infeasible: computed bounds [" << lb + << ", " << ub << ']' << std::endl; + } + if (0.0 != obj) { + dObjVarLB = lb; + dObjVarUB = ub; + getMIPWrapper()->output.nObjVarIndex = res; + if (getMIPWrapper()->fVerbose) + std::cerr << " MIP: objective variable index (0-based): " << res << std::endl; + } + _variableMap.insert(id, res); + assert(res == _variableMap.get(id)); + } + } + if (mip_wrap->fVerbose && mip_wrap->sLitValues.size()) + std::cerr << " MIP_solverinstance: during Phase 1, " << mip_wrap->nLitVars + << " literals with " << mip_wrap->sLitValues.size() << " values used." << std::endl; + if (!getMIPWrapper()->fPhase1Over) getMIPWrapper()->addPhase1Vars(); + + if (mip_wrap->fVerbose) std::cerr << " MIP_solverinstance: adding constraints..." << std::flush; + + for (ConstraintIterator it = getEnv()->flat()->begin_constraints(); + it != getEnv()->flat()->end_constraints(); ++it) { + if (!it->removed()) { + if (Call* c = it->e()->dyn_cast()) { + _constraintRegistry.post(c); + } + } + } + + if (mip_wrap->fVerbose) { + std::cerr << " done, " << mip_wrap->getNRows() << " rows && " << mip_wrap->getNCols() + << " columns in total."; + if (mip_wrap->nIndicatorConstr) + std::cerr << " " << mip_wrap->nIndicatorConstr << " indicator constraints." << std::endl; + std::cerr << std::endl; + if (mip_wrap->sLitValues.size()) + std::cerr << " MIP_solverinstance: overall, " << mip_wrap->nLitVars << " literals with " + << mip_wrap->sLitValues.size() << " values used." << std::endl; + } + + processSearchAnnotations(solveItem->ann()); + + processWarmstartAnnotations(solveItem->ann()); +} // processFlatZinc + +template +Expression* MIP_solverinstance::getSolutionValue(Id* id) { + id = id->decl()->id(); + + if (id->type().isvar()) { + MIP_solver::Variable var = exprToVar(id); + double val = getMIPWrapper()->getValues()[var]; + switch (id->type().bt()) { + case Type::BT_INT: + return IntLit::a(round_to_longlong(val)); + case Type::BT_FLOAT: + return FloatLit::a(val); + case Type::BT_BOOL: + return new BoolLit(Location(), round_to_longlong(val) != 0); + default: + return NULL; + } + } else { + return id->decl()->e(); + } +} + +template +void MIP_solverinstance::genCuts(const MIP_wrapper::Output& slvOut, + MIP_wrapper::CutInput& cutsIn, bool fMIPSol) { + for (auto& pCG : cutGenerators) { + if (!fMIPSol || pCG->getMask() & MIP_wrapper::MaskConsType_Lazy) pCG->generate(slvOut, cutsIn); + } + /// Select some most violated? TODO +} + +template +void MIP_solverinstance::printStatisticsLine(bool fLegend) { + // auto nn = std::chrono::system_clock::now(); + // auto n_c = std::chrono::system_clock::to_time_t( nn ); + { + std::ios oldState(nullptr); + oldState.copyfmt(_log); + _log.precision(12); + _log << " % MIP Status: " << mip_wrap->getStatusName() << std::endl; + if (fLegend) _log << " % obj, bound, time wall/CPU, nodes (left): "; + _log << mip_wrap->getObjValue() << ", "; + _log << mip_wrap->getBestBound() << ", "; + _log.setf(std::ios::fixed); + _log.precision(1); + _log << mip_wrap->getWallTimeElapsed() << "/"; + _log << mip_wrap->getCPUTime() << ", "; + _log << mip_wrap->getNNodes(); + if (mip_wrap->getNOpen()) _log << " ( " << mip_wrap->getNOpen() << " )"; + // _log << " " << std::ctime( &n_c ); + // ctime already adds EOL. os << endl; + _log << std::endl; + _log.copyfmt(oldState); + } +} + +template +void MIP_solverinstance::printStatistics(bool fLegend) { + // auto nn = std::chrono::system_clock::now(); + // auto n_c = std::chrono::system_clock::to_time_t( nn ); + { + EnvI& env = getEnv()->envi(); + + std::ios oldState(nullptr); + oldState.copyfmt(env.outstream); + env.outstream.precision(12); + env.outstream << "%%%mzn-stat objective=" << mip_wrap->getObjValue() << std::endl; + ; + env.outstream << "%%%mzn-stat objectiveBound=" << mip_wrap->getBestBound() << std::endl; + ; + env.outstream << "%%%mzn-stat nodes=" << mip_wrap->getNNodes() << std::endl; + ; + if (mip_wrap->getNOpen()) + env.outstream << "%%%mzn-stat openNodes=" << mip_wrap->getNOpen() << std::endl; + ; + env.outstream.setf(std::ios::fixed); + env.outstream.precision(4); + env.outstream << "%%%mzn-stat solveTime=" << mip_wrap->getWallTimeElapsed() << std::endl; + ; + env.outstream.copyfmt(oldState); + + env.outstream << "%%%mzn-stat-end" << std::endl; + } +} + +template +void HandleSolutionCallback(const MIP_wrapper::Output& out, void* pp) { + // multi-threading? TODO + MIP_solverinstance* pSI = static_cast*>(pp); + assert(pSI); + /// Not for -a: + // if (fabs(pSI->lastIncumbent - out.objVal) > 1e-12*(1.0 + fabs(out.objVal))) { + pSI->lastIncumbent = out.objVal; + + try { /// Sometimes the intermediate output is wrong, especially in SCIP + pSI->printSolution(); // The solution in [out] is not used TODO + } catch (const Exception& e) { + std::cerr << std::endl; + std::cerr << " Error when evaluating an intermediate solution: " << e.what() << ": " + << e.msg() << std::endl; + } catch (const std::exception& e) { + std::cerr << std::endl; + std::cerr << " Error when evaluating an intermediate solution: " << e.what() << std::endl; + } catch (...) { + std::cerr << std::endl; + std::cerr << " Error when evaluating an intermediate solution: " + << " UNKNOWN EXCEPTION." << std::endl; + } + // } +} + +template +void HandleCutCallback(const MIP_wrapper::Output& out, MIP_wrapper::CutInput& in, void* pp, + bool fMIPSol) { + // multi-threading? TODO + MIP_solverinstance* pSI = static_cast*>(pp); + assert(pSI); + assert(&out); + assert(&in); + pSI->genCuts(out, in, fMIPSol); +} + +template +SolverInstance::Status MIP_solverinstance::solve(void) { + SolveI* solveItem = getEnv()->flat()->solveItem(); + int nProbType = 0; + if (solveItem->st() != SolveI::SolveType::ST_SAT) { + if (solveItem->st() == SolveI::SolveType::ST_MAX) { + getMIPWrapper()->setObjSense(1); + getMIPWrapper()->setProbType(1); + nProbType = 1; + if (mip_wrap->fVerbose) + std::cerr << " MIP_solverinstance: this is a MAXimization problem." << std::endl; + } else { + getMIPWrapper()->setObjSense(-1); + getMIPWrapper()->setProbType(-1); + nProbType = -1; + if (mip_wrap->fVerbose) + std::cerr << " MIP_solverinstance: this is a MINimization problem." << std::endl; + } + if (mip_wrap->fVerbose) { + std::cerr << " MIP_solverinstance: bounds for the objective function: " << dObjVarLB + << ", " << dObjVarUB << std::endl; + } + } else { + getMIPWrapper()->setProbType(0); + if (mip_wrap->fVerbose) + std::cerr << " MIP_solverinstance: this is a SATisfiability problem." << std::endl; + } + + lastIncumbent = 1e200; // for callbacks + MIP_wrapper::Status sw; + if (SolverInstance::UNSAT == _status) // already deduced - exit now + return _status; + if (getMIPWrapper()->getNCols()) { // If any variables, we need to run solver just to get values? + getMIPWrapper()->provideSolutionCallback(HandleSolutionCallback, this); + if (cutGenerators.size()) // only then, can modify presolve + getMIPWrapper()->provideCutCallback(HandleCutCallback, this); + ////////////// clean up envi ///////////////// + { + /// Removing for now - need access to output variables TODO + // cleanupForNonincrementalSolving(); + if (GC::locked() && mip_wrap->fVerbose) + std::cerr << "WARNING: GC is locked before SolverInstance::solve()! Wasting memory." + << std::endl; + // GCLock lock; + GC::trigger(); + } + getMIPWrapper()->solve(); + // printStatistics(cout, 1); MznSolver does this (if it wants) + sw = getMIPWrapper()->getStatus(); + } else { + if (mip_wrap->fVerbose) + std::cerr << " MIP_solverinstance: no constraints - skipping actual solution phase." + << std::endl; + sw = MIP_wrapper::Status::OPT; + printSolution(); + } + SolverInstance::Status s = SolverInstance::UNKNOWN; + switch (sw) { + case MIP_wrapper::Status::OPT: + if (0 != nProbType) { + s = SolverInstance::OPT; + } else { + s = SolverInstance::SAT; // For SAT problems, just say SAT unless we know it's complete + } + break; + case MIP_wrapper::Status::SAT: + s = SolverInstance::SAT; + break; + case MIP_wrapper::Status::UNSAT: + s = SolverInstance::UNSAT; + break; + case MIP_wrapper::Status::UNBND: + s = SolverInstance::UNBND; + break; + case MIP_wrapper::Status::UNSATorUNBND: + s = SolverInstance::UNSATorUNBND; + break; + case MIP_wrapper::Status::UNKNOWN: + s = SolverInstance::UNKNOWN; + break; + default: + s = SolverInstance::ERROR; + } + return s; +} + +namespace SCIPConstraints { + +bool CheckAnnUserCut(const Call* call); +bool CheckAnnLazyConstraint(const Call* call); +int GetMaskConsType(const Call* call); + +/// Create constraint name +/// Input: a prefix, a counter, and the original call. +/// If the call has a path annotation, that is used, +/// otherwise pfx << cnt. +inline std::string makeConstrName(const char* pfx, int cnt, const Expression* cOrig = nullptr) { + Call* mznp; + if (nullptr != cOrig && (mznp = cOrig->ann().getCall(constants().ann.mzn_path))) { + assert(1 == mznp->n_args()); + auto strp = mznp->arg(0)->dyn_cast(); + assert(strp); + return strp->v().str().substr(0, 255); // Gurobi 8.1 has <=255 characters + } + std::ostringstream ss; + ss << pfx << cnt; + return ss.str(); +} + +/// Gurobi 8.1.0 complains about duplicates, CPLEX 12.8.0 just ignores repeats +/// An example for duplicated indices was on 72a9b64f with two floats equated +template +void removeDuplicates(std::vector& rmi, std::vector& rmv) { + std::unordered_map linExp; + for (int i = rmi.size(); i--;) linExp[rmi[i]] += rmv[i]; + if (rmi.size() == linExp.size()) return; + rmi.resize(linExp.size()); + rmv.resize(linExp.size()); + int i = 0; + for (const auto& iv : linExp) { + rmi[i] = iv.first; + rmv[i] = iv.second; + ++i; + } +} + +template +void p_lin(SolverInstanceBase& si, const Call* call, MIP_wrapper::LinConType lt) { + MIP_solverinstance& gi = dynamic_cast&>(si); + Env& _env = gi.env(); + // ArrayLit* al = eval_array_lit(_env.envi(), args[0]); + // int nvars = al->v().size(); + std::vector coefs; + // gi.exprToArray(args[0], coefs); + std::vector::VarId> vars; + // gi.exprToVarArray(args[1], vars); + IntVal ires; + FloatVal fres; + + double rhs; + if (call->arg(2)->type().isint()) { + ires = eval_int(_env.envi(), call->arg(2)); + rhs = static_cast(ires.toInt()); + } else if (call->arg(2)->type().isfloat()) { + fres = eval_float(_env.envi(), call->arg(2)); + rhs = fres.toDouble(); + } else { + throw InternalError("p_lin: rhs unknown type"); + } + + /// Process coefs & vars together to eliminate literals (problem with Gurobi's updatemodel()'s) + ArrayLit* alC = eval_array_lit(_env.envi(), call->arg(0)); + coefs.reserve(alC->size()); + ArrayLit* alV = eval_array_lit(_env.envi(), call->arg(1)); + vars.reserve(alV->size()); + for (unsigned int i = 0; i < alV->size(); i++) { + const double dCoef = gi.exprToConst((*alC)[i]); + if (Id* ident = (*alV)[i]->dyn_cast()) { + coefs.push_back(dCoef); + vars.push_back(gi.exprToVar(ident)); + } else + rhs -= dCoef * gi.exprToConst((*alV)[i]); + } + assert(coefs.size() == vars.size()); + + /// Check feas-ty + if (coefs.empty()) { + if ((MIP_wrapper::LinConType::EQ == lt && 1e-5 < fabs(rhs)) || + (MIP_wrapper::LinConType::LQ == lt && -1e-5 > (rhs)) || + (MIP_wrapper::LinConType::GQ == lt && 1e-5 < (rhs))) { + si._status = SolverInstance::UNSAT; + if (gi.getMIPWrapper()->fVerbose) + std::cerr << " Constraint '" << *call << "' seems infeasible: simplified to 0 (rel) " + << rhs << std::endl; + } + } else { + removeDuplicates(vars, coefs); + // See if the solver adds indexation itself: no. + gi.getMIPWrapper()->addRow(static_cast(coefs.size()), &vars[0], &coefs[0], lt, rhs, + GetMaskConsType(call), + makeConstrName("p_lin_", (gi.getMIPWrapper()->nAddedRows++), call)); + } +} + +template +void p_int_lin_le(SolverInstanceBase& si, const Call* call) { + p_lin(si, call, MIP_wrapper::LQ); +} +template +void p_int_lin_eq(SolverInstanceBase& si, const Call* call) { + p_lin(si, call, MIP_wrapper::EQ); +} +template +void p_float_lin_le(SolverInstanceBase& si, const Call* call) { + p_lin(si, call, MIP_wrapper::LQ); +} +template +void p_float_lin_eq(SolverInstanceBase& si, const Call* call) { + p_lin(si, call, MIP_wrapper::EQ); +} + +// The non-_lin constraints happen in a failed model || in a non-optimized one: +template +void p_non_lin(SolverInstanceBase& si, const Call* call, MIP_wrapper::LinConType nCmp) { + MIP_solverinstance& gi = dynamic_cast&>(si); + std::vector coefs; + std::vector vars; + double rhs = 0.0; + if (call->arg(0)->isa()) { + coefs.push_back(1.0); + vars.push_back(gi.exprToVar(call->arg(0))); + } else + rhs -= gi.exprToConst(call->arg(0)); + if (call->arg(1)->isa()) { + coefs.push_back(-1.0); + vars.push_back(gi.exprToVar(call->arg(1))); + } else + rhs += gi.exprToConst(call->arg(1)); + /// Check feas-ty + if (coefs.empty()) { + if ((MIP_wrapper::LinConType::EQ == nCmp && 1e-5 < fabs(rhs)) || + (MIP_wrapper::LinConType::LQ == nCmp && -1e-5 > (rhs)) || + (MIP_wrapper::LinConType::GQ == nCmp && 1e-5 < (rhs))) { + si._status = SolverInstance::UNSAT; + if (gi.getMIPWrapper()->fVerbose) + std::cerr << " Constraint '" << *call << "' seems infeasible: simplified to 0 (rel) " + << rhs << std::endl; + } + } else { + removeDuplicates(vars, coefs); + gi.getMIPWrapper()->addRow(static_cast(vars.size()), &vars[0], &coefs[0], nCmp, rhs, + GetMaskConsType(call), + makeConstrName("p_eq_", (gi.getMIPWrapper()->nAddedRows++), call)); + } +} +template +void p_eq(SolverInstanceBase& si, const Call* call) { + p_non_lin(si, call, MIP_wrapper::EQ); +} +template +void p_le(SolverInstanceBase& si, const Call* call) { + p_non_lin(si, call, MIP_wrapper::LQ); +} + +/// var1<=0 if var2==0 +template +void p_indicator_le0_if0(SolverInstanceBase& si, const Call* call) { + MIP_solverinstance& gi = dynamic_cast&>(si); + /// Looking at the bounded variable and the flag + bool f1const = 0, f2const = 0; + double val1, val2; + MIP_solver::Variable var1, var2; + if (call->arg(0)->isa()) { + var1 = gi.exprToVar(call->arg(0)); + } else { + f1const = 1; + val1 = gi.exprToConst(call->arg(0)); + } + if (call->arg(1)->isa()) { + var2 = gi.exprToVar(call->arg(1)); + } else { + f2const = 1; + val2 = gi.exprToConst(call->arg(1)); + } + /// Check feas-ty. 1e-6 ????????????? TODO + if (f1const && f2const) { + if (val1 > 1e-6 && val2 < 1e-6) { + si._status = SolverInstance::UNSAT; + if (gi.getMIPWrapper()->fVerbose) + std::cerr << " Constraint '" << *call << "' seems infeasible: " << val2 << "==0 -> " + << val1 << "<=0" << std::endl; + } + } else if (f1const) { + if (val1 > 1e-6) // so var2==1 + gi.getMIPWrapper()->setVarBounds(var2, 1.0, 1.0); + } else if (f2const) { + if (val2 < 1e-6) // so var1<=0 + gi.getMIPWrapper()->setVarUB(var1, 0.0); + } else { + double coef = 1.0; + gi.getMIPWrapper()->addIndicatorConstraint( + var2, 0, 1, &var1, &coef, MIP_wrapper::LinConType::LQ, 0.0, + makeConstrName("p_ind_", (gi.getMIPWrapper()->nAddedRows++), call)); + ++gi.getMIPWrapper()->nIndicatorConstr; + } +} + +/// var1==var2 if var3==1 +template +void p_indicator_eq_if1(SolverInstanceBase& si, const Call* call) { + MIP_solverinstance& gi = dynamic_cast&>(si); + std::vector coefs; + std::vector vars; + double rhs = 0.0; + /// Looking at the bounded variables and the flag + bool f1const = 0, f2const = 0, fBconst = 0; + double val1, val2, valB; + MIP_solver::Variable var1, var2, varB; + if (call->arg(0)->isa()) { + var1 = gi.exprToVar(call->arg(0)); + coefs.push_back(1.0); + vars.push_back(var1); + } else { + f1const = 1; + val1 = gi.exprToConst(call->arg(0)); + rhs -= val1; + } + if (call->arg(1)->isa()) { + var2 = gi.exprToVar(call->arg(1)); + coefs.push_back(-1.0); + vars.push_back(var2); + } else { + f2const = 1; + val2 = gi.exprToConst(call->arg(1)); + rhs += val2; + } + if (call->arg(2)->isa()) { + varB = gi.exprToVar(call->arg(2)); + } else { + fBconst = 1; + valB = gi.exprToConst(call->arg(2)); + } + /// Check feas-ty. 1e-6 ????????????? TODO + if (f1const && f2const && fBconst) { + if (fabs(val1 - val2) > 1e-6 && val2 > 0.999999) { + si._status = SolverInstance::UNSAT; + if (gi.getMIPWrapper()->fVerbose) + std::cerr << " Constraint '" << *call << "' seems infeasible: " << valB << "==0 -> " + << val1 << "==" << val2 << std::endl; + } + } else if (f1const && f2const) { + if (fabs(val1 - val2) > 1e-6) // so varB=0 + gi.getMIPWrapper()->setVarBounds(varB, 0.0, 0.0); + } else if (fBconst) { + if (val2 > 0.999999) { // so var1<=0 + removeDuplicates(vars, coefs); + gi.getMIPWrapper()->addRow(static_cast(vars.size()), &vars[0], &coefs[0], + MIP_wrapper::LinConType::EQ, rhs, MIP_wrapper::MaskConsType_Normal, + makeConstrName("p_eq_", (gi.getMIPWrapper()->nAddedRows++), call)); + } + } else { + std::ostringstream ss; + ss << "p_ind_" << (gi.getMIPWrapper()->nAddedRows++); + gi.getMIPWrapper()->addIndicatorConstraint( + varB, 1, static_cast(coefs.size()), vars.data(), coefs.data(), + MIP_wrapper::LinConType::EQ, rhs, + makeConstrName("p_ind_", (gi.getMIPWrapper()->nAddedRows++), call)); + ++gi.getMIPWrapper()->nIndicatorConstr; + } +} + +/// Cumulative +template +void p_cumulative(SolverInstanceBase& si, const Call* call) { + MIP_solverinstance& gi = dynamic_cast&>(si); + + std::unique_ptr pCG(new SECCutGen(gi.getMIPWrapper())); + + assert(call->n_args() == 4); + + std::vector startTimes; + gi.exprToVarArray(call->arg(0), startTimes); + std::vector durations, demands; + gi.exprToArray(call->arg(1), durations); + gi.exprToArray(call->arg(2), demands); + double b = gi.exprToConst(call->arg(3)); + + gi.getMIPWrapper()->addCumulative( + startTimes.size(), startTimes.data(), durations.data(), demands.data(), b, + makeConstrName("p_cumulative_", (gi.getMIPWrapper()->nAddedRows++), call)); +} + +/// The XBZ cut generator +template +void p_XBZ_cutgen(SolverInstanceBase& si, const Call* call) { + MIP_solverinstance& gi = dynamic_cast&>(si); + + // auto pCG = make_unique(); + std::unique_ptr pCG(new XBZCutGen(gi.getMIPWrapper())); + + assert(call->n_args() == 3); + gi.exprToVarArray(call->arg(0), pCG->varX); + gi.exprToVarArray(call->arg(1), pCG->varB); + assert(pCG->varX.size() == pCG->varB.size()); + pCG->varZ = gi.exprToVar(call->arg(2)); + // cout << " NEXT_CUTGEN" << endl; + // pCG->print( cout ); + + gi.registerCutGenerator(move(pCG)); +} + +/// Initialize the SEC cut generator +template +void p_SEC_cutgen(SolverInstanceBase& si, const Call* call) { + MIP_solverinstance& gi = dynamic_cast&>(si); + + std::unique_ptr pCG(new SECCutGen(gi.getMIPWrapper())); + + assert(call->n_args() == 1); + gi.exprToVarArray(call->arg(0), pCG->varXij); // WHAT ABOUT CONSTANTS? + const double dN = sqrt(pCG->varXij.size()); + MZN_ASSERT_HARD(fabs(dN - round(dN)) < 1e-6); // should be a square matrix + pCG->nN = round(dN); + const auto sVld = pCG->validate(); + MZN_ASSERT_HARD_MSG(sVld.empty(), "ERROR(s): " << sVld); + // cout << " NEXT_CUTGEN" << endl; + // pCG->print( cout ); + + gi.registerCutGenerator(move(pCG)); +} + +/// SCIP's bound disj +template +void p_bounds_disj(SolverInstanceBase& si, const Call* call) { + MIP_solverinstance& gi = dynamic_cast&>(si); + assert(6 == call->n_args()); + std::vector fUB, fUBF, bnd, bndF; + std::vector vars, varsF; + gi.exprToArray(call->arg(0), fUB); + gi.exprToArray(call->arg(3), fUBF); + gi.exprToArray(call->arg(1), bnd); + gi.exprToArray(call->arg(4), bndF); + gi.exprToVarArray(call->arg(2), vars); + gi.exprToVarArray(call->arg(5), varsF); + double coef = 1.0; + gi.getMIPWrapper()->addBoundsDisj( + fUB.size(), fUB.data(), bnd.data(), vars.data(), fUBF.size(), fUBF.data(), bndF.data(), + varsF.data(), makeConstrName("p_bounds_disj_", (gi.getMIPWrapper()->nAddedRows++), call)); +} + +} // namespace SCIPConstraints + +template +void MIP_solverinstance::registerConstraints() { + GCLock lock; + _constraintRegistry.add("int2float", SCIPConstraints::p_eq); + _constraintRegistry.add("bool_eq", + SCIPConstraints::p_eq); // for inconsistency reported in fzn + _constraintRegistry.add("int_eq", SCIPConstraints::p_eq); + _constraintRegistry.add("int_le", SCIPConstraints::p_le); + _constraintRegistry.add("int_lin_eq", SCIPConstraints::p_int_lin_eq); + _constraintRegistry.add("int_lin_le", SCIPConstraints::p_int_lin_le); + // _constraintRegistry.add("int_plus", SCIPConstraints::p_plus); + // _constraintRegistry.add("bool2int", SCIPConstraints::p_eq); + _constraintRegistry.add("float_eq", SCIPConstraints::p_eq); + _constraintRegistry.add("float_le", SCIPConstraints::p_le); + _constraintRegistry.add("float_lin_eq", SCIPConstraints::p_float_lin_eq); + _constraintRegistry.add("float_lin_le", SCIPConstraints::p_float_lin_le); + // _constraintRegistry.add("float_plus", SCIPConstraints::p_plus); + + /// Indicators, if supported by the solver + _constraintRegistry.add("aux_int_le_zero_if_0__IND", + SCIPConstraints::p_indicator_le0_if0); + _constraintRegistry.add("aux_float_le_zero_if_0__IND", + SCIPConstraints::p_indicator_le0_if0); + _constraintRegistry.add("aux_float_eq_if_1__IND", + SCIPConstraints::p_indicator_eq_if1); + + _constraintRegistry.add("fzn_cumulative", SCIPConstraints::p_cumulative); + + /// XBZ cut generator + _constraintRegistry.add("array_var_float_element__XBZ_lb__cutgen", + SCIPConstraints::p_XBZ_cutgen); + _constraintRegistry.add("circuit__SECcuts", SCIPConstraints::p_SEC_cutgen); + + _constraintRegistry.add("bounds_disj", SCIPConstraints::p_bounds_disj); +} + +} // namespace MiniZinc diff --git a/include/minizinc/solvers/MIP/MIP_wrap.hh b/include/minizinc/solvers/MIP/MIP_wrap.hh new file mode 100644 index 0000000..aa41d2d --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_wrap.hh @@ -0,0 +1,325 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MIP_WRAPPER__ +#define __MIP_WRAPPER__ + +#include +//#include +#include +#include +#include +#include +#include +#include + +/// Facilitate lhs computation of a cut +inline double computeSparse(int n, const int* ind, const double* coef, const double* dense, + int nVarsDense) { + assert(ind && coef && dense); + double val = 0.0; + for (int i = 0; i < n; ++i) { + assert(ind[i] >= 0); + assert(ind[i] < nVarsDense); + val += coef[i] * dense[ind[i]]; + } + return val; +} + +class MIP_wrapper; + +/// An abstract MIP wrapper. +/// Does not include MZN stuff so can be used independently +/// although it's limited to the MZN solving needs. +class MIP_wrapper { +public: + typedef int VarId; // CPLEX uses int + enum VarType { REAL, INT, BINARY }; + enum LinConType { LQ = -1, EQ = 0, GQ = 1 }; + + // CPLEX 12.6.2 advises anti-symmetry constraints to be user+lazy + static const int MaskConsType_Normal = 1; + /// User cut. Only cuts off fractional points, no integer feasible points + static const int MaskConsType_Usercut = 2; + /// Lazy cut. Can cut off otherwise feasible integer solutions. + /// Callback should be able to produce previously generated cuts again if needed [Gurobi] + static const int MaskConsType_Lazy = 4; + enum Status { OPT, SAT, UNSAT, UNBND, UNSATorUNBND, UNKNOWN, __ERROR }; + +public: + /// Columns for SCIP upfront and with obj coefs: + std::vector colObj, colLB, colUB; + std::vector colTypes; + std::vector colNames; + // , rowLB, rowUB, elements; + // veci whichInt + // , starts, column; + // double objUB; + // double qpu; + +public: + /// Parameter + bool fVerbose = false; + + int nProbType = -2; // +-1: max/min; 0: sat + +public: + struct Output { + Status status; + std::string statusName = "Untouched"; + double objVal = 1e308; + double bestBound = 1e308; + int nCols = 0; + int nObjVarIndex = -1; + const double* x = 0; + int nNodes = 0; + int nOpenNodes = 0; + double dWallTime = 0.0; + std::chrono::time_point dWallTime0; + double dCPUTime = 0; + std::clock_t cCPUTime0 = 0; + }; + Output output; + + /// General cut definition, could be used for addRow() too + class CutDef { + CutDef() {} + + public: + CutDef(LinConType s, int m) : sense(s), mask(m) {} + std::vector rmatind; + std::vector rmatval; + LinConType sense = LQ; + double rhs = 0.0; + int mask = 0; // need to know what type of cuts are registered before solve() TODO + std::string rowName = ""; + void addVar(int i, double c) { + rmatind.push_back(i); + rmatval.push_back(c); + } + double computeViol(const double* x, int nCols) { + double lhs = + computeSparse(static_cast(rmatind.size()), rmatind.data(), rmatval.data(), x, nCols); + if (LQ == sense) { + return lhs - rhs; + } else if (GQ == sense) { + return rhs - lhs; + } else + assert(0); + return 0.0; + } + }; + /// Cut callback fills one + typedef std::vector CutInput; + +public: + /// solution callback handler, the wrapper might not have these callbacks implemented + typedef void (*SolCallbackFn)(const Output&, void*); + /// cut callback handler, the wrapper might not have these callbacks implemented + typedef void (*CutCallbackFn)(const Output&, CutInput&, void*, + bool fMIPSol // if with a MIP feas sol - lazy cuts only + ); + struct CBUserInfo { + MIP_wrapper* wrapper = 0; + MIP_wrapper::Output* pOutput = 0; + MIP_wrapper::Output* pCutOutput = 0; + void* psi = 0; // external info. Intended to keep MIP_solverinstance + SolCallbackFn solcbfn = 0; + CutCallbackFn cutcbfn = 0; + /// Union of all flags used for the registered callback cuts + /// See MaskConstrType_.. + /// Solvers need to know this + /// In MIP_solverinstance, class CutGen defines getMask() which should return that + int cutMask = 0; // can be any combination of User/Lazy + bool fVerb = false; // used in Gurobi + bool printed = false; // whether any solution was output + double nTimeoutFeas = -1.0; // >=0 => stop that long after 1st feas + double nTime1Feas = -1e100; // time of the 1st feas + }; + CBUserInfo cbui; + +public: + MIP_wrapper() { cbui.wrapper = this; } + virtual ~MIP_wrapper() { /* cleanup(); */ + } + + /// derived should overload and call the ancestor + // virtual void cleanup() { + // colObj.clear(); colLB.clear(); colUB.clear(); + // colTypes.clear(); colNames.clear(); + // } + /// re-create solver object. Called from the base class constructor + // virtual void resetModel() { }; + + // virtual void printVersion(ostream& os) { os << "Abstract MIP wrapper"; } + // virtual void printHelp(ostream& ) { } + +public: + bool fPhase1Over = false; + +private: + /// adding a variable just internally (in Phase 1 only that). Not to be used directly. + virtual VarId addVarLocal(double obj, double lb, double ub, VarType vt, std::string name = "") { + // cerr << " addVarLocal: colObj.size() == " << colObj.size() + // << " obj == " <(colObj.size() - 1); + } + /// add the given var to the solver. Asserts all previous are added. Phase >=2. No direct use + virtual void addVar(int j) { + assert(j == getNCols()); + assert(fPhase1Over); + doAddVars(1, &colObj[j], &colLB[j], &colUB[j], &colTypes[j], &colNames[j]); + } + /// actual adding new variables to the solver. "Updates" the model (e.g., Gurobi). No direct use + virtual void doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, + std::string* names) = 0; + +public: + /// debugging stuff + // set sLitValues; + std::unordered_map sLitValues; + + void setProbType(int t) { nProbType = t; } + + /// adding a variable, at once to the solver, this is for the 2nd phase + virtual VarId addVar(double obj, double lb, double ub, VarType vt, std::string name = "") { + // cerr << " AddVar: " << lb << ": "; + VarId res = addVarLocal(obj, lb, ub, vt, name); + if (fPhase1Over) addVar(res); + return res; + } + int nLitVars = 0; + /// adding a literal as a variable. Should not happen in feasible models + virtual VarId addLitVar(double v) { + // Cannot do this: at least CBC does not support duplicated indexes TODO?? + // ++nLitVars; + // auto itFound = sLitValues.find(v); + // if (sLitValues.end() != itFound) + // return itFound->second; + std::ostringstream oss; + oss << "lit_" << v << "__" << (nLitVars++); + std::string name = oss.str(); + size_t pos = name.find('.'); + if (std::string::npos != pos) name.replace(pos, 1, "p"); + VarId res = addVarLocal(0.0, v, v, REAL, name); + if (fPhase1Over) addVar(res); + // cerr << " AddLitVar " << v << " (PROBABLY WRONG)" << endl; + sLitValues[v] = res; + return res; + } + /// adding all local variables upfront. Makes sure it's called only once + virtual void addPhase1Vars() { + assert(0 == getNColsModel()); + assert(!fPhase1Over); + if (fVerbose) + std::cerr << " MIP_wrapper: adding the " << colObj.size() << " Phase-1 variables..." + << std::flush; + doAddVars(colObj.size(), &colObj[0], &colLB[0], &colUB[0], &colTypes[0], &colNames[0]); + if (fVerbose) std::cerr << " done." << std::endl; + fPhase1Over = true; // SCIP needs after adding + } + + /// var bounds + virtual void setVarBounds(int iVar, double lb, double ub) { throw 0; } + virtual void setVarLB(int iVar, double lb) { throw 0; } + virtual void setVarUB(int iVar, double ub) { throw 0; } + /// adding a linear constraint + virtual void addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask = MaskConsType_Normal, std::string rowName = "") = 0; + /// Indicator constraint: x[iBVar]==bVal -> lin constr + virtual void addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, double* rmatval, + LinConType sense, double rhs, std::string rowName = "") { + throw std::runtime_error("Indicator constraints not supported. "); + } + + /// Bounds disj for SCIP + virtual void addBoundsDisj(int n, double* fUB, double* bnd, int* vars, int nF, double* fUBF, + double* bndF, int* varsF, std::string rowName = "") { + throw std::runtime_error("Bounds disjunctions not supported. "); + } + + /// Cumulative, currently SCIP only + virtual void addCumulative(int nnz, int* rmatind, double* d, double* r, double b, + std::string rowName = "") { + throw std::runtime_error("Cumulative constraints not supported. "); + } + /// 0: model-defined level, 1: free, 2: uniform search + virtual int getFreeSearch() { return 1; } + /// Return 0 if ignoring searches + virtual bool addSearch(const std::vector& vars, const std::vector pri) { + return false; + } + /// Return 0 if ignoring warm starts + virtual bool addWarmStart(const std::vector& vars, const std::vector vals) { + return false; + } + + int nAddedRows = 0; // for name counting + int nIndicatorConstr = 0; + /// adding an implication + // virtual void addImpl() = 0; + virtual void setObjSense(int s) = 0; // +/-1 for max/min + + virtual double getInfBound() = 0; + + virtual int getNCols() = 0; + virtual int getNColsModel() { return getNCols(); } // from the solver + virtual int getNRows() = 0; + + // void setObjUB(double ub) { objUB = ub; } + // void addQPUniform(double c) { qpu = c; } // also sets problem type to MIQP unless c=0 + + /// Set solution callback. Thread-safety?? + /// solution callback handler, the wrapper might not have these callbacks implemented + virtual void provideSolutionCallback(SolCallbackFn cbfn, void* info) { + assert(cbfn); + cbui.pOutput = &output; + cbui.psi = info; + cbui.solcbfn = cbfn; + } + /// solution callback handler, the wrapper might not have these callbacks implemented + virtual void provideCutCallback(CutCallbackFn cbfn, void* info) { + assert(cbfn); + cbui.pCutOutput = 0; // &outpCuts; thread-safety: caller has to provide this + cbui.psi = info; + cbui.cutcbfn = cbfn; + } + + virtual void solve() = 0; + + /// OUTPUT, should also work in a callback + virtual const double* getValues() = 0; + virtual double getObjValue() = 0; + virtual double getBestBound() = 0; + virtual double getWallTimeElapsed() { return output.dWallTime; } + virtual double getCPUTime() = 0; + + virtual Status getStatus() = 0; + virtual std::string getStatusName() = 0; + + virtual int getNNodes() = 0; + virtual int getNOpen() = 0; + + /// Default MZN library for MIP + static std::string getMznLib(); +}; + +#endif // __MIP_WRAPPER__ diff --git a/include/minizinc/solvers/MIP/MIP_xpress_solverfactory.hh b/include/minizinc/solvers/MIP/MIP_xpress_solverfactory.hh new file mode 100644 index 0000000..9626fa4 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_xpress_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MIP_XPRESS_SOLVERFACTORY_HH__ +#define __MINIZINC_MIP_XPRESS_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class Xpress_SolverFactoryInitialiser { +public: + Xpress_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/MIP/MIP_xpress_wrap.hh b/include/minizinc/solvers/MIP/MIP_xpress_wrap.hh new file mode 100644 index 0000000..ceb45c6 --- /dev/null +++ b/include/minizinc/solvers/MIP/MIP_xpress_wrap.hh @@ -0,0 +1,95 @@ +/* + * Main authors: + * Karsten Lehmann + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MIP_XPRESS_WRAPPER_H__ +#define __MIP_XPRESS_WRAPPER_H__ +#include + +#include "minizinc/solvers/MIP/MIP_wrap.hh" +#include "xprb_cpp.h" +#include "xprs.h" + +using namespace std; +using namespace dashoptimization; + +class MIP_xpress_wrapper : public MIP_wrapper { +public: + class Options : public MiniZinc::SolverInstanceBase::Options { + public: + int msgLevel = 0; + int timeout = 0; + int numSolutions = 0; + std::string logFile = ""; + std::string writeModelFile = ""; + std::string writeModelFormat = "lp"; + double absGap = 0; + double relGap = 0.0001; + bool printAllSolutions = false; + bool processOption(int& i, std::vector& argv); + static void printHelp(std::ostream&); + }; + +private: + Options* options = nullptr; + +public: +public: + virtual void doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, string* names); + virtual void addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask = MaskConsType_Normal, string rowName = ""); + virtual void setObjSense(int s); + virtual void solve(); + virtual void setVarLB(int iVar, double lb); + virtual void setVarUB(int iVar, double ub); + virtual void setVarBounds(int iVar, double lb, double ub); + virtual void addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, double* rmatval, + LinConType sense, double rhs, std::string rowName = ""); + virtual bool addWarmStart(const std::vector& vars, const std::vector vals); + + virtual int getNCols() { return variables.size(); } + virtual int getNRows() { return nRows; } + virtual double getInfBound() { return XPRB_INFINITY; } + virtual const double* getValues() { return output.x; } + virtual double getObjValue() { return output.objVal; } + virtual double getBestBound() { return output.bestBound; } + virtual double getCPUTime() { return output.dCPUTime; } + virtual Status getStatus() { return output.status; } + virtual string getStatusName() { return output.statusName; } + virtual int getNNodes() { return output.nNodes; } + virtual int getNOpen() { return output.nOpenNodes; } + + MIP_xpress_wrapper(Options* opt) : options(opt) {} + virtual ~MIP_xpress_wrapper() {} + + static std::string getDescription(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getVersion(MiniZinc::SolverInstanceBase::Options* opt = NULL); + static std::string getId(void); + static std::string getName(void); + static std::vector getTags(void); + static std::vector getStdFlags(void); + static std::string needDllFlag(void); + +private: + XPRBprob problem{}; + XPRBexpr xpressObj{}; + vector variables{}; + size_t nRows{0}; + + void setUserSolutionCallback(); + void setOptions(); + void writeModelIfRequested(); + int convertConstraintType(LinConType sense); + int convertVariableType(VarType varType); + int convertObjectiveSense(int s); + XPRBctr addConstraint(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, + int mask, string rowName); + void addDummyConstraint(); +}; + +#endif // __MIP_XPRESS_WRAPPER_H__ diff --git a/include/minizinc/solvers/fzn_solverfactory.hh b/include/minizinc/solvers/fzn_solverfactory.hh new file mode 100644 index 0000000..2b34661 --- /dev/null +++ b/include/minizinc/solvers/fzn_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FZN_SOLVERFACTORY_HH__ +#define __MINIZINC_FZN_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class FZN_SolverFactoryInitialiser { +public: + FZN_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/fzn_solverinstance.hh b/include/minizinc/solvers/fzn_solverinstance.hh new file mode 100644 index 0000000..79e0c6d --- /dev/null +++ b/include/minizinc/solvers/fzn_solverinstance.hh @@ -0,0 +1,129 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_FZN_SOLVER_INSTANCE_HH__ +#define __MINIZINC_FZN_SOLVER_INSTANCE_HH__ + +#include +#include +#include +//#include + +namespace MiniZinc { + +class FZNSolverOptions : public SolverInstanceBase::Options { +public: + std::string fzn_solver; + std::string backend; + std::vector fzn_flags; + int numSols = 1; + bool allSols = false; + std::string parallel; + int fzn_time_limit_ms = 0; + int solver_time_limit_ms = 0; + bool fzn_sigint = false; + + bool fzn_needs_paths = false; + bool fzn_output_passthrough = false; + + bool supports_a = false; + bool supports_n = false; + bool supports_f = false; + bool supports_p = false; + bool supports_s = false; + bool supports_r = false; + bool supports_v = false; + bool supports_t = false; + std::vector fzn_solver_flags; +}; + +struct VarStore { + KeepAlive int_var = nullptr; + KeepAlive bool_var = nullptr; + bool used_int = false; + bool used_bool = false; + VarStore(KeepAlive iv, KeepAlive bv, bool ui, bool ub) + : int_var(iv), bool_var(bv), used_int(ui), used_bool(ub){}; +}; + +class FZNSolverInstance : public SolverInstanceBase, public TrailableSolverInstance { +private: + std::string _fzn_solver; + std::unordered_map vdmap; + std::unordered_set uninitialised_vars; + Model* _model; + Env env; + std::vector stack; + +public: + FZNSolverInstance(std::ostream& log, SolverInstanceBase::Options* opt); + + ~FZNSolverInstance(void); + + Status next(void) override { return SolverInstance::ERROR; } + + Status solve(void) override; + void printStatistics(bool fLegend = 0) override; + void printFlatZincToFile(std::string filename); + void printOutputToFile(std::string filename); + void outputArray(Vec* arr); + void outputDict(Variable* start); + + void processFlatZinc(void) override; + + void addFunction(FunctionI* fi) override; + void addConstraint(const std::vector& bs, Constraint* c) override; + void addVariable(Variable* var, bool isOuput) override; + Val getSolutionValue(Variable* var) override; + + // Able to return to the solver into a position where no search decisions + // have been made. SolverInstance must allow addDefinition calls after + // restart call. + void restart() override{}; + + // Returns the number of stored states + size_t states() override { return stack.size(); } + + // Able to store the current solver state to the Trail. + void pushState() override; + + // Able to restore the last solver state that was saved to the Trail. + void popState() override; + + void resetSolver(void) override; + +protected: + void createFunctionItems(); + Expression* getSolutionValue(Id* id); + Expression* val_to_expr(Type ty, Val v); + VarDecl* add_var_to_model(int ident, TypeInst* ti, bool view = false, bool isOutput = false); +}; + +class FZN_SolverFactory : public SolverFactory { +protected: + virtual SolverInstanceBase* doCreateSI(std::ostream& log, SolverInstanceBase::Options* opt); + +public: + FZN_SolverFactory(void); + virtual SolverInstanceBase::Options* createOptions(void); + virtual std::string getDescription(SolverInstanceBase::Options* opt = NULL); + virtual std::string getVersion(SolverInstanceBase::Options* opt = NULL); + virtual std::string getId(void); + virtual bool processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv); + virtual void printHelp(std::ostream& os); + void setAcceptedFlags(SolverInstanceBase::Options* opt, + const std::vector& flags); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/geas/geas_constraints.hh b/include/minizinc/solvers/geas/geas_constraints.hh new file mode 100644 index 0000000..d73c5ae --- /dev/null +++ b/include/minizinc/solvers/geas/geas_constraints.hh @@ -0,0 +1,158 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GEAS_CONSTRAINTS_HH__ +#define __MINIZINC_GEAS_CONSTRAINTS_HH__ + +#include +#include + +namespace MiniZinc { +namespace GeasConstraints { + +#define PosterImpl(X) void X(SolverInstanceBase& s, const Definition* ce) + +PosterImpl(p_mk_intvar); + +/* Integer Comparison Constraints */ +PosterImpl(p_int_eq); +PosterImpl(p_int_le); +PosterImpl(p_int_lt); + +/* Integer Arithmetic Constraints */ +PosterImpl(p_int_abs); +PosterImpl(p_int_times); +PosterImpl(p_int_div); +PosterImpl(p_int_max); +PosterImpl(p_int_min); + +/* Integer Linear Constraints */ +PosterImpl(p_int_lin_eq); +PosterImpl(p_int_lin_ne); +PosterImpl(p_int_lin_le); +PosterImpl(p_int_lin_eq_imp); +PosterImpl(p_int_lin_ne_imp); +PosterImpl(p_int_lin_le_imp); +PosterImpl(p_int_lin_eq_reif); +PosterImpl(p_int_lin_ne_reif); +PosterImpl(p_int_lin_le_reif); + +/* Boolean Comparison Constraints */ +PosterImpl(p_bool_eq); +PosterImpl(p_bool_ne); +PosterImpl(p_bool_le); +PosterImpl(p_bool_lt); +PosterImpl(p_bool_eq_imp); +PosterImpl(p_bool_ne_imp); +PosterImpl(p_bool_le_imp); +PosterImpl(p_bool_lt_imp); +PosterImpl(p_bool_eq_reif); +PosterImpl(p_bool_ne_reif); +PosterImpl(p_bool_le_reif); +PosterImpl(p_bool_lt_reif); + +/* Boolean Arithmetic Constraints */ +PosterImpl(p_bool_or); +PosterImpl(p_bool_and); +PosterImpl(p_bool_xor); +PosterImpl(p_bool_not); +PosterImpl(p_bool_or_imp); +PosterImpl(p_bool_and_imp); +PosterImpl(p_bool_xor_imp); + +PosterImpl(p_bool_clause); +PosterImpl(p_array_bool_or); +PosterImpl(p_array_bool_and); +PosterImpl(p_bool_clause_imp); +PosterImpl(p_array_bool_or_imp); +PosterImpl(p_array_bool_and_imp); +PosterImpl(p_bool_clause_reif); + +/* Boolean Linear Constraints */ +PosterImpl(p_bool_lin_eq); +PosterImpl(p_bool_lin_ne); +PosterImpl(p_bool_lin_le); +PosterImpl(p_bool_lin_eq_imp); +PosterImpl(p_bool_lin_ne_imp); +PosterImpl(p_bool_lin_le_imp); +PosterImpl(p_bool_lin_lt_imp); +PosterImpl(p_bool_lin_eq_reif); +PosterImpl(p_bool_lin_ne_reif); +PosterImpl(p_bool_lin_le_reif); + +/* Coercion Constraints */ +PosterImpl(p_bool2int); + +/* Element Constraints */ +PosterImpl(p_array_int_element); +PosterImpl(p_array_bool_element); +PosterImpl(p_array_int_maximum); +PosterImpl(p_array_int_minimum); +PosterImpl(p_array_var_int_element); +PosterImpl(p_array_var_bool_element); + +/* Global Constraints */ +PosterImpl(p_all_different); +PosterImpl(p_all_different_except_0); +PosterImpl(p_at_most); +PosterImpl(p_at_most1); +PosterImpl(p_cumulative); +PosterImpl(p_disjunctive); +PosterImpl(p_global_cardinality); +PosterImpl(p_table_int); + +/**** NOT YET SUPPORTED: ****/ + +/* Boolean Arithmetic Constraints */ +// PosterImpl(p_array_bool_xor); +// PosterImpl(p_array_bool_xor_imp); + +/* Floating Point Comparison Constraints */ +// PosterImpl(p_float_eq); +// PosterImpl(p_float_ne); +// PosterImpl(p_float_le); +// PosterImpl(p_float_lt); +// PosterImpl(p_float_eq_reif); +// PosterImpl(p_float_le_reif) +// PosterImpl(p_float_lt_reif); + +/* Floating Point Arithmetic Constraints */ +// PosterImpl(p_float_times); +// PosterImpl(p_float_div); +// PosterImpl(p_float_plus) ; +// PosterImpl(p_float_sqrt); +// PosterImpl(p_float_abs);; +// PosterImpl(p_float_max); +// PosterImpl(p_float_min); +// PosterImpl(p_float_acos); +// PosterImpl(p_float_asin); +// PosterImpl(p_float_atan); +// PosterImpl(p_float_cos); +// PosterImpl(p_float_exp); +// PosterImpl(p_float_sin); +// PosterImpl(p_float_tan); +// PosterImpl(p_float_ln); +// PosterImpl(p_float_log10); +// PosterImpl(p_float_log2); + +/* Floating Linear Constraints */ +// PosterImpl(p_float_lin_eq); +// PosterImpl(p_float_lin_eq_reif); +// PosterImpl(p_float_lin_le); +// PosterImpl(p_float_lin_le_reif); + +/* Coercion Constraints */ +// PosterImpl(p_int2float); + +} // namespace GeasConstraints +} // namespace MiniZinc + +#endif // __MINIZINC_GEAS_CONSTRAINTS_HH__ \ No newline at end of file diff --git a/include/minizinc/solvers/geas_solverfactory.hh b/include/minizinc/solvers/geas_solverfactory.hh new file mode 100644 index 0000000..c4b4244 --- /dev/null +++ b/include/minizinc/solvers/geas_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GEAS_SOLVERFACTORY_HH__ +#define __MINIZINC_GEAS_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class Geas_SolverFactoryInitialiser { +public: + Geas_SolverFactoryInitialiser(); +}; +} // namespace MiniZinc + +#endif // __MINIZINC_GEAS_SOLVERFACTORY_HH__ diff --git a/include/minizinc/solvers/geas_solverinstance.hh b/include/minizinc/solvers/geas_solverinstance.hh new file mode 100644 index 0000000..5881a76 --- /dev/null +++ b/include/minizinc/solvers/geas_solverinstance.hh @@ -0,0 +1,164 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GEAS_SOLVER_INSTANCE_HH__ +#define __MINIZINC_GEAS_SOLVER_INSTANCE_HH__ + +#include +#include +#include + +#include + +namespace MiniZinc { + +class GeasOptions : public SolverInstanceBase::Options { +public: + bool all_solutions = false; + int conflicts = 0; + bool free_search = false; + int nr_solutions = 1; + int obj_probe_limit = 0; + bool statistics = false; + std::chrono::milliseconds time = std::chrono::milliseconds(0); +}; + +class GeasVariable { +public: + enum Type { BOOL_TYPE, FLOAT_TYPE, INT_TYPE }; + +protected: + Type _t; // Type of the variable + union { + geas::patom_t _bv; + geas::fp::fpvar _fv; + geas::intvar _iv; + }; + +public: + explicit GeasVariable(const geas::patom_t& bv) : _t(BOOL_TYPE), _bv(bv){}; + explicit GeasVariable(const geas::fp::fpvar& fv) : _t(FLOAT_TYPE), _fv(fv){}; + explicit GeasVariable(const geas::intvar& iv) : _t(INT_TYPE), _iv(iv){}; + + GeasVariable(const GeasVariable& gv) : _t(gv._t) { + switch (_t) { + case BOOL_TYPE: + _bv = gv._bv; + break; + case FLOAT_TYPE: + _fv = gv._fv; + break; + case INT_TYPE: + _iv = gv._iv; + break; + } + } + + bool isBool() const { return _t == BOOL_TYPE; } + bool isFloat() const { return _t == FLOAT_TYPE; } + bool isInt() const { return _t == INT_TYPE; } + + geas::patom_t boolVar() { return _bv; } + geas::fp::fpvar floatVar() { return _fv; } + geas::intvar intVar() { return _iv; } +}; + +class GeasTypes { +public: + typedef GeasVariable Variable; + typedef MiniZinc::Statistics Statistics; +}; + +class GeasSolverInstance : public SolverInstanceImpl, public TrailableSolverInstance { +public: + GeasSolverInstance(std::ostream& log, SolverInstanceBase::Options* opt); + ~GeasSolverInstance() override = default; + void processFlatZinc() override; + geas::solver_data* solver_data() { return _solver.data; } + geas::solver& solver() { return _solver; } + + Status solve() override; + Status next() override { return SolverInstance::ERROR; } // TODO: Implement + void resetSolver() override; + + Expression* getSolutionValue(Id* id) override; + void printStatistics(bool fLegend) override; + + void addDefinition(const std::vector& bs, Definition* def) override; + Val getSolutionValue(Definition* def) override; + + // MiniZinc to Geas conversions + static bool asBool(const Val& val) { return val().toInt() != 0; } + vec asBoolVec(const Val& val); + geas::patom_t asBoolVar(const Val& val); + vec asBoolVarVec(const Val& val); + vec asIntVec(const Val& val); + static int asInt(const Val& val) { return val().toInt(); } + geas::intvar asIntVar(const Val& val); + vec asIntVarVec(const Val& val); + + void insertVar(const Definition* def, GeasVariable gv) { + _variableMap.insert({def->timestamp(), gv}); + }; + + // TODO: create only when necessary or use Geas internal + geas::intvar zero; + + // Return to the solver into a position where no search decisions have been made. SolverInstance + // must allow addDefinition calls after restart call. + void restart() override { _solver.restart(); }; + + // Returns the number of stored states + size_t states() override { return stack.size(); } + + // Able to store the current solver state to the Trail. + void pushState() override; + + // Able to restore the last solver state that was saved to the Trail. + void popState() override; + + geas::patom_t currentState(); + +protected: + geas::solver _solver; + + std::vector stack; + + SolveI::SolveType _obj_type = SolveI::ST_SAT; + std::unique_ptr _obj_var; + + GeasTypes::Variable& resolveVar(Definition* val); + bool addSolutionNoGood(); + + void registerConstraint(std::string name, poster p); + void registerConstraints(); +}; + +class Geas_SolverFactory : public SolverFactory { +public: + Geas_SolverFactory(); + SolverInstanceBase::Options* createOptions() override; + SolverInstanceBase* doCreateSI(std::ostream& log, SolverInstanceBase::Options* opt) override; + + std::string getDescription(SolverInstanceBase::Options* opt) override { + return "Elsie Geas - Another Lazy Clause Generation Solver"; + }; + std::string getVersion(SolverInstanceBase::Options* opt) override { return "0.0.1"; } + std::string getId() override { return "org.minizinc.geas"; } + + bool processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv) override; + void printHelp(std::ostream& os) override; +}; + +} // namespace MiniZinc + +#endif // __MINIZINC_GEAS_SOLVER_INSTANCE_HH__ diff --git a/include/minizinc/solvers/gecode/fzn_space.hh b/include/minizinc/solvers/gecode/fzn_space.hh new file mode 100644 index 0000000..c56f6bd --- /dev/null +++ b/include/minizinc/solvers/gecode/fzn_space.hh @@ -0,0 +1,103 @@ +/* + * Main authors: + * Kevin Leo + * Andrea Rendl + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GECODE_FZN_SPACE_HH__ +#define __MINIZINC_GECODE_FZN_SPACE_HH__ + +#include +#include +#include + +#ifdef GECODE_HAS_SET_VARS +#include +#endif + +#ifdef GECODE_HAS_FLOAT_VARS +#include +#endif + +#include + +namespace MiniZinc { + +class FznSpace : public Gecode::Space { +public: + /// The integer variables + std::vector iv; + /// The introduced integer variables + Gecode::IntVarArray iv_aux; + /// Indicates whether an integer variable is introduced by mzn2fzn + std::vector iv_introduced; + /// Indicates whether an integer variable is defined + std::vector iv_defined; + /// The Boolean variables + std::vector bv; + /// The introduced Boolean variables + Gecode::BoolVarArray bv_aux; + /// Indicates whether a Boolean variable is introduced by mzn2fzn + std::vector bv_introduced; + /// Indicates whether a Boolean variable is defined + std::vector bv_defined; +#ifdef GECODE_HAS_SET_VARS + /// The set variables + std::vector sv; + /// The introduced set variables + Gecode::SetVarArray sv_aux; + /// Indicates whether a set variable is introduced by mzn2fzn + std::vector sv_introduced; + /// Indicates whether a set variable is introduced by mzn2fzn + std::vector sv_defined; +#endif +#ifdef GECODE_HAS_FLOAT_VARS + /// The float variables + std::vector fv; + /// The introduced float variables + Gecode::FloatVarArray fv_aux; + /// Indicates whether a float variable is introduced by mzn2fzn + std::vector fv_introduced; + /// Indicates whether a float variable is defined + std::vector fv_defined; +#endif + /// Indicates if the objective variable is integer (float otherwise) + bool _optVarIsInt; + /// Index of the variable to optimize + int _optVarIdx; + /// Whether the introduced variables still need to be copied + bool _copyAuxVars; + /// solve type (SAT, MIN or MAX) + MiniZinc::SolveI::SolveType _solveType; + + /// copy constructor + FznSpace(FznSpace&); + /// standard constructor + FznSpace(void) : _optVarIsInt(true), _optVarIdx(-1), _copyAuxVars(true){}; + ~FznSpace(void) {} + + /// get the index of the Boolean variable in bv; return -1 if not exists + int getBoolAliasIndex(Gecode::BoolVar bvar) { + for (unsigned int i = 0; i < bv.size(); i++) { + if (bv[i].varimp() == bvar.varimp()) { + // std::cout << "DEBUG: settings bool alias of variable to index " << i << std::endl; + return i; + } + } + return -1; // we should have found the boolvar in bv + } + +protected: + /// Implement optimization + virtual void constrain(const Space& s); + /// Copy function + virtual Gecode::Space* copy(void); +}; + +} // namespace MiniZinc +#endif diff --git a/include/minizinc/solvers/gecode/gecode_constraints.hh b/include/minizinc/solvers/gecode/gecode_constraints.hh new file mode 100644 index 0000000..ba8acdb --- /dev/null +++ b/include/minizinc/solvers/gecode/gecode_constraints.hh @@ -0,0 +1,288 @@ +/* + * Main authors: + * Kevin Leo + * Andrea Rendl + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GECODE_CONSTRAINTS_HH__ +#define __MINIZINC_GECODE_CONSTRAINTS_HH__ + +#include +#include + +#include +#include + +namespace MiniZinc { + +namespace GecodeConstraints { + +void p_mk_intvar(SolverInstanceBase& s, const Variable* var, bool isOutput); + +#define PosterImpl(X) void X(SolverInstanceBase& s, const Constraint* ce) + +PosterImpl(p_distinct); +PosterImpl(p_distinctOffset); +PosterImpl(p_all_equal); +void p_int_CMP(GecodeSolverInstance& s, Gecode::IntRelType irt, const Constraint* ce); +PosterImpl(p_int_eq); +PosterImpl(p_int_ne); +PosterImpl(p_int_ge); +PosterImpl(p_int_gt); +PosterImpl(p_int_le); +PosterImpl(p_int_lt); +void p_int_CMP_reif(GecodeSolverInstance& s, Gecode::IntRelType irt, Gecode::ReifyMode rm, + const Constraint* call); +///* Comparisons */ +PosterImpl(p_int_eq_reif); +PosterImpl(p_int_ne_reif); +PosterImpl(p_int_ge_reif); +PosterImpl(p_int_gt_reif); +PosterImpl(p_int_le_reif); +PosterImpl(p_int_lt_reif); +PosterImpl(p_int_eq_imp); +PosterImpl(p_int_ne_imp); +PosterImpl(p_int_ge_imp); +PosterImpl(p_int_gt_imp); +PosterImpl(p_int_le_imp); +PosterImpl(p_int_lt_imp); +void p_int_lin_CMP(GecodeSolverInstance& s, Gecode::IntRelType irt, const Constraint* call); +void p_int_lin_CMP_reif(GecodeSolverInstance& s, Gecode::IntRelType irt, Gecode::ReifyMode rm, + const Constraint* call); +PosterImpl(p_int_lin_eq); +PosterImpl(p_int_lin_eq_reif); +PosterImpl(p_int_lin_eq_imp); +PosterImpl(p_int_lin_le); +PosterImpl(p_int_lin_le_reif); +PosterImpl(p_int_lin_le_imp); +PosterImpl(p_int_lin_lt); +PosterImpl(p_int_lin_lt_reif); +PosterImpl(p_int_lin_lt_imp); +PosterImpl(p_int_lin_ge); +PosterImpl(p_int_lin_ge_reif); +PosterImpl(p_int_lin_ge_imp); +PosterImpl(p_int_lin_gt); +PosterImpl(p_int_lin_gt_reif); +PosterImpl(p_int_lin_gt_imp); +void p_bool_lin_CMP(GecodeSolverInstance& s, Gecode::IntRelType irt, const Constraint* call); +void p_bool_lin_CMP_reif(GecodeSolverInstance& s, Gecode::IntRelType irt, Gecode::ReifyMode rm, + const Constraint* call); +PosterImpl(p_bool_lin_eq); +PosterImpl(p_bool_lin_eq_reif); +PosterImpl(p_bool_lin_eq_imp); +PosterImpl(p_bool_lin_ne); +PosterImpl(p_bool_lin_ne_reif); +PosterImpl(p_bool_lin_ne_imp); +PosterImpl(p_bool_lin_le); +PosterImpl(p_bool_lin_le_reif); +PosterImpl(p_bool_lin_le_imp); +PosterImpl(p_bool_lin_lt); +PosterImpl(p_bool_lin_lt_reif); +PosterImpl(p_bool_lin_lt_imp); +PosterImpl(p_bool_lin_ge); +PosterImpl(p_bool_lin_ge_reif); +PosterImpl(p_bool_lin_ge_imp); +PosterImpl(p_bool_lin_gt); +PosterImpl(p_bool_lin_gt_reif); +PosterImpl(p_bool_lin_gt_imp); + +///* arithmetic constraints */ +// PosterImpl(p_int_plus); +PosterImpl(p_int_sum); +// PosterImpl(p_int_minus); +PosterImpl(p_int_times); +PosterImpl(p_int_div); +PosterImpl(p_int_mod); +PosterImpl(p_int_min); +PosterImpl(p_int_max); +PosterImpl(p_int_negate); + +///* Boolean constraints */ +void p_bool_CMP(GecodeSolverInstance& s, Gecode::IntRelType irt, const Constraint* call); +void p_bool_CMP_reif(GecodeSolverInstance& s, Gecode::IntRelType irt, Gecode::ReifyMode rm, + const Constraint* call); +PosterImpl(p_bool_eq); +PosterImpl(p_bool_eq_reif); +PosterImpl(p_bool_eq_imp); +PosterImpl(p_bool_ne); +PosterImpl(p_bool_ne_reif); +PosterImpl(p_bool_ne_imp); +PosterImpl(p_bool_ge); +PosterImpl(p_bool_ge_reif); +PosterImpl(p_bool_ge_imp); +PosterImpl(p_bool_le); +PosterImpl(p_bool_le_reif); +PosterImpl(p_bool_le_imp); +PosterImpl(p_bool_gt); +PosterImpl(p_bool_gt_reif); +PosterImpl(p_bool_gt_imp); +PosterImpl(p_bool_lt); +PosterImpl(p_bool_lt_reif); +PosterImpl(p_bool_lt_imp); +PosterImpl(p_bool_or); +PosterImpl(p_bool_or_imp); +PosterImpl(p_bool_and); +PosterImpl(p_forall); +PosterImpl(p_bool_and_imp); +PosterImpl(p_array_bool_and); +PosterImpl(p_array_bool_and_imp); +PosterImpl(p_array_bool_or); +// PosterImpl(p_exists); +PosterImpl(p_array_bool_or_imp); +PosterImpl(p_array_bool_xor); +PosterImpl(p_array_bool_xor_imp); +PosterImpl(p_array_bool_clause); +PosterImpl(p_array_bool_clause_reif); +PosterImpl(p_array_bool_clause_imp); +PosterImpl(p_bool_xor); +PosterImpl(p_bool_xor_imp); +PosterImpl(p_bool_l_imp); +PosterImpl(p_bool_r_imp); +// PosterImpl(p_bool_not); + +///* element constraints */ +PosterImpl(p_array_int_element); +PosterImpl(p_array_bool_element); + +///* coercion constraints */ +PosterImpl(p_bool2int); +PosterImpl(p_int_in); +PosterImpl(p_int_in_reif); +PosterImpl(p_int_in_imp); + +///* constraints from the standard library */ +PosterImpl(p_abs); +PosterImpl(p_array_int_lt); +PosterImpl(p_array_int_lq); +PosterImpl(p_array_bool_lt); +PosterImpl(p_array_bool_lq); +PosterImpl(p_count); +PosterImpl(p_count_reif); +PosterImpl(p_count_imp); +void count_rel(Gecode::IntRelType irt, SolverInstanceBase& s, const Constraint* call); +PosterImpl(p_at_most); +PosterImpl(p_at_least); +PosterImpl(p_bin_packing_load); +PosterImpl(p_global_cardinality); +PosterImpl(p_global_cardinality_closed); +PosterImpl(p_global_cardinality_low_up); +PosterImpl(p_global_cardinality_low_up_closed); +PosterImpl(p_minimum); +PosterImpl(p_maximum); +PosterImpl(p_minimum_arg); +PosterImpl(p_maximum_arg); +PosterImpl(p_regular); +PosterImpl(p_sort); +PosterImpl(p_inverse_offsets); +PosterImpl(p_increasing_int); +PosterImpl(p_increasing_bool); +PosterImpl(p_decreasing_int); +PosterImpl(p_decreasing_bool); +PosterImpl(p_table_int); +PosterImpl(p_table_bool); +PosterImpl(p_cumulatives); +PosterImpl(p_among_seq_int); +PosterImpl(p_among_seq_bool); +PosterImpl(p_schedule_unary); +PosterImpl(p_schedule_unary_optional); +PosterImpl(p_cumulative_opt); +PosterImpl(p_circuit); +PosterImpl(p_circuit_cost_array); +PosterImpl(p_circuit_cost); +PosterImpl(p_nooverlap); +PosterImpl(p_precede); +PosterImpl(p_nvalue); +PosterImpl(p_among); +PosterImpl(p_member_int); +PosterImpl(p_member_int_reif); +PosterImpl(p_member_bool); +PosterImpl(p_member_bool_reif); + +#ifdef GECODE_HAS_FLOAT_VARS +PosterImpl(p_int2float); +void p_float_lin_cmp(GecodeSolverInstance& s, Gecode::FloatRelType frt, const Definition* ce); +void p_float_lin_cmp_reif(GecodeSolverInstance& s, Gecode::FloatRelType frt, const Definition* ce); +PosterImpl(p_float_lin_eq); +PosterImpl(p_float_lin_eq_reif); +PosterImpl(p_float_lin_le); +PosterImpl(p_float_lin_le_reif); +PosterImpl(p_float_times); +PosterImpl(p_float_div); +PosterImpl(p_float_plus); +PosterImpl(p_float_sqrt); +PosterImpl(p_float_abs); +PosterImpl(p_float_eq); +PosterImpl(p_float_eq_reif); +PosterImpl(p_float_le); +PosterImpl(p_float_le_reif); +PosterImpl(p_float_max); +PosterImpl(p_float_min); +PosterImpl(p_float_lt); +PosterImpl(p_float_lt_reif); +PosterImpl(p_float_ne); +#ifdef GECODE_HAS_MPFR +PosterImpl(p_float_acos); +PosterImpl(p_float_asin); +PosterImpl(p_float_atan); +PosterImpl(p_float_cos); +PosterImpl(p_float_exp); +PosterImpl(p_float_sin); +PosterImpl(p_float_tan); +PosterImpl(p_float_ln); +PosterImpl(p_float_log10); +PosterImpl(p_float_log2); +#endif +#endif + +#ifdef GECODE_HAS_SET_VARS +PosterImpl(p_set_eq); +PosterImpl(p_set_le); +PosterImpl(p_set_lt); +PosterImpl(p_set_eq); +PosterImpl(p_set_ne); +PosterImpl(p_set_union); +PosterImpl(p_array_set_element); +PosterImpl(p_array_set_element); +PosterImpl(p_set_intersect); +PosterImpl(p_set_diff); +PosterImpl(p_set_symdiff); +PosterImpl(p_set_subset); +PosterImpl(p_set_superset); +PosterImpl(p_set_card); +PosterImpl(p_set_in); +PosterImpl(p_set_eq_reif); +PosterImpl(p_set_le_reif); +PosterImpl(p_set_lt_reif); +PosterImpl(p_set_eq_reif); +PosterImpl(p_set_ne_reif); +PosterImpl(p_set_subset_reif); +PosterImpl(p_set_superset_reif); +PosterImpl(p_set_in_reif); +PosterImpl(p_set_in_imp); +PosterImpl(p_set_disjoint); +PosterImpl(p_link_set_to_booleans); +PosterImpl(p_array_set_union); +PosterImpl(p_array_set_partition); +PosterImpl(p_set_convex); +PosterImpl(p_array_set_seq); +PosterImpl(p_array_set_seq_union); +PosterImpl(p_array_set_element_union); +PosterImpl(p_array_set_element_intersect); +PosterImpl(p_array_set_element_intersect_in); +PosterImpl(p_array_set_element_partition); +PosterImpl(p_int_set_channel); +PosterImpl(p_range); +PosterImpl(p_weights); +PosterImpl(p_inverse_set); +PosterImpl(p_precede_set); +#endif + +} // namespace GecodeConstraints +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/gecode_solverfactory.hh b/include/minizinc/solvers/gecode_solverfactory.hh new file mode 100644 index 0000000..a0c9d1c --- /dev/null +++ b/include/minizinc/solvers/gecode_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GECODE_SOLVERFACTORY_HH__ +#define __MINIZINC_GECODE_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class Gecode_SolverFactoryInitialiser { +public: + Gecode_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/gecode_solverinstance.hh b/include/minizinc/solvers/gecode_solverinstance.hh new file mode 100644 index 0000000..156a9d7 --- /dev/null +++ b/include/minizinc/solvers/gecode_solverinstance.hh @@ -0,0 +1,426 @@ +/* + * Main authors: + * Kevin Leo + * Andrea Rendl + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_GECODE_SOLVER_INSTANCE_HH__ +#define __MINIZINC_GECODE_SOLVER_INSTANCE_HH__ + +#include + +#include +#include +#include + +#undef GECODE_HAS_FLOAT_VARS +#undef GECODE_HAS_SET_VARS + +#ifdef GECODE_HAS_SET_VARS +#include +#endif + +#ifdef GECODE_HAS_FLOAT_VARS +#include +#endif + +#include +#include +#include +#include +#include + +#if GECODE_VERSION_NUMBER < 600000 +#error Gecode versions before 6.0 are not supported +#endif + +#define MZ_IntConLevel Gecode::IntPropLevel +#define MZ_ICL_VAL Gecode::IPL_VAL +#define MZ_ICL_DOM Gecode::IPL_DOM +#define MZ_ICL_BND Gecode::IPL_BND +#define MZ_ICL_DEF Gecode::IPL_DEF +#define MZ_EPK_DEF Gecode::IPL_DEF + +namespace MiniZinc { + +/* class GecodeVariable { + public: + enum vartype {BOOL_TYPE,FLOAT_TYPE,INT_TYPE,SET_TYPE}; + protected: + Gecode::VarImpBase* _var; + vartype _t; + /// the index in FznSpace::bv of the boolean variable that corresponds to the int var; if not + exists then -1 int _boolAliasIndex; public: GecodeVariable(Gecode::IntVar& x) : _var(x.varimp()), + _t(INT_TYPE), _boolAliasIndex(-1) {} GecodeVariable(Gecode::BoolVar& x) : _var(x.varimp()), + _t(BOOL_TYPE), _boolAliasIndex(-1) {} GecodeVariable(Gecode::FloatVar& x) : _var(x.varimp()), + _t(FLOAT_TYPE), _boolAliasIndex(-1) {} GecodeVariable(Gecode::SetVar& x) : _var(x.varimp()), + _t(SET_TYPE), _boolAliasIndex(-1) {} + + Gecode::IntVar intVar(void) { + assert(_t == INT_TYPE); + Gecode::Int::IntView iv(static_cast(_var)); + return Gecode::IntVar(iv); + } + + Gecode::BoolVar boolVar(void) { + assert(_t == BOOL_TYPE); + Gecode::Int::BoolView bv(static_cast(_var)); + return Gecode::BoolVar(bv); + } + + Gecode::FloatVar floatVar(void) { + assert(_t == FLOAT_TYPE); + Gecode::Float::FloatView fv(static_cast(_var)); + return Gecode::FloatVar(fv); + } + + Gecode::SetVar setVar(void) { + assert(_t == FLOAT_TYPE); + Gecode::Set::SetView sv(static_cast(_var)); + return Gecode::SetVar(sv); + } + + bool isint(void) const { + return _t == INT_TYPE; + } + + bool isbool(void) const { + return _t == BOOL_TYPE; + } + + bool isfloat(void) const { + return _t == FLOAT_TYPE; + } + + bool isset(void) const { + return _t == SET_TYPE; + } + + bool hasBoolAlias(void) { + return _boolAliasIndex >= 0; + } + + /// set the index in FznSpace::bv of the Boolean variable that corresponds to the int variable + void setBoolAliasIndex(int index) { + assert(_t == INT_TYPE); + assert(index >= 0); + _boolAliasIndex = index; + } + + int boolAliasIndex(void) { + return _boolAliasIndex; + } + + vartype t(void) const { + return _t; + } + }; */ + +class GecodeVariable { +public: + enum vartype { BOOL_TYPE, FLOAT_TYPE, INT_TYPE, SET_TYPE }; + +protected: + /// variable type + vartype _t; + /// the index in the iv/bv/fv/sv array in the space, depending the type _t + unsigned int _index; + /// the index in FznSpace::bv of the boolean variable that corresponds to the int var; if not + /// exists then -1 + int _boolAliasIndex; + +public: + GecodeVariable(vartype t, unsigned int index) : _t(t), _index(index), _boolAliasIndex(-1) {} + + bool isint(void) const { return _t == INT_TYPE; } + + bool isbool(void) const { return _t == BOOL_TYPE; } + + bool isfloat(void) const { return _t == FLOAT_TYPE; } + + bool isset(void) const { return _t == SET_TYPE; } + + bool hasBoolAlias(void) { return _boolAliasIndex >= 0; } + + /// set the index in FznSpace::bv of the Boolean variable that corresponds to the int variable + void setBoolAliasIndex(int index) { + assert(_t == INT_TYPE); + assert(index >= 0); + _boolAliasIndex = index; + } + + int boolAliasIndex(void) { return _boolAliasIndex; } + + int index(void) { return _index; } + + Gecode::IntVar& intVar(MiniZinc::FznSpace* space) { + assert(_t == INT_TYPE); + assert(_index < space->iv.size()); + return space->iv[_index]; + } + + Gecode::BoolVar& boolVar(MiniZinc::FznSpace* space) { + assert(_t == BOOL_TYPE); + assert(_index < space->bv.size()); + return space->bv[_index]; + } + +#ifdef GECODE_HAS_FLOAT_VARS + Gecode::FloatVar& floatVar(MiniZinc::FznSpace* space) { + assert(_t == FLOAT_TYPE); + assert(_index < space->fv.size()); + return space->fv[_index]; + } +#endif + +#ifdef GECODE_HAS_SET_VARS + Gecode::SetVar& setVar(MiniZinc::FznSpace* space) { + assert(_t == SET_TYPE); + assert(_index < space->sv.size()); + return space->sv[_index]; + } +#endif +}; + +class GecodeSolver { +public: + typedef GecodeVariable Variable; + typedef MiniZinc::Statistics Statistics; +}; + +class GecodeEngine; + +class GecodeOptions : public SolverInstanceBase::Options { +public: + bool allow_unbounded_vars = false; + bool only_range_domains = false; + bool sac = false; + bool shave = false; + bool verbose = false; + int pre_passes = 0; + bool statistics = false; + bool all_solutions = false; + int n_solutions = -1; + int nodes = 0; + int fails = 0; + int time = 0; + int seed = 1; + double decay = 0.5; +}; + +class GecodeSolverInstance : public SolverInstanceImpl, + public TrailableSolverInstance { +private: + bool _print_stats; + bool _only_range_domains; + bool _run_sac; + bool _run_shave; + unsigned int _pre_passes; + bool _all_solutions; + int _n_max_solutions; + int _n_found_solutions; + bool _allow_unbounded_vars; + std::vector stack; + Expression* solveExpr = nullptr; + +public: + /// the Gecode space that will be/has been solved + FznSpace* _current_space; + /// the solution (or NULL if does not exist or not yet computed) + FznSpace* _solution; + /// the variable declarations with output annotations + std::vector _varsWithOutput; + /// declaration map for processing and printing output + // typedef std::pair DE; + // ASTStringMap::t _declmap; + /// TODO: we can probably get rid of this + std::unordered_map*> arrayMap; + /// The solver engine + GecodeEngine* engine; + Gecode::Search::Options engine_options; + + GecodeSolverInstance(std::ostream& log, SolverInstanceBase::Options* opt); + virtual ~GecodeSolverInstance(void); + + Status next(void) override; + void processFlatZinc(void) override; + Status solve(void) override; + void resetSolver(void) override; + + void addConstraint(const std::vector& bs, Constraint* c) override; + void addVariable(Variable* var, bool isOutput) override; + Val getSolutionValue(Variable* var) override; + + // Able to return to the solver into a position where no search decisions + // have been made. SolverInstance must allow addDefinition calls after + // restart call. + void restart() override{}; + + // Returns the number of stored states + size_t states() override { return stack.size(); } + + // Able to store the current solver state to the Trail. + void pushState() override; + + // Able to restore the last solver state that was saved to the Trail. + void popState() override; + + // Presolve the currently loaded model, updating variables with the same + // names in the given Model* m. + bool presolve(Model* m = NULL); + bool sac(bool toFixedPoint, bool shaving); + void print_stats(); + + void processSolution(bool last_sol = false); + Expression* getSolutionValue(Id* id) override; + + Gecode::Space* getGecodeModel(void); + + // helpers for getting correct int bounds + bool valueWithinBounds(double b); + + // helper functions for processing flatzinc constraints + /// Convert \a arg (array of integers) to IntArgs + Gecode::IntArgs arg2intargs(Expression* arg, int offset = 0); + Gecode::IntArgs arg2intargs(const Val& arg, int offset = 0); + /// Convert \a arg (array of Booleans) to IntArgs + Gecode::IntArgs arg2boolargs(Expression* arg, int offset = 0); + Gecode::IntArgs arg2boolargs(const Val& arg, int offset = 0); + /// Convert \a n to IntSet + Gecode::IntSet arg2intset(EnvI& envi, Expression* sl); + Gecode::IntSet arg2intset(const Val& sl); + /// Convert \a n to IntSetArgs + Gecode::IntSetArgs arg2intsetargs(EnvI& envi, Expression* arg, int offset = 0); + /// Convert \a arg to IntVarArgs + Gecode::IntVarArgs arg2intvarargs(Expression* arg, int offset = 0); + Gecode::IntVarArgs arg2intvarargs(const Val& arg, int offset = 0); + /// Convert \a arg to BoolVarArgs + Gecode::BoolVarArgs arg2boolvarargs(Expression* a, int offset = 0, int siv = -1); + Gecode::BoolVarArgs arg2boolvarargs(const Val& a, int offset = 0, int siv = -1); + /// Convert \a n to BoolVar + Gecode::BoolVar arg2boolvar(Expression* e); + Gecode::BoolVar arg2boolvar(const Val& e); + /// Convert \a n to IntVar + Gecode::IntVar arg2intvar(Expression* e); + Gecode::IntVar arg2intvar(const Val& e); + /// Convert \a n to SetVar + Gecode::SetVar arg2setvar(Expression* e); + /// Convert \a arg to SetVarArgs + Gecode::SetVarArgs arg2setvarargs(Expression* arg, int offset = 0, int doffset = 0, + const Gecode::IntSet& od = Gecode::IntSet::empty); + /// convert \a arg to an ArrayLit (throws InternalError if not possible) + ArrayLit* arg2arraylit(Expression* arg); + /// Check if \a b is array of Booleans (or has a single integer) + bool isBoolArray(ArrayLit* a, int& singleInt); + bool isBoolArray(const Val& a, int& singleInt); +#ifdef GECODE_HAS_FLOAT_VARS + /// Convert \a n to FloatValArgs + Gecode::FloatValArgs arg2floatargs(Expression* arg, int offset = 0); + /// Convert \a n to FloatVar + Gecode::FloatVar arg2floatvar(Expression* n); + /// Convert \a n to FloatVarArgs + Gecode::FloatVarArgs arg2floatvarargs(Expression* arg, int offset = 0); +#endif + /// Convert \a ann to IntConLevel + + MZ_IntConLevel ann2icl(const Annotation& ann); + MZ_IntConLevel ann2icl(const Val& ann) { return MZ_ICL_DEF; } + + /// convert the annotation \a s int variable selection to the respective Gecode var selection + Gecode::TieBreak ann2ivarsel(std::string s, Gecode::Rnd& rnd, double decay); + /// convert the annotation \a s int value selection to the respective Gecode val selection + Gecode::IntValBranch ann2ivalsel(std::string s, std::string& r0, std::string& r1, + Gecode::Rnd& rnd); + /// convert assign value selection + Gecode::IntAssign ann2asnivalsel(std::string s, Gecode::Rnd& rnd); + + Gecode::TieBreak ann2bvarsel(std::string s, Gecode::Rnd& rnd, + double decay); + /// convert the annotation \a s int value selection to the respectbve Gecode val selection + Gecode::BoolValBranch ann2bvalsel(std::string s, std::string& r0, std::string& r1, + Gecode::Rnd& rnd); + /// convert assign value selection + Gecode::BoolAssign ann2asnbvalsel(std::string s, Gecode::Rnd& rnd); + +#ifdef GECODE_HAS_SET_VARS + Gecode::SetVarBranch ann2svarsel(std::string s, Gecode::Rnd& rnd, double decay); + Gecode::SetValBranch ann2svalsel(std::string s, std::string r0, std::string r1, Gecode::Rnd& rnd); +#endif +#ifdef GECODE_HAS_FLOAT_VARS + Gecode::TieBreak ann2fvarsel(std::string s, Gecode::Rnd& rnd, + double decay); + Gecode::FloatValBranch ann2fvalsel(std::string s, std::string r0, std::string r1); +#endif + /// Returns the VarDecl of \a expr and throws an InternalError if not possible + VarDecl* getVarDecl(Expression* expr); + /// Returns the VarDecl of \a aa + VarDecl* resolveArrayAccess(ArrayAccess* aa); + /// Returns the VarDecl of \a array at index \a index + VarDecl* resolveArrayAccess(VarDecl* array, int index); + + /// Returns the GecodeVariable representing the Id, VarDecl or ArrayAccess + GecodeSolver::Variable resolveVar(Expression* e); + GecodeSolver::Variable resolveVar(Variable* var); + + Gecode::BoolVar reifyVar(const Variable* var); + + /// Inserts variable gv into _variableMap with key id + inline void insertVar(const Variable* var, GecodeVariable gv) { + Val va = Val::follow_alias(Val(var)); + if (va.isVar()) { + _variableMap.back().emplace(va.timestamp(), gv); + } + } + +protected: + void registerConstraints(void); + void registerConstraint(std::string name, poster p); + + /// creates the gecode branchers // TODO: what is decay, ignoreUnknown -> do we need all the args? + void createBranchers(Annotation& ann, Expression* additionalAnn, int seed, double decay, + bool ignoreUnknown, std::ostream& err); + void prepareEngine(void); + void setSearchStrategyFromAnnotation( + std::vector flatAnn, std::vector& iv_searched, + std::vector& bv_searched, +#ifdef GECODE_HAS_SET_VARS + std::vector& sv_searched, +#endif +#ifdef GECODE_HAS_FLOAT_VARS + std::vector& fv_searched, +#endif + Gecode::TieBreak& def_int_varsel, Gecode::IntValBranch& def_int_valsel, + Gecode::TieBreak& def_bool_varsel, + Gecode::BoolValBranch& def_bool_valsel, + +#ifdef GECODE_HAS_SET_VARS + Gecode::SetVarBranch& def_set_varsel, Gecode::SetValBranch& def_set_valsel, +#endif +#ifdef GECODE_HAS_FLOAT_VARS + Gecode::TieBreak& def_float_varsel, + Gecode::FloatValBranch& def_float_valsel, +#endif + Gecode::Rnd& rnd, double decay, bool ignoreUnknown, std::ostream& err); +}; + +class Gecode_SolverFactory : public SolverFactory { +public: + Gecode_SolverFactory(void); + SolverInstanceBase::Options* createOptions(void); + SolverInstanceBase* doCreateSI(std::ostream& log, SolverInstanceBase::Options* opt); + std::string getDescription(SolverInstanceBase::Options* opt = NULL); + std::string getVersion(SolverInstanceBase::Options* opt = NULL); + std::string getId() { return "org.minizinc.gecode_presolver"; } + virtual bool processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv); + void printHelp(std::ostream& os); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/incremental_interfaces.hh b/include/minizinc/solvers/incremental_interfaces.hh new file mode 100644 index 0000000..3275d81 --- /dev/null +++ b/include/minizinc/solvers/incremental_interfaces.hh @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#pragma once + +#include + +#include + +#ifdef ERROR // Microsoft. +#undef ERROR +#endif + +namespace MiniZinc { + +class RestartableSolverInstance { +public: + // Able to return to the solver into a position where no search decisions + // have been made. SolverInstance must allow addDefinition calls after + // restart call. + virtual void restart() = 0; +}; + +class TrailableSolverInstance : public RestartableSolverInstance { +public: + // Returns the number of stored states + virtual size_t states() = 0; + + // Able to store the current solver state to the Trail. + virtual void pushState() = 0; + + // Able to restore the last solver state that was saved to the Trail. + virtual void popState() = 0; +}; + +} // namespace MiniZinc diff --git a/include/minizinc/solvers/mzn_solverfactory.hh b/include/minizinc/solvers/mzn_solverfactory.hh new file mode 100644 index 0000000..16f3b01 --- /dev/null +++ b/include/minizinc/solvers/mzn_solverfactory.hh @@ -0,0 +1,22 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MZN_SOLVERFACTORY_HH__ +#define __MINIZINC_MZN_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class MZN_SolverFactoryInitialiser { +public: + MZN_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/mzn_solverinstance.hh b/include/minizinc/solvers/mzn_solverinstance.hh new file mode 100644 index 0000000..e898c26 --- /dev/null +++ b/include/minizinc/solvers/mzn_solverinstance.hh @@ -0,0 +1,74 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MZN_SOLVER_INSTANCE_HH__ +#define __MINIZINC_MZN_SOLVER_INSTANCE_HH__ + +#include + +namespace MiniZinc { + +class MZNSolverOptions : public SolverInstanceBase::Options { +public: + std::string mzn_solver; + std::vector mzn_flags; + int numSols = 1; + bool allSols = false; + std::string parallel; + int mzn_time_limit_ms = 0; + int solver_time_limit_ms = 0; + bool mzn_sigint = false; + bool supports_t = false; + std::vector mzn_solver_flags; +}; + +class MZNSolverInstance : public SolverInstanceBase { +private: + std::string _mzn_solver; + +public: + MZNSolverInstance(std::ostream& log, SolverInstanceBase::Options* opt); + + ~MZNSolverInstance(void); + + Status next(void) override { return SolverInstance::ERROR; } + + Status solve(void) override; + + void processFlatZinc(void) override; + // TODO: + void addConstraint(const std::vector& bs, Constraint* c) override{}; + void addVariable(Variable* var, bool isOuput) override{}; + Val getSolutionValue(Variable* var) override { return Val(); }; + + void resetSolver(void) override; +}; + +class MZN_SolverFactory : public SolverFactory { +protected: + virtual SolverInstanceBase* doCreateSI(std::ostream& log, SolverInstanceBase::Options* opt); + +public: + MZN_SolverFactory(void); + virtual SolverInstanceBase::Options* createOptions(void); + virtual std::string getDescription(SolverInstanceBase::Options* opt = NULL); + virtual std::string getVersion(SolverInstanceBase::Options* opt = NULL); + virtual std::string getId(void); + virtual bool processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv); + virtual void printHelp(std::ostream& os); + void setAcceptedFlags(SolverInstanceBase::Options* opt, + const std::vector& flags); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/nl/nl_components.hh b/include/minizinc/solvers/nl/nl_components.hh new file mode 100644 index 0000000..13b8147 --- /dev/null +++ b/include/minizinc/solvers/nl/nl_components.hh @@ -0,0 +1,500 @@ +#ifndef __MINIZINC_NL_COMPONENTS_HH__ +#define __MINIZINC_NL_COMPONENTS_HH__ + +//#include + +#include +#include +#include +#include +#include + +using namespace std; + +/* A NL File is composed of a header and several segments. +Adding items in the nl file is done through adding segment (or adding item in a segment). +As for the header, segment are printable. +Segment are identified by a prefix, which should be one of (taken from table 13 in 'writing nl +files'): F imported function description S suffix values V defined variable definition (must +precede V,C,L,O segments where used) (yes, I know, "V must preceed V"...) C algebraic constraint +body L logical constraint expression O objective function d dual initial guess x primal +initial guess r bounds on algebraic constraint bodies (“ranges”), can only appears once b bounds +on variable, can only appears once k Jacobian column counts (must precede all J segments) J +Jacobian sparsity, linear terms G Gradient sparsity, linear terms +*/ + +namespace MiniZinc { + +// --- --- --- Tooling + +/** Exception when translating + * Code mostly taken from https://www.softwariness.com/articles/assertions-in-cpp/ + */ +class NLException : public exception { +public: + // --- --- --- Fields + const char* expression; + const char* file; + int line; + string message; + string report; + + /** Exception constructor. Use with the macro assert/should_not_happen. + * If not, WARNING: stream must be a std::ostringstream& + * We only use a ostreamé so we can use the standard "<<" operator, which is returning a ostream& + */ + NLException(const char* expression, const char* file, int line, ostream& stream) + : expression(expression), file(file), line(line) { + message = static_cast(stream).str(); + std::ostringstream outputStream; + + if (expression == NULL) { + outputStream << "Something should not have happen in file '" << file << "' line " << line + << ". Message:" << endl; + if (!message.empty()) { + outputStream << message << endl; + } else { + outputStream << "No message provided..." << endl; + } + } else { + string expressionString = expression; + if (expressionString == "false" || expressionString == "0" || expressionString == "FALSE") { + outputStream << "Unreachable code assertion"; + } else { + outputStream << "Assertion '" << expression << "'"; + } + outputStream << " failed in file '" << file << "' line " << line << endl; + } + outputStream << "Note: the NL component is still in development!" << endl; + report = outputStream.str(); + } + + /** Exception interface */ + virtual const char* what() const noexcept { return report.c_str(); } + + ~NLException() noexcept = default; +}; + +#ifdef assert +#undef assert +#endif + +/** Should not happen macro */ +#define should_not_happen(MESSAGE) \ + do { \ + ostringstream oss; \ + oss << MESSAGE; \ + throw NLException(NULL, __FILE__, __LINE__, oss); \ + } while (false) + +/* CMake febug build flag: double negation... because... ? */ +#ifndef NDEBUG +#define DEBUG_MSG(STR) \ + do { \ + std::cerr << "%[NL DEBUG] " << STR << endl; \ + } while (false) +#define assert(EXPRESSION) \ + do { \ + if (!(EXPRESSION)) { \ + ostringstream oss; \ + throw NLException(#EXPRESSION, __FILE__, __LINE__, oss); \ + } \ + } while (false) +#else +#define DEBUG_MSG(STR) \ + do { \ + } while (false) +#define assert(EXPRESSION) \ + do { \ + } while (false) +#endif + +// --- --- --- Components + +// Declaration +class NLFile; + +/** A Bound. + * A bound can represent various constraint on a variable or a constraint. + * Because it apply to both variables and constraints, we keep it general enough. + * Note that the targeted variable or constraint is implicitely represented by its position in the + final NL File. + * As a result, this information does not appear here. + * Bounds are used in the 'b' and 'r' segments. + + # Text # Starting the segment # Variable # Tag in enum NLS_Bounditem::Bound + 0 1.1 3.4 # 1.1 =< V =< 3.4 First variable LB_UB + 1 2.5 # V =< 2.5 Second variable UB + 2 7 # 7 =< V etc... LB + 3 # no constraint NONE + 4 9.4 # V = 9.4 EQ + + * Notes: - bound values are stored as 'double', even for integer variables. + * - we do not make that class Printable as it is better "printed" with a name for the + targeted variable/constraint + */ +class NLBound { +public: + /** Bound kind. Declaration matches specification above. */ + enum Bound { LB_UB = 0, UB = 1, LB = 2, NONE = 3, EQ = 4 }; + + /** *** *** *** Fields *** *** *** **/ + Bound tag = NONE; + double lb = 0; + double ub = 0; + + /** *** *** *** Constructors & helpers *** *** *** **/ + NLBound() = default; + NLBound(Bound tag, double lb, double ub); + + static NLBound make_bounded(double lb, double ub); + static NLBound make_ub_bounded(double ub); + static NLBound make_lb_bounded(double lb); + static NLBound make_nobound(); + static NLBound make_equal(double val); + + /** *** *** *** Update the lower or upper bound *** *** *** **/ + // Note: this method are "additive only": we cannot use them to remove a a bound. + void update_lb(double new_lb); + void update_ub(double new_ub); + void update_eq(double new_eq); + + /** *** *** *** Printing Methods *** *** *** **/ + + /** Print the bound with a comment containing the name of the variable/constraint. */ + ostream& print_on(ostream& o, string vname) const; + + /** Printing with 'body' as the name of the variable/constraint. */ + ostream& print_on(ostream& o) const; +}; + +/** A Declared variable. + * A variable is identified by its name, which is supposed to be unique in the MZN representation. + * In an NL file, variables are identified by their index. However, those index are dependent on + * the variable ordering, which can only be known once all variables are known. Hence, the + * computation of the index can only be achieved at a later stage. A variable is always associated + * to a bound, even if none are specified (See LNBound above)/ + */ +class NLVar { +public: + /** Variable name. */ + string name; + + /** Is the variable an integer variable? Else is a floating point variable. */ + bool is_integer = false; + + /** Is this variable flagged to be reported? */ + bool to_report = false; + + /** Is the variable appearing in a nonlinear constraint (including logical constraint, L segment). + */ + bool is_in_nl_constraint = false; + + /** Is the variable appearing non linearly in the objective? */ + bool is_in_nl_objective = false; + + /** Number of occurrences in Jacobian. */ + long jacobian_count = 0; + + /** The bound over this variable. + * Used when producing the unique 'b' segment of the NL file. + */ + NLBound bound; + + /* *** *** *** Constructors *** *** *** */ + NLVar() = default; + + /** Constructor with declare time information */ + NLVar(const string& name, bool is_integer, bool to_report, NLBound bound) + : name(name), is_integer(is_integer), to_report(to_report), bound(bound) {} + + /** Copy constructor, with update on bound */ + NLVar copy_with_bound(NLBound bound) const; +}; + +/** A NLArray: + * We do not use "real" array. + * This type only serves when sending the result back to minizinc + */ +class NLArray { +public: + /** Array item; if the string is empty, use the value. */ + class Item { + public: + string variable; + double value; + }; + + /** Array name */ + string name; + + /** Dimensions part, e.g. array2d( '0..4', '0..5' [ .... ]) */ + vector dimensions; + + /** Related variables */ + vector items; + + /** Is this an array or integers or floats? */ + bool is_integer = false; +}; + +/** A token from an 'expression graph'. + * An expression graph is express in Polish Prefix Notation: operator followed by operand. + * A token represent an operator or an operand. + * See the definition of the various enum. + */ +class NLToken { +public: + /** Kind of token. */ + enum Kind { + NUMERIC, // "n42.42" a numeric constant, double + VARIABLE, // "v4" reference to a decision variable 0<= i < nb_vars (see header) or a + // defined variable for i>=nb_vars + STRING, // "h11:some string" Probably unused in our case. + FUNCALL, // "f0 3" Call a defined function (index 0, 3 args). Probably unused in + // our case. + OP, // "o5" An operation defined by its operation code + MOP // "o7\n3" Operator with multiple operand. The number of operands (3) is on + // the next line ("\n")? + }; + + /** Opcode for operator with a fixed number of operands. */ + enum OpCode { + OPPLUS = 0, + OPMINUS = 1, + OPMULT = 2, + OPDIV = 3, + OPREM = 4, + OPPOW = 5, + OPLESS = 6, + FLOOR = 13, + CEIL = 14, + ABS = 15, + OPUMINUS = 16, + OPOR = 20, + OPAND = 21, + LT = 22, + LE = 23, + EQ = 24, + GE = 28, + GT = 29, + NE = 30, + OPNOT = 34, + OPIFnl = 35, + OP_tanh = 37, + OP_tan = 38, + OP_sqrt = 39, + OP_sinh = 40, + OP_sin = 41, + OP_log10 = 42, + OP_log = 43, + OP_exp = 44, + OP_cosh = 45, + OP_cos = 46, + OP_atanh = 47, + OP_atan2 = 48, + OP_atan = 49, + OP_asinh = 50, + OP_asin = 51, + OP_acosh = 52, + OP_acos = 53, + OPintDIV = 55, + OPprecision = 56, + OPround = 57, + OPtrunc = 58, + OPATLEAST = 62, + OPATMOST = 63, + OPPLTERM = 64, + OPIFSYM = 65, + OPEXACTLY = 66, + OPNOTATLEAST = 67, + OPNOTATMOST = 68, + OPNOTEXACTLY = 69, + OPIMPELSE = 72, + OP_IFF = 73, + OPSOMESAME = 75, + OP1POW = 76, + OP2POW = 77, + OPCPOW = 78, + OPFUNCALL = 79, + OPNUM = 80, + OPHOL = 81, + OPVARVAL = 82, + N_OPS = 83, + }; + + /** Opcodes for operand taking multiple arguments. */ + enum MOpCode { + MINLIST = 11, + MAXLIST = 12, + OPSUMLIST = 54, + OPCOUNT = 59, + OPNUMBEROF = 60, + OPNUMBEROFs = 61, + ANDLIST = 70, + ORLIST = 71, + OPALLDIFF = 74, + }; + + /** Obtain the name of an operator from its opcode. */ + static const char* get_name(OpCode oc); + + /** Obtain the name of an operator (with multiple operands) from its opcode. */ + static const char* get_name(MOpCode moc); + + /* *** *** *** Fields *** *** *** */ + + Kind kind; + double numeric_value; // if kind==NUMERIC + int nb_args; // if kind==FUNCALL or kind==MOP + string str; // if kind==STRING or kind=VARIABLE (variable name) or kind=FUNCALL (function name) + OpCode oc; // if kind==OP + MOpCode moc; // if kind==MOP + + /* *** *** *** Constructor and helpers *** *** *** */ + + NLToken() = default; + + static NLToken n(double value); + + static NLToken v(string vname); + + static NLToken o(OpCode opc); + + static NLToken mo(MOpCode mopc, int nb); + + /* *** *** *** Query *** *** *** */ + bool is_variable() const; + + bool is_constant() const; + + /* *** *** *** Printable *** *** *** */ + + ostream& print_on(ostream& o, const NLFile& nl_file) const; +}; + +/** A algebraic constraint. + * Contains both a linear and a non linear part. + * We do not handle network constraints. + */ +class NLAlgCons { +public: + /** Constraint name, also acts as identifier. */ + string name; + + /** Bound on the algebraic constraint. + * Used when producing the unique r of the NL file. + */ + NLBound range; + + /** Expression graph, used for the non linear part. + * Used to produce a new, standalone, C segment. + * If the expression graph is empty (linear constraint), produce the expression graph 'n0' + */ + vector expression_graph = {}; + + /** Jacobian, used for the linear part. Identify a variable by its name and associate a + * coefficent. Used to produce a new, standalone, J segment. + */ + vector> jacobian = {}; + + /** Method to build the var_coeff vector. + * The NLFile is used to access the variables through their name in order to increase their + * jacobian count. + */ + void set_jacobian(const vector& vnames, const vector& coeffs, NLFile* nl_file); + + /* *** *** *** Helpers *** *** *** */ + + /** A constraint is considered linear if the expression_graph is empty. */ + bool is_linear() const; + + /* *** *** *** Printable *** *** *** */ + + ostream& print_on(ostream& o, const NLFile& nl_file) const; +}; + +/** A logical constraint. + * Contains only a non linear part. + * We do not handle network constraints. + * Logical constraint stands on their own and do not need any identifier. + * However, for consistency sake, we still keep their name. + */ +class NLLogicalCons { +public: + /** Constraint name, also acts as identifier. */ + string name; + + /** Index */ + int index = -1; + + /** Expression graph, used for the non linear part. + * Used to produce a new, standalone, L segment. + * If the expression graph is empty (linear constraint), produce the expression graph 'n0' + */ + vector expression_graph = {}; + + /* *** *** *** Constructor *** *** *** */ + NLLogicalCons(int idx) : index(idx) {} + + /* *** *** *** Printable *** *** *** */ + + ostream& print_on(ostream& o, const NLFile& nl_file) const; +}; + +/** The header. */ +class NLHeader { +public: + /* *** *** *** Printable *** *** *** */ + + ostream& print_on(ostream& o, const NLFile& nl_file) const; +}; + +/** An Objective + * In an NL file, we can have several of those. + * However, in flatzinc, only one is allowed, so we only have one. + * Note that in NL, we do not have a "satisfy" objective, only a minimize or maximize one. + * We translate the "satisfy" with "minimize n0". + */ +class NLObjective { +public: + enum MinMax { + UNDEF = -2, + SATISFY = -1, + MINIMIZE = 0, + MAXIMIZE = 1, + }; + + /* *** *** *** Fields *** *** *** */ + MinMax minmax = UNDEF; + vector expression_graph = {}; // If empty, produce a 'n0' when printing + + /* *** *** *** Gradient *** *** *** */ + + /** Gradient, used for the linear part. Identify a variable by its name and associate a + * coefficent. Used to produce a new, standalone, G segment. + */ + vector> gradient = {}; + + /** Method to build the var_coeff vector. */ + void set_gradient(const vector& vnames, const vector& coeffs); + + int gradient_count() const; + + /* *** *** *** Helpers *** *** *** */ + bool is_defined() const; + + bool is_linear() const; + + bool is_optimisation() const; + + /* *** *** *** Constructor *** *** *** */ + NLObjective() = default; + + /* *** *** *** Printable *** *** *** */ + ostream& print_on(ostream& o, const NLFile& nl_file) const; +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/nl/nl_file.hh b/include/minizinc/solvers/nl/nl_file.hh new file mode 100644 index 0000000..d874f2e --- /dev/null +++ b/include/minizinc/solvers/nl/nl_file.hh @@ -0,0 +1,560 @@ +/** + * Describe the structure of a NL file. + * Main author: Matthieu Herrmann, Monash University, Melbourne, Australia. 2019 + **/ + +#ifndef __MINIZINC_NL_FILE_HH__ +#define __MINIZINC_NL_FILE_HH__ + +#include +#include +#include + +#include +#include +#include +#include + +using namespace std; + +// This files declare data-structure describing the various components of a nl files. +// A nl files is composed of two main parts: a header and a list of segments. +// The header contains statistics and meta information about the model. +// The segments allow to describe the model, i.e. the variables, the constraints and objectives. +// The order of the segments and, when relevant, the order of their content +// (e.g. a segment declaring a list of variables) matters. + +/** NL File. + * Good to know: + * * We use string as variable unique identifier. + * Given a MZN variable declaration (can be obtain from a MZN variable), the 'get_vname' + * helper produces the string. + * * In our case, we only have one 'solve' per file. + * * NL file use double everywhere. Hence, even with dealing with integer variable, we store + * the information with double. + * + */ + +namespace MiniZinc { + +// --- --- --- NL Files +class NLFile { +public: + /* *** *** *** Helpers *** *** *** */ + + /** Create a string representing the name (and unique identifier) from an identifier. */ + static string get_vname(const Id* id); + + /** Create a string representing the name (and unique identifier) of a variable from a variable + * declaration. */ + static string get_vname(const VarDecl& vd); + + /** Create a string representing the name (and unique identifier) of a constraint from a specific + * call expression. */ + static string get_cname(const Call& c); + + /** Extract an array literal from an expression. */ + static const ArrayLit& get_arraylit(const Expression* e); + + /** Create a vector of double from a vector containing Expression being integer literal IntLit. */ + static vector from_vec_int(const ArrayLit& v_int); + + /** Create a vector of double from a vector containing Expression being float literal FloatLit. */ + static vector from_vec_fp(const ArrayLit& v_fp); + + /** Create a vector of variable names from a vector containing Expression being identifier Id. */ + static vector from_vec_id(const ArrayLit& v_id); + + /* *** *** *** Phase 1: collecting data from MZN *** *** *** */ + + // Variables collection, identified by name + // Needs ordering, see phase 2 + map variables = {}; + + // Algebraic constraints collection, identified by name + // Needs ordering, see phase 2 + map constraints = {}; + + // Logical constraints do not need ordering: + vector logical_constraints = {}; + + // Objective field. Only one, so we do not need ordering. + NLObjective objective = {}; + + // Output arrays + vector output_arrays = {}; + + /** Add a solve goal in the NL File. In our case, we can only have one and only one solve goal. */ + void add_solve(SolveI::SolveType st, const Expression* e); + + /** Add a variable declaration in the NL File. + * This function pre-analyse the declaration VarDecl, then delegate to add_vdecl_integer or + * add_vdecl_fp. Analyse a variable declaration 'vd' of type 'ti' with an 'rhs'. The variable + * declaration gives us access to the variable name while the type allows us to discriminate + * between integer, floating point value and arrays. Array are ignored (not declared): if we + * encouter an array in a constraint, we can find the array through the variable (ot it is a + * litteral). Notes: - We use -Glinear, so we do not have boolean. + * - This will change TODO keep checking comment and code consistency. + * + * RHS is for arrays: it contains the definition of the array. + * + * The type also gives us the domain, which can be: + * NULL: no restriction over the variable + * SetLit: Gives us a lower and upper bound + * If a variable is bounded only on one side, then the domain is NULL and the bound is expressed + * through a constraint. + */ + void add_vdecl(const VarDecl& vd, const TypeInst& ti, const Expression& rhs); + + /** Add an integer variable declaration to the NL File. */ + void add_vdecl_integer(const string& name, const IntSetVal* isv, bool to_report); + + /** Add a floating point variable declaration to the NL File. */ + void add_vdecl_fp(const string& name, const FloatSetVal* fsv, bool to_report); + + // --- --- --- Constraints analysis + + /** Add a constraint to the NL File. + * This method is a dispatcher for all the other constraints methods below. */ + void analyse_constraint(const Call& c); + + // --- --- --- Helpers + + /** Create a token from an expression representing a variable. + * ONLY USE FOR CONSTRAINT, NOT OBJECTIVES! (UPDATE VARIABLES FLAG FOR CONSTRAINTS) + */ + NLToken get_tok_var(const Expression* e); + + /** Create a token from an expression representing either a variable or an integer numeric value. + * ONLY USE FOR CONSTRAINT, NOT OBJECTIVES! + */ + NLToken get_tok_var_int(const Expression* e); + + /** Create a token from an expression representing either a variable or a floating point numeric + * value. ONLY USE FOR CONSTRAINT, NOT OBJECTIVES! + */ + NLToken get_tok_var_fp(const Expression* e); + + /** Update an expression graph (only by appending token) with a linear combination + * of coefficients and variables. + * ONLY USE FOR CONSTRAINTS, NOT OBJECTIVES! + */ + void make_SigmaMult(vector& expression_graph, const vector& coeffs, + const vector& vars); + + // --- --- --- Linear Builders + // Use an array of literals 'coeffs' := c.arg(0), an array of variables 'vars' := c.arg(1), + // and a variable or literal 'value' := c.arg(2). + // [coeffs] and value are fixed (no variable allowed). + // The call is needed to create the name. However, the extraction of the coefficients and the + // value is left to the calling function as this could be use with both integer and floating point + // (we only have floating point in NL) + + /** Create a linear constraint [coeffs] *+ [vars] = value. */ + void lincons_eq(const Call& c, const vector& coeffs, const vector& vars, + NLToken value); + + /** Create a linear constraint [coeffs] *+ [vars] <= value. */ + void lincons_le(const Call& c, const vector& coeffs, const vector& vars, + NLToken value); + + /** Create a linear logical constraint [coeffs] *+ [vars] PREDICATE value. + * Use a generic comparison operator. + * Warnings: - Creates a logical constraint + * - Only use for conmparisons that cannot be expressed with '=' xor '<='. + */ + void lincons_predicate(const Call& c, NLToken::OpCode oc, const vector& coeffs, + const vector& vars, NLToken value); + + // --- --- --- Non Linear Builders + // For predicates, uses 2 variables or literals: x := c.arg(0), y := c.arg(1) + // x PREDICATE y + + // For unary operations, uses 2 variables or literals: x := c.arg(0), y := c.arg(1) + // OPEARTOR x = y + + // For binary operations, uses 3 variables or literals: x := c.arg(0), y := c.arg(1), and z := + // c.arg(2). x OPERATOR y = z + + /** Create a non linear constraint x = y + * Use the jacobian and the bound on constraint to translate into x - y = 0 + * Simply update the bound if one is a constant. + */ + void nlcons_eq(const Call& c, NLToken x, NLToken y); + + /** Create a non linear constraint x <= y + * Use the jacobian and the bound on constraint to translate into x - y <= 0 + * Simply update the bound if one is a constant. + */ + void nlcons_le(const Call& c, NLToken x, NLToken y); + + /** Create a non linear constraint with a predicate: x PREDICATE y + * Use a generic comparison operator. + * Warnings: - Creates a logical constraint + * - Only use for conmparisons that cannot be expressed with '=' xor '<='. + */ + void nlcons_predicate(const Call& c, NLToken::OpCode oc, NLToken x, NLToken y); + + /** Create a non linear constraint with a binary operator: x OPERATOR y = z */ + void nlcons_operator_binary(const Call& c, NLToken::OpCode oc, NLToken x, NLToken y, NLToken z); + + /** Create a non linear constraint with a binary operator: x OPERATOR y = z. + * OPERATOR is now a Multiop, with a count of 2 (so the choice of the method to use depends on + * the LN implementation) */ + void nlcons_operator_binary(const Call& c, NLToken::MOpCode moc, NLToken x, NLToken y, NLToken z); + + /** Create a non linear constraint with an unary operator: OPERATOR x = y */ + void nlcons_operator_unary(const Call& c, NLToken::OpCode oc, NLToken x, NLToken y); + + /** Create a non linear constraint, specialized for log2 unary operator: Log2(x) = y */ + void nlcons_operator_unary_log2(const Call& c, NLToken x, NLToken y); + + // --- --- --- Integer Linear Constraints + + /** Linar constraint: [coeffs] *+ [vars] = value */ + void consint_lin_eq(const Call& c); + + /** Linar constraint: [coeffs] *+ [vars] =< value */ + void consint_lin_le(const Call& c); + + /** Linar constraint: [coeffs] *+ [vars] != value */ + void consint_lin_ne(const Call& c); + + // --- --- --- Integer Non Linear Predicate Constraints + + /** Non linear constraint x = y */ + void consint_eq(const Call& c); + + /** Non linear constraint x <= y */ + void consint_le(const Call& c); + + /** Non linear constraint x != y */ + void consint_ne(const Call& c); + + // --- --- --- Integer Non Linear Binary Operator Constraints + + /** Non linear constraint x + y = z */ + void consint_plus(const Call& c); + + /** Non linear constraint x * y = z */ + void consint_times(const Call& c); + + /** Non linear constraint x / y = z */ + void consint_div(const Call& c); + + /** Non linear constraint x mod y = z */ + void consint_mod(const Call& c); + + /** Non linear constraint x pow y = z */ + void int_pow(const Call& c); + + /** Non linear constraint max(x, y) = z */ + void int_max(const Call& c); + + /** Non linear constraint min(x, y) = z */ + void int_min(const Call& c); + + // --- --- --- Integer Non Linear Unary Operator Constraints + + void int_abs(const Call& c); + + // --- --- --- Floating Point Linear Constraints + + /** Linar constraint: [coeffs] *+ [vars] = value */ + void consfp_lin_eq(const Call& c); + + /** Linar constraint: [coeffs] *+ [vars] = value */ + void consfp_lin_le(const Call& c); + + /** Linar constraint: [coeffs] *+ [vars] != value */ + void consfp_lin_ne(const Call& c); + + /** Linar constraint: [coeffs] *+ [vars] < value */ + void consfp_lin_lt(const Call& c); + + // --- --- --- Floating Point Non Linear Predicate Constraints + + /** Non linear constraint x = y */ + void consfp_eq(const Call& c); + + /** Non linear constraint x <= y */ + void consfp_le(const Call& c); + + /** Non linear constraint x != y */ + void consfp_ne(const Call& c); + + /** Non linear constraint x < y */ + void consfp_lt(const Call& c); + + // --- --- --- Floating Point Non Linear Binary Operator Constraints + + /** Non linear constraint x + y = z */ + void consfp_plus(const Call& c); + + /** Non linear constraint x - y = z */ + void consfp_minus(const Call& c); + + /** Non linear constraint x * y = z */ + void consfp_times(const Call& c); + + /** Non linear constraint x / y = z */ + void consfp_div(const Call& c); + + /** Non linear constraint x mod y = z */ + void consfp_mod(const Call& c); + + /** Non linear constraint x pow y = z */ + void float_pow(const Call& c); + + /** Non linear constraint max(x, y) = z */ + void float_max(const Call& c); + + /** Non linear constraint min(x, y) = z */ + void float_min(const Call& c); + + // --- --- --- Floating Point Non Linear Unary Operator Constraints + + /** Non linear constraint abs x = y */ + void float_abs(const Call& c); + + /** Non linear constraint acos x = y */ + void float_acos(const Call& c); + + /** Non linear constraint acosh x = y */ + void float_acosh(const Call& c); + + /** Non linear constraint asin x = y */ + void float_asin(const Call& c); + + /** Non linear constraint asinh x = y */ + void float_asinh(const Call& c); + + /** Non linear constraint atan x = y */ + void float_atan(const Call& c); + + /** Non linear constraint atanh x = y */ + void float_atanh(const Call& c); + + /** Non linear constraint cos x = y */ + void float_cos(const Call& c); + + /** Non linear constraint cosh x = y */ + void float_cosh(const Call& c); + + /** Non linear constraint exp x = y */ + void float_exp(const Call& c); + + /** Non linear constraint ln x = y */ + void float_ln(const Call& c); + + /** Non linear constraint log10 x = y */ + void float_log10(const Call& c); + + /** Non linear constraint log2 x = y */ + void float_log2(const Call& c); + + /** Non linear constraint sqrt x = y */ + void float_sqrt(const Call& c); + + /** Non linear constraint sin x = y */ + void float_sin(const Call& c); + + /** Non linear constraint sinh x = y */ + void float_sinh(const Call& c); + + /** Non linear constraint tan x = y */ + void float_tan(const Call& c); + + /** Non linear constraint tanh x = y */ + void float_tanh(const Call& c); + + // --- --- --- Other + + /** Integer x to floating point y. Constraint x = y translated into x - y = 0. */ + void int2float(const Call& c); + + /* *** *** *** Phase 2: processing *** *** *** */ + + void phase_2(); + + // Ordering of variables according to "hooking your solver" + /* Meaning of the names (total, then by appearance order in the tables below) + n_var total number of variables + nlvc number of variables appearing nonlinearly in constraints + nlvo number of variables appearing nonlinearly in objectives + nwv number of linear arcs + niv number of "other" integer variables + nbv number of binary variables + + + Order of variables (yes, the way things are counted is... "special".) + Category Count + --- --- --- --- | --- --- --- --- --- + nonlinear max(nlvc, nlvo) // See below for order on + non linear variables linear arcs nwv // Not + implemented other linear n_var − (max {nlvc, nlvo} + niv + nbv + nwv) // Linear + Continuous binary nbv // Booleans + other integer niv // Linear Integer + + + + Order of non linear variables (see 'nonlinear' above) + Meaning of the names: + nlvb number of variables appearing nonlinearly in both constraints and + objectives nlvbi number of integer variables appearing nonlinearly in both + constraints and objectives nlvc number of variables appearing nonlinearly in + constraints nlvci number of integer variables appearing nonlinearly in constraints + **only** nlvo number of variables appearing nonlinearly in objectives nlvoi number + of integer variables appearing nonlinearly in objectives **only** + + Category Count + --- --- --- --- --- --- --- --- --- --- --- --- --- | --- --- --- --- --- + Continuous in BOTH an objective AND a constraint | nlvb - nlvbi + Integer, in BOTH an objective AND a constraint | nlvbi + Continuous, in constraints only | nlvc − (nlvb + nlvci) + Integer, in constraints only | nlvci + Continous, in objectives only | nlvo − (nlvc + nlvoi) + Integer, in objectives only | nlvoi + */ + + /** Non Linear Continuous Variables in BOTH an objective and a constraint. */ + vector vname_nlcv_both = {}; + + /** Non Linear Integer Variables in BOTH an objective and a constraint. */ + vector vname_nliv_both = {}; + + /** Non Linear Continuous Variables in CONStraints only. */ + vector vname_nlcv_cons = {}; + + /** Non Linear Integer Variables in CONStraints only. */ + vector vname_nliv_cons = {}; + + /** Non Linear Continuous Variables in OBJectives only. */ + vector vname_nlcv_obj = {}; + + /** Non Linear Integer Variables in OBJectives only. */ + vector vname_nliv_obj = {}; + + /** Linear arcs. (Network not implemented) */ + vector vname_larc_all = {}; + + /** Linear Continuous Variables (ALL of them). */ + vector vname_lcv_all = {}; + + /** Binary Variables (ALL of them). */ + vector vname_bv_all = {}; + + /** Linear Integer Variables (ALL of them). */ + vector vname_liv_all = {}; + + /** Contained all ordered variable names. Mapping variable index -> variable name */ + vector vnames = {}; + + /** Mapping variable name -> variable index */ + map variable_indexes = {}; + + // --- --- --- Simple tests + + bool has_integer_vars() const; + + bool has_continous_vars() const; + + // --- --- --- Variables counts + + // When the phase 2 is done, all the following counts should be available. + // taken from "hooking your solver" and used in the above explanatios + + /** Total number of variables. */ + int n_var() const; + + /** Number of variables appearing nonlinearly in constraints. */ + int nlvc() const; + + /** Number of variables appearing nonlinearly in objectives. */ + int nlvo() const; + + /** Number of variables appearing nonlinearly in both constraints and objectives.*/ + int nlvb() const; + + /** Number of integer variables appearing nonlinearly in both constraints and objectives.*/ + int nlvbi() const; + + /** Number of integer variables appearing nonlinearly in constraints **only**.*/ + int nlvci() const; + + /** Number of integer variables appearing nonlinearly in objectives **only**.*/ + int nlvoi() const; + + /** Number of linear arcs .*/ + int nwv() const; + + /** Number of "other" integer variables.*/ + int niv() const; + + /** Number of binary variables.*/ + int nbv() const; + + /** Accumulation of Jacobian counts. */ + int jacobian_count() const; + int _jacobian_count = 0; + + // Ordering of constraints according to "hooking your solver" + /* Meaning of the names: + n_con Total number of constraint + nlc Number of nonlinear general constraint, including network constraint + nlnc Number of nonlinear network constraint + lnc Number of linear network constraint + + Order of constraints: + Category Count + --- --- --- --- --- | --- --- --- --- --- + Nonlinear general nlc - nlnc + Nonlinear network nlnc + Linear network lnc + Linear general n_con - (nlc + lnc) + */ + + /** Nonlinear general constraints. */ + vector cnames_nl_general = {}; + + /** Nonlinear network constraints. */ + vector cnames_nl_network = {}; + + /** Linear network constraints. */ + vector cnames_lin_network = {}; + + /** Linear general constraints. */ + vector cnames_lin_general = {}; + + /** Contained all ordered algebraic (and network if they were implemented) constraints names. + * Mapping constraint index -> constraint name + */ + vector cnames = {}; + + /** Mapping constraint name -> contraint index */ + map constraint_indexes = {}; + + // Count of algebraic constraints: + // The header needs to know how many range algebraic constraints and equality algebraic + // constraints we have. + /** Number of range algebraic constraints */ + int nb_alg_cons_range = 0; + /** equality algebraic constraints */ + int nb_alg_cons_eq = 0; + + /* *** *** *** Constructor *** *** *** */ + + NLFile() = default; + + /* *** *** *** Printable *** *** *** */ + + /** Print the NLFile on a stream. + * Note: this is not the 'Printable' interface as we do not pass any nl_file (that would be + * 'this') as a reference. + */ + ostream& print_on(ostream& o) const; +}; + +} // End of NameSpace MiniZinc + +#endif \ No newline at end of file diff --git a/include/minizinc/solvers/nl/nl_solreader.hh b/include/minizinc/solvers/nl/nl_solreader.hh new file mode 100644 index 0000000..ef8abef --- /dev/null +++ b/include/minizinc/solvers/nl/nl_solreader.hh @@ -0,0 +1,117 @@ +#ifndef __MINIZINC_NL_READ_SOL_HH__ +#define __MINIZINC_NL_READ_SOL_HH__ + +#include +#include + +#include +#include +#include +#include + +using namespace std; + +// Reading a .sol file. +// Note: I did not find any description of the format, so I did a bit of reverse engineering. +// A sol file looks like this (first line is the 'message...' line) +/* + message from the solver + + Options + 3 + 1 + 1 + 0 + 1 + 0 + 2 + 2 + 1 + 2 + objno 0 0 +*/ +// A sol file does not contains comments, and the blank line between the message and 'Options' is +// meaningfull. Now, with our own comments. +/* + message from the solver # Starts on first line. Can span several line as long as their is no + blank line # the blank line marks the end of the message Options # Inform + about option used by the solver (?). May be absent. 3 # Number of options. + 1 # opt 1 Note: I did not fully understand this, but reading + "hooking your solver" 1 # opt 2 suggests that we can + (probably) ignore that. 0 # opt 3 1 # Number A of + dual before "suf_sos()" (before solving ?) 0 # Number of dual after. Seems + to be either A or 0. Will probably always be 0 (???) 2 # Number B of primal + before "suf_sos()" (before solving ?) 2 # Number of primal after. Seems to + be either B or 0 1 # No dual. So this is the result for the first primal + (variable indice 0) 2 # result for variable indice 1 objno 0 0 # Objectif 0 + +*/ +// Result code can be : +// (from https://github.com/ampl/mp/blob/master/include/mp/common.h) +// UNKNOWN = -1, +// SOLVED = 0, // Optimal sol found for an optim problem or a feasible solution found for a +// satisfaction problem. UNCERTAIN = 100, // Solution returned but it can be non-optimal or even +// infeasible. INFEASIBLE = 200, UNBOUNDED = 300, // Problem is unbounded. LIMIT = 400, +// // Stopped by a limit, e.g. on iterations or time. FAILURE = 500, // A solver error. +// INTERRUPTED = 600 // Interrupted by the user. + +namespace MiniZinc { + +/** Declaration of the exit codes. */ +enum NL_Solver_Status { + PARSE_ERROR = -2, // We are adding our own error code for parsing + UNKNOWN = -1, + SOLVED = 0, // Optimal sol found for an optim problem or a feasible solution found for a + // satisfaction problem. + UNCERTAIN = 100, // Solution returned but it can be non-optimal or even infeasible. + INFEASIBLE = 200, + UNBOUNDED = 300, // Problem is unbounded. + LIMIT = 400, // Stopped by a limit, e.g. on iterations or time. + FAILURE = 500, // A solver error. + INTERRUPTED = 600 // Interrupted by the user. +}; + +/** Represent a solution read from a file '.sol'. */ +class NLSol { +public: + // --- --- --- Fields + string message; + NL_Solver_Status status; + vector values; + + // --- --- --- Constructors + + NLSol() = default; + + NLSol(string mes, NL_Solver_Status st, vector res) + : message(mes), status(st), values(res) {} + + // --- --- --- Static functions + + static NLSol parse_sol(std::istream& in); +}; + +/** Our version of Solns2Out **/ + +class NLSolns2Out { +private: + Solns2Out* out; + NLFile& nl_file; + std::ofstream dummy_ofstream; + + // Controls for feedRawDataChunk + bool in_line; + bool verbose; + +public: + NLSolns2Out(Solns2Out* out0, NLFile& nl_file0, bool verbose0) + : out(out0), nl_file(nl_file0), in_line(false), verbose(verbose0) {} + + void parse_sol(const string& filename); + + bool feedRawDataChunk(const char* data); + std::ostream& getLog(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/nl/nl_solverfactory.hh b/include/minizinc/solvers/nl/nl_solverfactory.hh new file mode 100644 index 0000000..d5f22de --- /dev/null +++ b/include/minizinc/solvers/nl/nl_solverfactory.hh @@ -0,0 +1,17 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_NL_SOLVERFACTORY_HH__ +#define __MINIZINC_NL_SOLVERFACTORY_HH__ + +namespace MiniZinc { +class NL_SolverFactoryInitialiser { +public: + NL_SolverFactoryInitialiser(void); +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/solvers/nl/nl_solverinstance.hh b/include/minizinc/solvers/nl/nl_solverinstance.hh new file mode 100644 index 0000000..cf2af17 --- /dev/null +++ b/include/minizinc/solvers/nl/nl_solverinstance.hh @@ -0,0 +1,85 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_NL_SOLVER_INSTANCE_HH__ +#define __MINIZINC_NL_SOLVER_INSTANCE_HH__ + +#include +#include +#include +#include + +#ifdef _WIN32 +#undef ERROR +#endif + +namespace MiniZinc { + +class NLSolverOptions : public SolverInstanceBase::Options { +public: + std::string nl_solver; + std::vector nl_flags; + std::vector nl_solver_flags; + bool do_hexafloat = false; + bool do_keepfile = false; +}; + +class NLSolverInstance : public SolverInstanceBase { +private: + std::string _fzn_solver; + +protected: + Model* _fzn; + Model* _ozn; + + NLFile nl_file; + +public: + NLSolverInstance(std::ostream& log, SolverInstanceBase::Options* opt); + + ~NLSolverInstance(void); + + Status next(void) override { return SolverInstance::Status::ERROR; } + + Status solve(void) override; + + void processFlatZinc(void) override; + + void resetSolver(void) override; + + // TODO: implement + void addConstraint(const std::vector& bs, Constraint* c) override { + assert(false); + }; + void addVariable(Variable* var, bool isOutput) override { assert(false); }; + Val getSolutionValue(Variable* var) override { return Val(); }; + +protected: + Expression* getSolutionValue(Id* id); + + void analyse(const Item* i); +}; + +class NL_SolverFactory : public SolverFactory { +protected: + virtual SolverInstanceBase* doCreateSI(std::ostream&, SolverInstanceBase::Options* opt); + +public: + NL_SolverFactory(void); + virtual SolverInstanceBase::Options* createOptions(void); + virtual std::string getDescription(SolverInstanceBase::Options* opt = NULL); + virtual std::string getVersion(SolverInstanceBase::Options* opt = NULL); + virtual std::string getId(void); + virtual bool processOption(SolverInstanceBase::Options* opt, int& i, + std::vector& argv); + virtual void printHelp(std::ostream& os); + // void setAcceptedFlags(SolverInstanceBase::Options* opt, const std::vector& + // flags); +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/statistics.hh b/include/minizinc/statistics.hh new file mode 100644 index 0000000..a67580b --- /dev/null +++ b/include/minizinc/statistics.hh @@ -0,0 +1,50 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_STATISTICS_HH__ +#define __MINIZINC_STATISTICS_HH__ +#include + +namespace MiniZinc { + +class Statistics { +protected: + // time in milliseconds + unsigned long long _time; + // search nodes + unsigned long long _nodes; + // failures/ backtracks + unsigned long long _failures; + // current objective value + double _objective; + +public: + Statistics() : _time(0), _nodes(0), _failures(0) {} + + virtual void print(std::ostream& os); + + void time(unsigned long long t); + void nodes(unsigned long long n); + void failures(unsigned long long f); + void objective(double o); + + unsigned long long time(); + unsigned long long nodes(); + unsigned long long failures(); + double objective(); + + Statistics& operator+=(Statistics& s); + + virtual void cleanup() { _time = _nodes = _failures = 0; } +}; +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/support/dtrace.h b/include/minizinc/support/dtrace.h new file mode 100644 index 0000000..d0866ce --- /dev/null +++ b/include/minizinc/support/dtrace.h @@ -0,0 +1,37 @@ +#pragma once + +#if defined(USE_DYNAMIC_TRACE) + +#include + +#define DTRACE_ENABLED(name) MINIZINC_##name##_ENABLED() +#define DTRACE0(name) MINIZINC_##name() +#define DTRACE1(name, a0) MINIZINC_##name(a0) +#define DTRACE2(name, a0, a1) MINIZINC_##name((a0), (a1)) +#define DTRACE3(name, a0, a1, a2) MINIZINC_##name((a0), (a1), (a2)) +#define DTRACE4(name, a0, a1, a2, a3) MINIZINC_##name((a0), (a1), (a2), (a3)) +#define DTRACE5(name, a0, a1, a2, a3, a4) MINIZINC_##name((a0), (a1), (a2), (a3), (a4)) + +#else + +#define DTRACE_ENABLED(name) 0 +#define DTRACE0(name) \ + do { \ + } while (0) +#define DTRACE1(name, a0) \ + do { \ + } while (0) +#define DTRACE2(name, a0, a1) \ + do { \ + } while (0) +#define DTRACE3(name, a0, a1, a2) \ + do { \ + } while (0) +#define DTRACE4(name, a0, a1, a2, a3) \ + do { \ + } while (0) +#define DTRACE5(name, a0, a1, a2, a3, a4) \ + do { \ + } while (0) + +#endif diff --git a/include/minizinc/support/dtrace_probes.d b/include/minizinc/support/dtrace_probes.d new file mode 100644 index 0000000..00c3477 --- /dev/null +++ b/include/minizinc/support/dtrace_probes.d @@ -0,0 +1,20 @@ +provider minizinc { + /** + * Fired when the garbage collection function has finished + */ + probe gc__end(); + + /** + * Fired when the garbage collection function has started + */ + probe gc__start(); + + probe cse__find__start(uintptr_t envi, short nargs); + + probe cse__find__end(uintptr_t envi, int success); + + probe cse__insert__start(uintptr_t envi, short nargs); + + probe cse__insert__end(uintptr_t envi); + +}; diff --git a/include/minizinc/support/mza_parser.hh b/include/minizinc/support/mza_parser.hh new file mode 100644 index 0000000..0e9c203 --- /dev/null +++ b/include/minizinc/support/mza_parser.hh @@ -0,0 +1,46 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_MZA_PARSER_HH__ +#define __MINIZINC_MZA_PARSER_HH__ + +// Assembly Parser Requirements +#include + +#include + +// This is a workaround for a bug in flex that only shows up +// with the Microsoft C++ compiler +#if defined(_MSC_VER) +#define YY_NO_UNISTD_H +#ifdef __cplusplus +extern "C" int isatty(int); +#endif +#endif + +// The Microsoft C++ compiler marks certain functions as deprecated, +// so let's take the alternative definitions +#if defined(_MSC_VER) +#define strdup _strdup +#define fileno _fileno +#endif + +// Anonymous struct for when yyparse is exported +typedef struct MZAContext MZAContext; +// Parser generated header +#include + +using namespace MiniZinc; + +// Parsing function +std::pair> parse_mza(const std::string& assembly_str); + +#endif //__MINIZINC_MZA_PARSER_HH__ diff --git a/include/minizinc/support/regex.hh b/include/minizinc/support/regex.hh new file mode 100644 index 0000000..8f0e92b --- /dev/null +++ b/include/minizinc/support/regex.hh @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_REGEX_HH__ +#define __MINIZINC_REGEX_HH__ +#ifdef HAS_GECODE + +// Regex Parser Requirements +#include + +#include +#include +#include + +#include + +// This is a workaround for a bug in flex that only shows up +// with the Microsoft C++ compiler +#if defined(_MSC_VER) +#define YY_NO_UNISTD_H +#ifdef __cplusplus +extern "C" int isatty(int); +#endif +#endif + +// The Microsoft C++ compiler marks certain functions as deprecated, +// so let's take the alternative definitions +#if defined(_MSC_VER) +#define strdup _strdup +#define fileno _fileno +#endif + +// Anonymous struct for when yyparse is exported +typedef struct REContext REContext; +// Parser generated header +#include + +using namespace Gecode; +using namespace MiniZinc; + +// Parsing function +std::unique_ptr regex_from_string(const std::string& expression, const IntSetVal& domain, + const std::unordered_map& identifiers); + +#endif // HAS_GECODE +#endif //__MINIZINC_REGEX_HH__ diff --git a/include/minizinc/thirdparty/b64/cdecode.h b/include/minizinc/thirdparty/b64/cdecode.h new file mode 100644 index 0000000..5729853 --- /dev/null +++ b/include/minizinc/thirdparty/b64/cdecode.h @@ -0,0 +1,29 @@ +/* +cdecode.h - c header for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CDECODE_H +#define BASE64_CDECODE_H + +typedef enum +{ + step_a, step_b, step_c, step_d +} base64_decodestep; + +typedef struct +{ + base64_decodestep step; + char plainchar; +} base64_decodestate; + +void base64_init_decodestate(base64_decodestate* state_in); + +int base64_decode_value(char value_in); + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); + +#endif /* BASE64_CDECODE_H */ + diff --git a/include/minizinc/thirdparty/b64/cencode.h b/include/minizinc/thirdparty/b64/cencode.h new file mode 100644 index 0000000..cf32131 --- /dev/null +++ b/include/minizinc/thirdparty/b64/cencode.h @@ -0,0 +1,32 @@ +/* +cencode.h - c header for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#ifndef BASE64_CENCODE_H +#define BASE64_CENCODE_H + +typedef enum +{ + step_A, step_B, step_C +} base64_encodestep; + +typedef struct +{ + base64_encodestep step; + char result; + int stepcount; +} base64_encodestate; + +void base64_init_encodestate(base64_encodestate* state_in); + +char base64_encode_value(char value_in); + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in); + +#endif /* BASE64_CENCODE_H */ + diff --git a/include/minizinc/thirdparty/b64/decode.h b/include/minizinc/thirdparty/b64/decode.h new file mode 100644 index 0000000..6d594a0 --- /dev/null +++ b/include/minizinc/thirdparty/b64/decode.h @@ -0,0 +1,72 @@ +// :mode=c++: +/* +decode.h - c++ wrapper for a base64 decoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ +#ifndef BASE64_DECODE_H +#define BASE64_DECODE_H + +#define BUFFERSIZE 4096 + +#include + +namespace base64 +{ + extern "C" + { + #include + } + + struct decoder + { + base64_decodestate _state; + int _buffersize; + + decoder(int buffersize_in = BUFFERSIZE) + : _buffersize(buffersize_in) + {} + + int decode(char value_in) + { + return base64_decode_value(value_in); + } + + int decode(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_decode_block(code_in, length_in, plaintext_out, &_state); + } + + void decode(std::istream& istream_in, std::ostream& ostream_in) + { + base64_init_decodestate(&_state); + // + const int N = _buffersize; + char* code = new char[N]; + char* plaintext = new char[N]; + int codelength; + int plainlength; + + do + { + istream_in.read((char*)code, N); + codelength = istream_in.gcount(); + plainlength = decode(code, codelength, plaintext); + ostream_in.write((const char*)plaintext, plainlength); + } + while (istream_in.good() && codelength > 0); + // + base64_init_decodestate(&_state); + + delete [] code; + delete [] plaintext; + } + }; + +} // namespace base64 + + + +#endif // BASE64_DECODE_H + diff --git a/include/minizinc/thirdparty/b64/encode.h b/include/minizinc/thirdparty/b64/encode.h new file mode 100644 index 0000000..f51e705 --- /dev/null +++ b/include/minizinc/thirdparty/b64/encode.h @@ -0,0 +1,79 @@ +// :mode=c++: +/* +encode.h - c++ wrapper for a base64 encoding algorithm + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ +#ifndef BASE64_ENCODE_H +#define BASE64_ENCODE_H + +#define BUFFERSIZE 4096 + +#include + +namespace base64 +{ + extern "C" + { + #include + } + + struct encoder + { + base64_encodestate _state; + int _buffersize; + + encoder(int buffersize_in = BUFFERSIZE) + : _buffersize(buffersize_in) + {} + + int encode(char value_in) + { + return base64_encode_value(value_in); + } + + int encode(const char* code_in, const int length_in, char* plaintext_out) + { + return base64_encode_block(code_in, length_in, plaintext_out, &_state); + } + + int encode_end(char* plaintext_out) + { + return base64_encode_blockend(plaintext_out, &_state); + } + + void encode(std::istream& istream_in, std::ostream& ostream_in) + { + base64_init_encodestate(&_state); + // + const int N = _buffersize; + char* plaintext = new char[N]; + char* code = new char[2*N]; + int plainlength; + int codelength; + + do + { + istream_in.read(plaintext, N); + plainlength = istream_in.gcount(); + // + codelength = encode(plaintext, plainlength, code); + ostream_in.write(code, codelength); + } + while (istream_in.good() && plainlength > 0); + + codelength = encode_end(code); + ostream_in.write(code, codelength); + // + base64_init_encodestate(&_state); + + delete [] code; + delete [] plaintext; + } + }; + +} // namespace base64 + +#endif // BASE64_ENCODE_H + diff --git a/include/minizinc/thirdparty/miniz.h b/include/minizinc/thirdparty/miniz.h new file mode 100644 index 0000000..8e5ad72 --- /dev/null +++ b/include/minizinc/thirdparty/miniz.h @@ -0,0 +1,1328 @@ +/* miniz.c 2.0.7 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + + + + + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.0.2" +#define MZ_VERNUM 0xA020 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 0 +#define MZ_VER_REVISION 2 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif +#pragma once +#include +#include +#include +#include + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern void miniz_def_free_func(void *opaque, void *address); +extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif +#pragma once + + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(); +void tdefl_compressor_free(tdefl_compressor *pComp); + +#ifdef __cplusplus +} +#endif +#pragma once + +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ + +tinfl_decompressor *tinfl_decompressor_alloc(); +void tinfl_decompressor_free(tinfl_decompressor *pDecomp); + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif + +#pragma once + + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +void mz_zip_zero_struct(mz_zip_archive *pZip); + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +/* Returns MZ_FALSE if the file cannot be found. */ +mz_bool mz_zip_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/include/minizinc/timer.hh b/include/minizinc/timer.hh new file mode 100644 index 0000000..30fb8c1 --- /dev/null +++ b/include/minizinc/timer.hh @@ -0,0 +1,53 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_TIMER_HH__ +#define __MINIZINC_TIMER_HH__ + +#include +#include +#include +#include +#include + +namespace MiniZinc { + +class Timer { +protected: + std::chrono::steady_clock::time_point last; + +public: + /// Construct timer + Timer(void) : last(std::chrono::steady_clock::now()) {} + /// Reset timer + void reset(void) { last = std::chrono::steady_clock::now(); } + /// Return milliseconds since timer was last reset + long long int ms(void) const { + return std::chrono::duration_cast(std::chrono::steady_clock::now() - + last) + .count(); + } + /// Return seconds since timer was last reset + double s(void) const { + return std::chrono::duration_cast >( + std::chrono::steady_clock::now() - last) + .count(); + } + std::string stoptime(void) const { + std::ostringstream oss; + oss << std::setprecision(2) << std::fixed << s() << " s"; + return oss.str(); + } +}; + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/type.hh b/include/minizinc/type.hh new file mode 100644 index 0000000..e00cb75 --- /dev/null +++ b/include/minizinc/type.hh @@ -0,0 +1,261 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_TYPE_HH__ +#define __MINIZINC_TYPE_HH__ + +#include +#include +#include + +namespace MiniZinc { + +class EnvI; + +/// Type of a MiniZinc expression +class Type { +public: + /// Type-inst + enum TypeInst { TI_PAR, TI_VAR }; + /// Basic type + enum BaseType { BT_BOOL, BT_INT, BT_FLOAT, BT_STRING, BT_ANN, BT_TOP, BT_BOT, BT_UNKNOWN }; + /// Whether the expression is plain or set + enum SetType { ST_PLAIN, ST_SET }; + /// Whether the expression is normal or optional + enum OptType { OT_PRESENT, OT_OPTIONAL }; + /// Whether the par expression contains a var argument + enum ContainsVarType { CV_NO, CV_YES }; + +private: + unsigned int _ti : 1; + unsigned int _bt : 4; + unsigned int _st : 1; + unsigned int _ot : 1; + unsigned int _cv : 1; + /** \brief Enumerated type identifier + * This is an index into a table in the Env. It is currently limited to + * 4095 different enumerated type identifiers. + * For a non-array type, this maps directly to the identity of the enum. + * For an array type, it maps to a tuple of enum identities. + */ + unsigned int _enumId : 12; + /// Number of array dimensions + int _dim : 7; + +public: + /// Default constructor + Type(void) + : _ti(TI_PAR), + _bt(BT_UNKNOWN), + _st(ST_PLAIN), + _ot(OT_PRESENT), + _cv(CV_NO), + _enumId(0), + _dim(0) {} + + /// Access type-inst + TypeInst ti(void) const { return static_cast(_ti); } + /// Set type-inst + void ti(const TypeInst& t) { + _ti = t; + if (t == TI_VAR) _cv = CV_YES; + } + + /// Access basic type + BaseType bt(void) const { return static_cast(_bt); } + /// Set basic type + void bt(const BaseType& b) { _bt = b; } + + /// Access set type + SetType st(void) const { return static_cast(_st); } + /// Set set type + void st(const SetType& s) { _st = s; } + + /// Access opt type + OptType ot(void) const { return static_cast(_ot); } + /// Set opt type + void ot(const OptType& o) { _ot = o; } + + /// Access var-in-par type + bool cv(void) const { return static_cast(_cv) == CV_YES; } + /// Set var-in-par type + void cv(bool b) { _cv = b ? CV_YES : CV_NO; } + + /// Access enum identifier + unsigned int enumId(void) const { return _enumId; } + /// Set enum identifier + void enumId(unsigned int eid) { _enumId = eid; } + + /// Access dimensions + int dim(void) const { return _dim; } + /// Set dimensions + void dim(int d) { + _dim = d; + assert(_dim == d); + } + +protected: + /// Constructor + Type(const TypeInst& ti, const BaseType& bt, const SetType& st, unsigned int enumId, int dim) + : _ti(ti), + _bt(bt), + _st(st), + _ot(OT_PRESENT), + _cv(ti == TI_VAR ? CV_YES : CV_NO), + _enumId(enumId), + _dim(dim) {} + +public: + static Type parint(int dim = 0) { return Type(TI_PAR, BT_INT, ST_PLAIN, 0, dim); } + static Type parenum(unsigned int enumId, int dim = 0) { + return Type(TI_PAR, BT_INT, ST_PLAIN, enumId, dim); + } + static Type parbool(int dim = 0) { return Type(TI_PAR, BT_BOOL, ST_PLAIN, 0, dim); } + static Type parfloat(int dim = 0) { return Type(TI_PAR, BT_FLOAT, ST_PLAIN, 0, dim); } + static Type parstring(int dim = 0) { return Type(TI_PAR, BT_STRING, ST_PLAIN, 0, dim); } + static Type partop(int dim = 0) { return Type(TI_PAR, BT_TOP, ST_PLAIN, 0, dim); } + static Type ann(int dim = 0) { return Type(TI_PAR, BT_ANN, ST_PLAIN, 0, dim); } + static Type parsetint(int dim = 0) { return Type(TI_PAR, BT_INT, ST_SET, 0, dim); } + static Type parsetenum(unsigned int enumId, int dim = 0) { + return Type(TI_PAR, BT_INT, ST_SET, enumId, dim); + } + static Type parsetbool(int dim = 0) { return Type(TI_PAR, BT_BOOL, ST_SET, 0, dim); } + static Type parsetfloat(int dim = 0) { return Type(TI_PAR, BT_FLOAT, ST_SET, 0, dim); } + static Type parsetstring(int dim = 0) { return Type(TI_PAR, BT_STRING, ST_SET, 0, dim); } + static Type varint(int dim = 0) { return Type(TI_VAR, BT_INT, ST_PLAIN, 0, dim); } + static Type varenumint(unsigned int enumId, int dim = 0) { + return Type(TI_VAR, BT_INT, ST_PLAIN, enumId, dim); + } + static Type varbool(int dim = 0) { return Type(TI_VAR, BT_BOOL, ST_PLAIN, 0, dim); } + static Type varfloat(int dim = 0) { return Type(TI_VAR, BT_FLOAT, ST_PLAIN, 0, dim); } + static Type varsetint(int dim = 0) { return Type(TI_VAR, BT_INT, ST_SET, 0, dim); } + static Type varbot(int dim = 0) { return Type(TI_VAR, BT_BOT, ST_PLAIN, 0, dim); } + static Type bot(int dim = 0) { return Type(TI_PAR, BT_BOT, ST_PLAIN, 0, dim); } + static Type top(int dim = 0) { return Type(TI_PAR, BT_TOP, ST_PLAIN, 0, dim); } + static Type vartop(int dim = 0) { return Type(TI_VAR, BT_TOP, ST_PLAIN, 0, dim); } + static Type optvartop(int dim = 0) { + Type t(TI_VAR, BT_TOP, ST_PLAIN, 0, dim); + t._ot = OT_OPTIONAL; + return t; + } + static Type optpartop(int dim = 0) { + Type t(TI_PAR, BT_TOP, ST_PLAIN, 0, dim); + t._ot = OT_OPTIONAL; + return t; + } + + static Type unboxedint; + static Type unboxedfloat; + + bool isunknown(void) const { return _bt == BT_UNKNOWN; } + bool isplain(void) const { return _dim == 0 && _st == ST_PLAIN && _ot == OT_PRESENT; } + bool isint(void) const { return _dim == 0 && _st == ST_PLAIN && _bt == BT_INT; } + bool isbot(void) const { return _bt == BT_BOT; } + bool isfloat(void) const { return _dim == 0 && _st == ST_PLAIN && _bt == BT_FLOAT; } + bool isbool(void) const { return _dim == 0 && _st == ST_PLAIN && _bt == BT_BOOL; } + bool isstring(void) const { return isplain() && _bt == BT_STRING; } + bool isvar(void) const { return _ti != TI_PAR; } + bool isvarbool(void) const { + return _ti == TI_VAR && _dim == 0 && _st == ST_PLAIN && _bt == BT_BOOL && _ot == OT_PRESENT; + } + bool isvarfloat(void) const { + return _ti == TI_VAR && _dim == 0 && _st == ST_PLAIN && _bt == BT_FLOAT && _ot == OT_PRESENT; + } + bool isvarint(void) const { + return _ti == TI_VAR && _dim == 0 && _st == ST_PLAIN && _bt == BT_INT && _ot == OT_PRESENT; + } + bool ispar(void) const { return _ti == TI_PAR; } + bool isopt(void) const { return _ot == OT_OPTIONAL; } + bool ispresent(void) const { return _ot == OT_PRESENT; } + bool is_set(void) const { return _dim == 0 && _st == ST_SET; } + bool isintset(void) const { return is_set() && (_bt == BT_INT || _bt == BT_BOT); } + bool isboolset(void) const { return is_set() && (_bt == BT_BOOL || _bt == BT_BOT); } + bool isfloatset(void) const { return is_set() && (_bt == BT_FLOAT || _bt == BT_BOT); } + bool isann(void) const { return isplain() && _bt == BT_ANN; } + bool isintarray(void) const { + return _dim == 1 && _st == ST_PLAIN && _ot == OT_PRESENT && _bt == BT_INT; + } + bool isboolarray(void) const { + return _dim == 1 && _st == ST_PLAIN && _ot == OT_PRESENT && _bt == BT_BOOL; + } + bool isintsetarray(void) const { return _dim == 1 && _st == ST_SET && _bt == BT_INT; } + + bool operator==(const Type& t) const { + return _ti == t._ti && _bt == t._bt && _st == t._st && _ot == t._ot && _dim == t._dim; + } + bool operator!=(const Type& t) const { return !this->operator==(t); } + // protected: + + int toInt(void) const { + return +((1 - static_cast(_st)) << 28) + (static_cast(_bt) << 24) + + (static_cast(_ti) << 21) + (static_cast(_ot) << 20) + + (static_cast(_enumId) << 8) + (_dim == -1 ? 1 : (_dim == 0 ? 0 : _dim + 1)); + } + static Type fromInt(int i) { + Type t; + t._st = 1 - static_cast((i >> 28) & 0x1); + t._bt = static_cast((i >> 24) & 0xF); + t._ti = static_cast((i >> 21) & 0x7); + t._ot = static_cast((i >> 20) & 0x1); + t._enumId = static_cast((i >> 8) & 0xFFF); + int dim = (i & 0x7F); + t._dim = (dim == 0 ? 0 : (dim == 1 ? -1 : dim - 1)); + return t; + } + std::string toString(EnvI& env) const; + std::string nonEnumToString(void) const; + +public: + /// Check if \a bt0 is a subtype of \a bt1 + static bool bt_subtype(const Type& t0, const Type& t1, bool strictEnums) { + if (t0.bt() == t1.bt() && + (!strictEnums || t0.dim() != 0 || (t0.enumId() == t1.enumId() || t1.enumId() == 0))) + return true; + switch (t0.bt()) { + case BT_BOOL: + return (t1.bt() == BT_INT || t1.bt() == BT_FLOAT); + case BT_INT: + return t1.bt() == BT_FLOAT; + default: + return false; + } + } + + /// Check if this type is a subtype of \a t + bool isSubtypeOf(const Type& t, bool strictEnums) const { + if (_dim == 0 && t._dim != 0 && _st == ST_SET && t._st == ST_PLAIN && + (bt() == BT_BOT || bt_subtype(*this, t, false) || t.bt() == BT_TOP) && _ti == TI_PAR && + (_ot == OT_PRESENT || _ot == t._ot)) + return true; + // either same dimension or t has variable dimension + if (_dim != t._dim && (_dim == 0 || t._dim != -1)) return false; + // same type, this is present or both optional + if (_ti == t._ti && bt_subtype(*this, t, strictEnums) && _st == t._st) + return _ot == OT_PRESENT || _ot == t._ot; + // this is par other than that same type as t + if (_ti == TI_PAR && bt_subtype(*this, t, strictEnums) && _st == t._st) + return _ot == OT_PRESENT || _ot == t._ot; + if (_ti == TI_PAR && t._bt == BT_BOT) return true; + if ((_ti == t._ti || _ti == TI_PAR) && _bt == BT_BOT && (_st == t._st || _st == ST_PLAIN)) + return _ot == OT_PRESENT || _ot == t._ot; + if (t._bt == BT_TOP && (_ot == OT_PRESENT || _ot == t._ot) && + (t._st == ST_PLAIN || _st == t._st) && (_ti == TI_PAR || t._ti == TI_VAR)) + return true; + return false; + } + + /// Compare types + int cmp(const Type& t) const { return toInt() < t.toInt() ? -1 : (toInt() > t.toInt() ? 1 : 0); } +}; + +}; // namespace MiniZinc + +#endif diff --git a/include/minizinc/typecheck.hh b/include/minizinc/typecheck.hh new file mode 100644 index 0000000..a674344 --- /dev/null +++ b/include/minizinc/typecheck.hh @@ -0,0 +1,95 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_TYPECHECK_HH__ +#define __MINIZINC_TYPECHECK_HH__ + +#include +#include +#include + +namespace MiniZinc { + +/// Scoped variable declarations +class Scopes { +protected: + typedef IdMap DeclMap; + struct Scope { + /// Whether this scope is toplevel + bool toplevel; + /// Map from identifiers to declarations + DeclMap m; + /// Constructor + Scope(void) : toplevel(false) {} + }; + /// Stack of scopes + std::vector s; + +public: + /// Constructor + Scopes(void); + + /// Add a variable declaration + void add(EnvI& env, VarDecl* vd); + + /// Push a new scope + void push(bool toplevel); + /// Pop topmost scope + void pop(void); + + /// Return declaration for \a ident, or NULL if not found + VarDecl* find(Id* ident); +}; + +/// Topological sorting of items +class TopoSorter { +public: + typedef std::vector Decls; + typedef std::unordered_map PosMap; + + /// List of all declarations + Decls decls; + /// Scoped declarations + Scopes scopes; + /// Map from declarations to positions + PosMap pos; + /// The model + Model* model; + + TopoSorter(Model* model0) : model(model0) {} + + /// Add a variable declaration item + void add(EnvI& env, VarDeclI* vd, bool handleEnums, Model* enumItems); + /// Get variable declaration from identifier \a id + VarDecl* get(EnvI& env, const ASTString& id, const Location& loc); + + VarDecl* checkId(EnvI& env, const ASTString& ident, const Location& loc); + VarDecl* checkId(EnvI& env, Id* ident, const Location& loc); + /// Run the topological sorting for expression \a e + void run(EnvI& env, Expression* e); +}; + +/// Type check the model \a m +void typecheck(Env& env, Model* m, std::vector& typeErrors, + bool ignoreUndefinedParameters, bool allowMultiAssignment, bool isFlatZinc = false); + +/// Type check new assign item \a ai in model \a m +void typecheck(Env& env, Model* m, AssignI* ai); + +/// Output description of parameters and output variables to \a os +void output_model_interface(Env& env, Model* m, std::ostream& os); + +/// Output information about variable types (enum types) to \a os +void output_model_variable_types(Env& env, Model* m, std::ostream& os); + +} // namespace MiniZinc + +#endif diff --git a/include/minizinc/utils.hh b/include/minizinc/utils.hh new file mode 100644 index 0000000..61ed243 --- /dev/null +++ b/include/minizinc/utils.hh @@ -0,0 +1,203 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_UTILS_H__ +#define __MINIZINC_UTILS_H__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MZN_HAS_LLROUND +#include +namespace MiniZinc { +inline long long int round_to_longlong(double v) { return ::llround(v); } +} // namespace MiniZinc +#else +namespace MiniZinc { +inline long long int round_to_longlong(double v) { + return static_cast(v < 0 ? v - 0.5 : v + 0.5); +} +} // namespace MiniZinc +#endif + +namespace MiniZinc { + +// #define __MZN_PRINTATONCE__ +#ifdef __MZN_PRINTATONCE__ +#define __MZN_PRINT_SRCLOC(e1, e2) \ + std::cerr << '\n' \ + << __FILE__ << ": " << __LINE__ << " (" << __func__ << "): not " << e1 << ": " \ + << std::flush; \ + std::cerr << e2 << std::endl +#else +#define __MZN_PRINT_SRCLOC(e1, e2) +#endif +#define MZN_ASSERT_HARD(c) \ + do { \ + if (!(c)) { \ + __MZN_PRINT_SRCLOC(#c, ""); \ + throw InternalError(#c); \ + } \ + } while (0) +#define MZN_ASSERT_HARD_MSG(c, e) \ + do { \ + if (!(c)) { \ + __MZN_PRINT_SRCLOC(#c, e); \ + std::ostringstream oss; \ + oss << "not " << #c << ": " << e; \ + throw MiniZinc::InternalError(oss.str()); \ + } \ + } while (0) + +inline bool beginswith(std::string s, std::string t) { return s.compare(0, t.length(), t) == 0; } + +inline void checkIOStatus(bool fOk, std::string msg, bool fHard = 1) { + if (!fOk) { +#ifdef _MSC_VER + char errBuf[1024]; + strerror_s(errBuf, sizeof(errBuf), errno); +#else + char* errBuf = strerror(errno); +#endif + std::cerr << "\n " << msg << ": " << errBuf << "." << std::endl; + MZN_ASSERT_HARD_MSG(!fHard, msg << ": " << errBuf); + } +} + +template +inline bool assignStr(T*, const std::string) { + return false; +} +template <> +inline bool assignStr(std::string* pS, const std::string s) { + *pS = s; + return true; +} + +/// A simple per-cmdline option parser +class CLOParser { + int& i; // current item + std::vector& argv; + +public: + CLOParser(int& ii, std::vector& av) : i(ii), argv(av) {} + template + inline bool get(const char* names, // space-separated option list + Value* pResult = nullptr, // pointer to value storage + bool fValueOptional = false // if pResult, for non-string values + ) { + return getOption(names, pResult, fValueOptional); + } + template + inline bool getOption(const char* names, // space-separated option list + Value* pResult = nullptr, // pointer to value storage + bool fValueOptional = false // if pResult, for non-string values + ) { + assert(0 == strchr(names, ',')); + assert(0 == strchr(names, ';')); + if (i >= argv.size()) return false; + std::string arg(argv[i]); + /// Separate keywords + std::string keyword; + std::istringstream iss(names); + while (iss >> keyword) { + if (((2 < keyword.size() || 0 == pResult) && arg != keyword) || // exact cmp + (0 != arg.compare(0, keyword.size(), keyword))) // truncated cmp + continue; + /// Process it + bool combinedArg = false; // whether arg and value are combined in one string (like -Ggecode) + if (keyword.size() < arg.size()) { + if (0 == pResult) continue; + combinedArg = true; + arg.erase(0, keyword.size()); + } else { + if (0 == pResult) return true; + i++; + if (i >= argv.size()) { + --i; + return fValueOptional; + } + arg = argv[i]; + } + assert(pResult); + if (assignStr(pResult, arg)) return true; + std::istringstream iss(arg); + Value tmp; + if (!(iss >> tmp)) { + if (!combinedArg) --i; + if (fValueOptional) { + return true; + } + // Not print because another agent can handle this option + // cerr << "\nBad value for " << keyword << ": " << arg << endl; + return false; + } + *pResult = tmp; + return true; + } + return false; + } +}; // class CLOParser + +/// This class prints a value if non-0 and adds comma if not 1st time +class HadOne { + bool fHadOne = false; + +public: + template + std::string operator()(const N& val, const char* descr = 0) { + std::ostringstream oss; + if (val) { + if (fHadOne) oss << ", "; + fHadOne = true; + oss << val; + if (descr) oss << descr; + } + return oss.str(); + } + void reset() { fHadOne = false; } + operator bool() const { return fHadOne; } + bool operator!() const { return !fHadOne; } +}; + +/// Split a string into words +/// Add the words into the given vector +inline void split(const std::string& str, std::vector& words) { + std::istringstream iss(str); + std::string buf; + while (iss) { + iss >> buf; + words.push_back(buf); + } +} + +/// Puts the strings' c_str()s into the 2nd argument. +/// The latter is only valid as long as the former isn't changed. +inline void vecString2vecPChar(const std::vector& vS, std::vector& vPC) { + vPC.resize(vS.size()); + for (size_t i = 0; i < vS.size(); ++i) { + vPC[i] = vS[i].c_str(); + } +} + +} // namespace MiniZinc + +#endif // __MINIZINC_UTILS_H__ diff --git a/include/minizinc/utils_savestream.hh b/include/minizinc/utils_savestream.hh new file mode 100644 index 0000000..8c2779c --- /dev/null +++ b/include/minizinc/utils_savestream.hh @@ -0,0 +1,52 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_UTILS_SAVESTREAM_H__ +#define __MINIZINC_UTILS_SAVESTREAM_H__ + +#include + +namespace MiniZinc { + +/// Helper class to redirect, e.g., stdout to stderr +class StreamRedir { + /// The stream to be changed + FILE* const d_s0; + /* + * Structure for retaining information about a stream, sufficient to + * recreate that stream later on. + * See + * https://stackoverflow.com/questions/4760201/how-do-i-suppress-output-while-using-a-dynamic-library + */ + struct stream_info { + int fd = -1; + fpos_t pos; + }; + /// The original stream + stream_info d_si; + +public: + /// Constructs with the stream to be changed + // StreamRedir(FILE* s0); + /// Constructs with s0 and replaces it by s1 + StreamRedir(FILE* s0, FILE* s1, bool fFlush = true); + ~StreamRedir(); + /// Restore original + void restore(bool fFLush = true); + +protected: + /// Replace & save stream by s1 + void replaceStream(FILE* s1, bool fFlush = true); +}; + +} // namespace MiniZinc + +#endif // __MINIZINC_UTILS_SAVESTREAM_H__ diff --git a/include/minizinc/values.hh b/include/minizinc/values.hh new file mode 100644 index 0000000..0733541 --- /dev/null +++ b/include/minizinc/values.hh @@ -0,0 +1,880 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_VALUES_HH__ +#define __MINIZINC_VALUES_HH__ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef min +#undef min +#endif +#ifdef max +#undef max +#endif + +namespace MiniZinc { +class IntVal; +} +namespace std { +MiniZinc::IntVal abs(const MiniZinc::IntVal& x); +} + +namespace MiniZinc { + +class FloatVal; + +class IntVal { + friend IntVal operator+(const IntVal& x, const IntVal& y); + friend IntVal operator-(const IntVal& x, const IntVal& y); + friend IntVal operator*(const IntVal& x, const IntVal& y); + friend IntVal operator/(const IntVal& x, const IntVal& y); + friend IntVal operator%(const IntVal& x, const IntVal& y); + friend IntVal std::abs(const MiniZinc::IntVal& x); + friend bool operator==(const IntVal& x, const IntVal& y); + friend class FloatVal; + +private: + long long int _v; + bool _infinity; + IntVal(long long int v, bool infinity) : _v(v), _infinity(infinity) {} + + static long long int safePlus(long long int x, long long int y) { + if (x < 0) { + if (y < std::numeric_limits::min() - x) + throw ArithmeticError("integer overflow"); + } else { + if (y > std::numeric_limits::max() - x) + throw ArithmeticError("integer overflow"); + } + return x + y; + } + static long long int safeMinus(long long int x, long long int y) { + if (x < 0) { + if (y > x - std::numeric_limits::min()) + throw ArithmeticError("integer overflow"); + } else { + if (y < x - std::numeric_limits::max()) + throw ArithmeticError("integer overflow"); + } + return x - y; + } + static long long int safeMult(long long int x, long long int y) { + if (y == 0) return 0; + long long unsigned int x_abs = (x < 0 ? 0 - x : x); + long long unsigned int y_abs = (y < 0 ? 0 - y : y); + if (x_abs > std::numeric_limits::max() / y_abs) + throw ArithmeticError("integer overflow"); + return x * y; + } + static long long int safeDiv(long long int x, long long int y) { + if (y == 0) throw ArithmeticError("integer division by zero"); + if (x == 0) return 0; + if (x == std::numeric_limits::min() && y == -1) + throw ArithmeticError("integer overflow"); + return x / y; + } + static long long int safeMod(long long int x, long long int y) { + if (y == 0) throw ArithmeticError("integer division by zero"); + if (y == -1) return 0; + return x % y; + } + +public: + IntVal(void) : _v(0), _infinity(false) {} + IntVal(long long int v) : _v(v), _infinity(false) {} + IntVal(const FloatVal& v); + + long long int toInt(void) const { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + return _v; + } + + long long int toIntUnsafe(void) const; + + bool isFinite(void) const { return !_infinity; } + bool isPlusInfinity(void) const { return _infinity && _v == 1; } + bool isMinusInfinity(void) const { return _infinity && _v == -1; } + + IntVal& operator+=(const IntVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v = safePlus(_v, x._v); + return *this; + } + IntVal& operator-=(const IntVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v = safeMinus(_v, x._v); + return *this; + } + IntVal& operator*=(const IntVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v = safeMult(_v, x._v); + return *this; + } + IntVal& operator/=(const IntVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v = safeDiv(_v, x._v); + return *this; + } + IntVal operator-() const { + IntVal r = *this; + r._v = safeMinus(0, _v); + return r; + } + IntVal& operator++() { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + _v = safePlus(_v, 1); + return *this; + } + IntVal operator++(int) { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + IntVal ret = *this; + _v = safePlus(_v, 1); + return ret; + } + IntVal& operator--() { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + _v = safeMinus(_v, 1); + return *this; + } + IntVal operator--(int) { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + IntVal ret = *this; + _v = safeMinus(_v, 1); + return ret; + } + IntVal pow(const IntVal& exponent) { + if (!exponent.isFinite() || !isFinite()) + throw ArithmeticError("arithmetic operation on infinite value"); + if (exponent == 0) return 1; + if (exponent == 1) return *this; + IntVal result = 1; + for (int i = 0; i < exponent.toInt(); i++) { + result *= *this; + } + return result; + } + + static const IntVal minint(void); + static const IntVal maxint(void); + static const IntVal infinity(void); + + /// Infinity-safe addition + IntVal plus(int x) const { + if (isFinite()) + return safePlus(_v, x); + else + return *this; + } + /// Infinity-safe subtraction + IntVal minus(int x) const { + if (isFinite()) + return safeMinus(_v, x); + else + return *this; + } + + size_t hash(void) const { + std::hash longhash; + return longhash(_v); + } +}; + +inline long long int IntVal::toIntUnsafe(void) const { return _v; } + +inline bool operator==(const IntVal& x, const IntVal& y) { + return x._infinity == y._infinity && x._v == y._v; +} +inline bool operator<=(const IntVal& x, const IntVal& y) { + return y.isPlusInfinity() || x.isMinusInfinity() || + (x.isFinite() && y.isFinite() && x.toInt() <= y.toInt()); +} +inline bool operator<(const IntVal& x, const IntVal& y) { + return (y.isPlusInfinity() && !x.isPlusInfinity()) || + (x.isMinusInfinity() && !y.isMinusInfinity()) || + (x.isFinite() && y.isFinite() && x.toInt() < y.toInt()); +} +inline bool operator>=(const IntVal& x, const IntVal& y) { return y <= x; } +inline bool operator>(const IntVal& x, const IntVal& y) { return y < x; } +inline bool operator!=(const IntVal& x, const IntVal& y) { return !(x == y); } +inline IntVal operator+(const IntVal& x, const IntVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return IntVal::safePlus(x._v, y._v); +} +inline IntVal operator-(const IntVal& x, const IntVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return IntVal::safeMinus(x._v, y._v); +} +inline IntVal operator*(const IntVal& x, const IntVal& y) { + if (!x.isFinite()) { + if (y.isFinite() && (y._v == 1 || y._v == -1)) + return IntVal(IntVal::safeMult(x._v, y._v), !x.isFinite()); + } else if (!y.isFinite()) { + if (x.isFinite() && (y._v == 1 || y._v == -1)) + return IntVal(IntVal::safeMult(x._v, y._v), true); + } else { + return IntVal::safeMult(x._v, y._v); + } + throw ArithmeticError("arithmetic operation on infinite value"); +} +inline IntVal operator/(const IntVal& x, const IntVal& y) { + if (y.isFinite() && (y._v == 1 || y._v == -1)) + return IntVal(IntVal::safeMult(x._v, y._v), !x.isFinite()); + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return IntVal::safeDiv(x._v, y._v); +} +inline IntVal operator%(const IntVal& x, const IntVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return IntVal::safeMod(x._v, y._v); +} +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const IntVal& s) { + if (s.isMinusInfinity()) + return os << "-infinity"; + else if (s.isPlusInfinity()) + return os << "infinity"; + else + return os << s.toInt(); +} + +} // namespace MiniZinc + +namespace std { +inline MiniZinc::IntVal abs(const MiniZinc::IntVal& x) { + if (!x.isFinite()) return MiniZinc::IntVal::infinity(); + return x < 0 ? MiniZinc::IntVal::safeMinus(0, x._v) : x; +} + +inline MiniZinc::IntVal min(const MiniZinc::IntVal& x, const MiniZinc::IntVal& y) { + return x <= y ? x : y; +} +inline MiniZinc::IntVal max(const MiniZinc::IntVal& x, const MiniZinc::IntVal& y) { + return x >= y ? x : y; +} + +template <> +struct equal_to { +public: + bool operator()(const MiniZinc::IntVal& s0, const MiniZinc::IntVal& s1) const { return s0 == s1; } +}; + +inline MiniZinc::FloatVal abs(const MiniZinc::FloatVal&); +} // namespace std + +namespace std { +template <> +struct hash { +public: + size_t operator()(const MiniZinc::IntVal& s) const { return s.hash(); } +}; +} // namespace std + +namespace MiniZinc { + +class FloatVal { + friend FloatVal operator+(const FloatVal& x, const FloatVal& y); + friend FloatVal operator-(const FloatVal& x, const FloatVal& y); + friend FloatVal operator*(const FloatVal& x, const FloatVal& y); + friend FloatVal operator/(const FloatVal& x, const FloatVal& y); + friend FloatVal std::abs(const MiniZinc::FloatVal& x); + friend bool operator==(const FloatVal& x, const FloatVal& y); + friend class IntVal; + +private: + double _v; + bool _infinity; + void checkOverflow(void) { + if (!std::isfinite(_v)) throw ArithmeticError("overflow in floating point operation"); + } + FloatVal(double v, bool infinity) : _v(v), _infinity(infinity) { checkOverflow(); } + +public: + FloatVal(void) : _v(0.0), _infinity(false) {} + FloatVal(double v) : _v(v), _infinity(false) { checkOverflow(); } + FloatVal(const IntVal& v) : _v(static_cast(v._v)), _infinity(!v.isFinite()) {} + + double toDouble(void) const { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + return _v; + } + + bool isFinite(void) const { return !_infinity; } + bool isPlusInfinity(void) const { return _infinity && _v == 1.0; } + bool isMinusInfinity(void) const { return _infinity && _v == -1.0; } + + FloatVal& operator+=(const FloatVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v += x._v; + checkOverflow(); + return *this; + } + FloatVal& operator-=(const FloatVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v -= x._v; + checkOverflow(); + return *this; + } + FloatVal& operator*=(const FloatVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v *= x._v; + checkOverflow(); + return *this; + } + FloatVal& operator/=(const FloatVal& x) { + if (!(isFinite() && x.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + _v = _v / x._v; + checkOverflow(); + return *this; + } + FloatVal operator-() const { + FloatVal r = *this; + r._v = -r._v; + return r; + } + FloatVal& operator++() { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + _v = _v + 1; + checkOverflow(); + return *this; + } + FloatVal operator++(int) { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + FloatVal ret = *this; + _v = _v + 1; + checkOverflow(); + return ret; + } + FloatVal& operator--() { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + _v = _v - 1; + checkOverflow(); + return *this; + } + FloatVal operator--(int) { + if (!isFinite()) throw ArithmeticError("arithmetic operation on infinite value"); + FloatVal ret = *this; + _v = _v - 1; + checkOverflow(); + return ret; + } + + static const FloatVal infinity(void); + + /// Infinity-safe addition + FloatVal plus(int x) { + if (isFinite()) + return (*this) + x; + else + return *this; + } + /// Infinity-safe subtraction + FloatVal minus(int x) { + if (isFinite()) + return (*this) - x; + else + return *this; + } + + size_t hash(void) const { + std::hash doublehash; + return doublehash(_v); + } +}; + +inline bool operator==(const FloatVal& x, const FloatVal& y) { + return x._infinity == y._infinity && x._v == y._v; +} +inline bool operator<=(const FloatVal& x, const FloatVal& y) { + return y.isPlusInfinity() || x.isMinusInfinity() || + (x.isFinite() && y.isFinite() && x.toDouble() <= y.toDouble()); +} +inline bool operator<(const FloatVal& x, const FloatVal& y) { + return (y.isPlusInfinity() && !x.isPlusInfinity()) || + (x.isMinusInfinity() && !y.isMinusInfinity()) || + (x.isFinite() && y.isFinite() && x.toDouble() < y.toDouble()); +} +inline bool operator>=(const FloatVal& x, const FloatVal& y) { return y <= x; } +inline bool operator>(const FloatVal& x, const FloatVal& y) { return y < x; } +inline bool operator!=(const FloatVal& x, const FloatVal& y) { return !(x == y); } +inline FloatVal operator+(const FloatVal& x, const FloatVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return x.toDouble() + y.toDouble(); +} +inline FloatVal operator-(const FloatVal& x, const FloatVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return x.toDouble() - y.toDouble(); +} +inline FloatVal operator*(const FloatVal& x, const FloatVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return x.toDouble() * y.toDouble(); +} +inline FloatVal operator/(const FloatVal& x, const FloatVal& y) { + if (!(x.isFinite() && y.isFinite())) + throw ArithmeticError("arithmetic operation on infinite value"); + return x.toDouble() / y.toDouble(); +} +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const FloatVal& s) { + if (s.isMinusInfinity()) + return os << "-infinity"; + else if (s.isPlusInfinity()) + return os << "infinity"; + else + return os << s.toDouble(); +} + +inline IntVal::IntVal(const FloatVal& v) + : _v(static_cast(v._v)), _infinity(!v.isFinite()) {} + +} // namespace MiniZinc + +namespace std { +inline MiniZinc::FloatVal abs(const MiniZinc::FloatVal& x) { + if (!x.isFinite()) return MiniZinc::FloatVal::infinity(); + return x.toDouble() < 0 ? MiniZinc::FloatVal(-x.toDouble()) : x; +} + +inline MiniZinc::FloatVal min(const MiniZinc::FloatVal& x, const MiniZinc::FloatVal& y) { + return x <= y ? x : y; +} +inline MiniZinc::FloatVal max(const MiniZinc::FloatVal& x, const MiniZinc::FloatVal& y) { + return x >= y ? x : y; +} + +inline MiniZinc::FloatVal floor(const MiniZinc::FloatVal& x) { + if (!x.isFinite()) return x; + return floor(x.toDouble()); +} +inline MiniZinc::FloatVal ceil(const MiniZinc::FloatVal& x) { + if (!x.isFinite()) return x; + return ceil(x.toDouble()); +} + +template <> +struct equal_to { +public: + bool operator()(const MiniZinc::FloatVal& s0, const MiniZinc::FloatVal& s1) const { + return s0 == s1; + } +}; +} // namespace std + +namespace std { +template <> +struct hash { +public: + size_t operator()(const MiniZinc::FloatVal& s) const { return s.hash(); } +}; +} // namespace std + +namespace MiniZinc { + +typedef unsigned long long int UIntVal; + +/// An integer set value +class IntSetVal : public ASTChunk { +public: + /// Contiguous range + struct Range { + /// Range minimum + IntVal min; + /// Range maximum + IntVal max; + /// Construct range from \a m to \a n + Range(IntVal m, IntVal n) : min(m), max(n) {} + /// Default constructor + Range(void) {} + }; + +private: + /// Return range at position \a i + Range& get(int i) { return reinterpret_cast(_data)[i]; } + /// Return range at position \a i + const Range& get(int i) const { return reinterpret_cast(_data)[i]; } + /// Construct empty set + IntSetVal(void) : ASTChunk(0) {} + /// Construct set of single range + IntSetVal(IntVal m, IntVal n); + /// Construct set from \a s + IntSetVal(const std::vector& s) : ASTChunk(sizeof(Range) * s.size()) { + for (unsigned int i = static_cast(s.size()); i--;) get(i) = s[i]; + } + + /// Disabled + IntSetVal(const IntSetVal& r); + /// Disabled + IntSetVal& operator=(const IntSetVal& r); + +public: + /// Return number of ranges + int size(void) const { return static_cast(_size / sizeof(Range)); } + /// Return minimum, or infinity if set is empty + IntVal min(void) const { return size() == 0 ? IntVal::infinity() : get(0).min; } + /// Return maximum, or minus infinity if set is empty + IntVal max(void) const { return size() == 0 ? -IntVal::infinity() : get(size() - 1).max; } + /// Return minimum of range \a i + IntVal min(int i) const { + assert(i < size()); + return get(i).min; + } + /// Return maximum of range \a i + IntVal max(int i) const { + assert(i < size()); + return get(i).max; + } + /// Return width of range \a i + IntVal width(int i) const { + assert(i < size()); + if (min(i).isFinite() && max(i).isFinite()) + return max(i) - min(i) + 1; + else + return IntVal::infinity(); + } + /// Return cardinality + IntVal card(void) const { + IntVal c = 0; + for (unsigned int i = size(); i--;) { + if (width(i).isFinite()) + c += width(i); + else + return IntVal::infinity(); + } + return c; + } + + /// Allocate empty set from context + static IntSetVal* a(void) { + IntSetVal* r = static_cast(ASTChunk::alloc(0)); + new (r) IntSetVal(); + return r; + } + + /// Allocate set \f$\{m,n\}\f$ from context + static IntSetVal* a(IntVal m, IntVal n) { + if (m > n) { + return a(); + } else { + IntSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range))); + new (r) IntSetVal(m, n); + return r; + } + } + + /// Allocate set using iterator \a i + template + static IntSetVal* ai(I& i) { + std::vector s; + for (; i(); ++i) s.push_back(Range(i.min(), i.max())); + IntSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range) * s.size())); + new (r) IntSetVal(s); + return r; + } + + /// Allocate set from vector \a s0 (may contain duplicates) + static IntSetVal* a(const std::vector& s0) { + if (s0.size() == 0) return a(); + std::vector s = s0; + std::sort(s.begin(), s.end()); + std::vector ranges; + IntVal min = s[0]; + IntVal max = min; + for (unsigned int i = 1; i < s.size(); i++) { + if (s[i] > max + 1) { + ranges.push_back(Range(min, max)); + min = s[i]; + max = min; + } else { + max = s[i]; + } + } + ranges.push_back(Range(min, max)); + IntSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range) * ranges.size())); + new (r) IntSetVal(ranges); + return r; + } + static IntSetVal* a(const std::vector& ranges) { + IntSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range) * ranges.size())); + new (r) IntSetVal(ranges); + return r; + } + + /// Check if set contains \a v + bool contains(const IntVal& v) { + for (int i = 0; i < size(); i++) { + if (v < min(i)) return false; + if (v <= max(i)) return true; + } + return false; + } + + /// Check if it is equal to \a s + bool equal(const IntSetVal* s) { + if (size() != s->size()) return false; + for (int i = 0; i < size(); i++) + if (min(i) != s->min(i) || max(i) != s->max(i)) return false; + return true; + } + + /// Mark for garbage collection + void mark(void) { _gc_mark = 1; } +}; + +/// Iterator over an IntSetVal +class IntSetRanges { + /// The set value + const IntSetVal* rs; + /// The current range + int n; + +public: + /// Constructor + IntSetRanges(const IntSetVal* r) : rs(r), n(0) {} + /// Check if iterator is still valid + bool operator()(void) const { return n < rs->size(); } + /// Move to next range + void operator++(void) { ++n; } + /// Return minimum of current range + IntVal min(void) const { return rs->min(n); } + /// Return maximum of current range + IntVal max(void) const { return rs->max(n); } + /// Return width of current range + IntVal width(void) const { return rs->width(n); } +}; + +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const IntSetVal& s) { + if (s.size() == 0) { + os << "1..0"; + } else if (s.size() == 1) { + // Print the range + IntSetRanges isr(&s); + os << isr.min() << ".." << isr.max(); + } else { + // Print each element of the set + bool first = true; + os << "{"; + for (IntSetRanges isr(&s); isr(); ++isr) { + if (!first) os << ", "; + first = false; + for (IntVal v = isr.min(); v < isr.max(); ++v) { + os << v; + } + } + os << "}"; + } + return os; +} + +/// An integer set value +class FloatSetVal : public ASTChunk { +public: + /// Contiguous range + struct Range { + /// Range minimum + FloatVal min; + /// Range maximum + FloatVal max; + /// Construct range from \a m to \a n + Range(FloatVal m, FloatVal n) : min(m), max(n) {} + /// Default constructor + Range(void) {} + }; + +private: + /// Return range at position \a i + Range& get(int i) { return reinterpret_cast(_data)[i]; } + /// Return range at position \a i + const Range& get(int i) const { return reinterpret_cast(_data)[i]; } + /// Construct empty set + FloatSetVal(void) : ASTChunk(0) {} + /// Construct set of single range + FloatSetVal(FloatVal m, FloatVal n); + /// Construct set from \a s + FloatSetVal(const std::vector& s) : ASTChunk(sizeof(Range) * s.size()) { + for (unsigned int i = static_cast(s.size()); i--;) get(i) = s[i]; + } + + /// Disabled + FloatSetVal(const FloatSetVal& r); + /// Disabled + FloatSetVal& operator=(const FloatSetVal& r); + +public: + /// Return number of ranges + int size(void) const { return static_cast(_size / sizeof(Range)); } + /// Return minimum, or infinity if set is empty + FloatVal min(void) const { return size() == 0 ? FloatVal::infinity() : get(0).min; } + /// Return maximum, or minus infinity if set is empty + FloatVal max(void) const { return size() == 0 ? -FloatVal::infinity() : get(size() - 1).max; } + /// Return minimum of range \a i + FloatVal min(int i) const { + assert(i < size()); + return get(i).min; + } + /// Return maximum of range \a i + FloatVal max(int i) const { + assert(i < size()); + return get(i).max; + } + /// Return width of range \a i + FloatVal width(int i) const { + assert(i < size()); + if (min(i).isFinite() && max(i).isFinite() && min(i) == max(i)) + return 1; + else + return IntVal::infinity(); + } + /// Return cardinality + FloatVal card(void) const { + FloatVal c = 0; + for (unsigned int i = size(); i--;) { + if (width(i).isFinite()) + c += width(i); + else + return FloatVal::infinity(); + } + return c; + } + + /// Allocate empty set from context + static FloatSetVal* a(void) { + FloatSetVal* r = static_cast(ASTChunk::alloc(0)); + new (r) FloatSetVal(); + return r; + } + + /// Allocate set \f$\{m,n\}\f$ from context + static FloatSetVal* a(FloatVal m, FloatVal n) { + if (m > n) { + return a(); + } else { + FloatSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range))); + new (r) FloatSetVal(m, n); + return r; + } + } + + /// Allocate set using iterator \a i + template + static FloatSetVal* ai(I& i) { + std::vector s; + for (; i(); ++i) s.push_back(Range(i.min(), i.max())); + FloatSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range) * s.size())); + new (r) FloatSetVal(s); + return r; + } + + /// Allocate set from vector \a s0 (may contain duplicates) + static FloatSetVal* a(const std::vector& s0) { + if (s0.size() == 0) return a(); + std::vector s = s0; + std::sort(s.begin(), s.end()); + std::vector ranges; + FloatVal min = s[0]; + FloatVal max = min; + for (unsigned int i = 1; i < s.size(); i++) { + if (s[i] > max) { + ranges.push_back(Range(min, max)); + min = s[i]; + max = min; + } else { + max = s[i]; + } + } + ranges.push_back(Range(min, max)); + FloatSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range) * ranges.size())); + new (r) FloatSetVal(ranges); + return r; + } + static FloatSetVal* a(const std::vector& ranges) { + FloatSetVal* r = static_cast(ASTChunk::alloc(sizeof(Range) * ranges.size())); + new (r) FloatSetVal(ranges); + return r; + } + + /// Check if set contains \a v + bool contains(const FloatVal& v) { + for (int i = 0; i < size(); i++) { + if (v < min(i)) return false; + if (v <= max(i)) return true; + } + return false; + } + + /// Check if it is equal to \a s + bool equal(const FloatSetVal* s) { + if (size() != s->size()) return false; + for (int i = 0; i < size(); i++) + if (min(i) != s->min(i) || max(i) != s->max(i)) return false; + return true; + } + + /// Mark for garbage collection + void mark(void) { _gc_mark = 1; } +}; + +/// Iterator over an IntSetVal +class FloatSetRanges { + /// The set value + const FloatSetVal* rs; + /// The current range + int n; + +public: + /// Constructor + FloatSetRanges(const FloatSetVal* r) : rs(r), n(0) {} + /// Check if iterator is still valid + bool operator()(void) const { return n < rs->size(); } + /// Move to next range + void operator++(void) { ++n; } + /// Return minimum of current range + FloatVal min(void) const { return rs->min(n); } + /// Return maximum of current range + FloatVal max(void) const { return rs->max(n); } + /// Return width of current range + FloatVal width(void) const { return rs->width(n); } +}; + +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const FloatSetVal& s) { + for (FloatSetRanges isr(&s); isr(); ++isr) os << isr.min() << ".." << isr.max() << " "; + return os; +} +} // namespace MiniZinc + +#endif diff --git a/interfaces/python/Annotation.h b/interfaces/python/Annotation.h new file mode 100644 index 0000000..a4d6407 --- /dev/null +++ b/interfaces/python/Annotation.h @@ -0,0 +1,65 @@ +#ifndef __Mzn_ANNOTATIONOBJECT_H +#define __Mzn_ANNOTATIONOBJECT_H + +#include "Expression.h" + +struct MznAnnotation : MznExpression {}; + +/*inline Expression* MznObject_get_e(MznAnnotation* self) { return +MznObject_get_e(reinterpret_cast(self)); } inline void +MznObject_set_e(MznAnnotation* self, Expression* e0) { +MznObject_set_e(reinterpret_cast(self), e0); }*/ + +static PyObject* MznAnnotation_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + MznAnnotation* self = reinterpret_cast(type->tp_alloc(type, 0)); + reinterpret_cast(self)->tid = MOC_ANN; + self->e = NULL; + return reinterpret_cast(self); +} + +static void MznAnnotation_dealloc(MznAnnotation* self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyTypeObject MznAnnotation_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc.Annotation", /* tp_name */ + sizeof(MznAnnotation), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznAnnotation_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Minizinc Variable (derived from MznExpression)", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /*MznAnnotation_getseters, /* tp_getset */ + &MznExpression_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + MznAnnotation_new, /* tp_new */ + 0, /* tp_free */ +}; + +#endif \ No newline at end of file diff --git a/interfaces/python/Changelog.txt b/interfaces/python/Changelog.txt new file mode 100644 index 0000000..0759421 --- /dev/null +++ b/interfaces/python/Changelog.txt @@ -0,0 +1,54 @@ +1. Calling solve with timer and solver (done) + + For simplicity, default solver must be set in minizinc.py + It's easier and clearer to change the default solver to be used: + In minizinc.py, class Model, there are 3 lines: + def satisfy(self, ann = None, data = None, solver = default_solver, time = 0) + def maximize(self, expr, ann = None, data = None, solver = default_solver, time = 0) + def minimize(self, expr, ann = None, data = None, solver = default_solver, time = 0): + + To change the default solver to, say Gecode, we change the line + solver = default_solver + to + solver = 'gecode' + + + NOTE: to pass the responsibility of specifying the default solver to C++ files: + in minizinc.py: change solver = default_solver --> solver = '' + in Model.h: change default_solver = SC_UNKNOWN + -> default_solver = SC_GECODE + +2. Updated arguments type checking: + A type of None indicates that it accepts any possible type + + XXX: what if users enter a list or tuple to that position of None? + XXX: Python Interface type checking is getting really clumpsy and expensive now + +---------------------------------------- + +3. Internal variable name declaration is now removed: + Sets, Variables and Arrays are previously assigned with an internal name when created (this is the memory address of the objects) + Users can change the objects' name during declaring phase: + instead of: + a = model.Variable(1,10) + we use: + a = model.Variable(1,10,'a') + and the object will have internal name 'a' + As we don't care about the internal names, this turns out to be useless and thus is now removed + +4. Changes to minizinc_internal Set initialization: + Is now minizinc_internal.Set( [1,5], 7, [10,15] ) + instead of minizinc_internal.Set( ([1,5], 7, [10,15]) ) + +5. Set is now model independent and can be reused across multiple models + Set.model = None + +6. Introducing Model.Range: + Model.Range accepts up to 2 arguments: + ub: returns a MiniZinc Set from 0 to ub - 1 + lb, ub: returns a MiniZinc Set from lb to ub + Model.Set initialization reworked: + previously initialized with: Set( (1,2,[3,4],5) ) + now initialized with: Set( 1,2,[3,4],5 ) + or: Set( 1,2,(3,4),5 ) + Model.Set.push adds more elements onto the Set, works the same as the initialization function \ No newline at end of file diff --git a/interfaces/python/Expression.h b/interfaces/python/Expression.h new file mode 100644 index 0000000..b2cbea9 --- /dev/null +++ b/interfaces/python/Expression.h @@ -0,0 +1,65 @@ +#ifndef __Mzn_EXPRESSIONOBJECT_H +#define __Mzn_EXPRESSIONOBJECT_H + +#include "Object.h" + +struct MznExpression : MznObject { + Expression* e; +}; + +/*inline Expression* MznObject_get_e(MznExpression* self) { return self->e; } +inline void MznObject_set_e(MznExpression* self, Expression* e0) { self->e = e0; }*/ + +static PyObject* MznExpression_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + MznExpression* self = reinterpret_cast(type->tp_alloc(type, 0)); + reinterpret_cast(self)->tid = MOC_EXPR; + self->e = NULL; + return reinterpret_cast(self); +} + +static void MznExpression_dealloc(MznExpression* self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyTypeObject MznExpression_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc.Expression", /* tp_name */ + sizeof(MznExpression), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznExpression_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Minizinc Expression Object (derived from MznObject)", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &MznObject_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + MznExpression_new, /* tp_new */ + 0, /* tp_free */ +}; + +#endif \ No newline at end of file diff --git a/interfaces/python/Model.cpp b/interfaces/python/Model.cpp new file mode 100644 index 0000000..5fb0120 --- /dev/null +++ b/interfaces/python/Model.cpp @@ -0,0 +1,694 @@ +#include "Model.h" + +using namespace MiniZinc; +using namespace std; + +int MznModel::addData(const char* const name, PyObject* value) { + GCLock Lock; + if (value == NULL) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Model.addData: Received a NULL value"); + return -1; + } + for (unsigned int i = 0; i < _m->size(); i++) + if (VarDeclI* vdi = (*_m)[i]->dyn_cast()) { + if (strcmp(vdi->e()->id()->str().c_str(), name) == 0) { + vector > dimList; + Type type; + Expression* rhs = + python_to_minizinc(value, vdi->e()->ti()->ranges()); //, vdi->e()->type(), name); + if (rhs == NULL) return -1; + vdi->e()->e(rhs); + return 0; + } + } + MZN_PYERR_SET_STRING(PyExc_TypeError, "MiniZinc: Model.addData: Undefined name '%s'", name); + return -1; +} + +int MznModel::load(PyObject* args, PyObject* keywds, bool fromFile) { + GCLock Lock; + Model* saveModel = _m; + stringstream errorStream; + vector data; + + PyObject* obj = Py_None; + char* options = NULL; + const char* py_string; + char* errorFile = "./error.txt"; + + bool isDict = false; + + if (fromFile) { + char* kwlist[] = {"file", "data", "options"}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|Os", kwlist, &py_string, &obj, &options)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: load: Parsing error"); + return -1; + } + if (options != NULL) { + char* pch; + bool t_flag = false; + pch = strtok(options, " "); + while (pch != NULL) { + if (strcmp(pch, "-t") == 0) + t_flag = true; + else { + if (t_flag) { + char* ptr; + int t = strtol(pch, &ptr, 10); + if (t == 0) { + PyErr_SetString(PyExc_ValueError, + "MiniZinc: Model.load: Time value must be a valid positive number"); + return -1; + } + timeLimit = t; + t_flag = false; + } else { + PyErr_SetString(PyExc_ValueError, "MiniZinc: Model.load: Unknown option"); + return -1; + } + } + pch = strtok(NULL, " "); + } + } + if (obj != Py_None) { + if (PyUnicode_Check(obj)) { + data.push_back(string(PyUnicode_AsUTF8(obj))); + } else if (PyList_Check(obj)) { + Py_ssize_t n = PyList_GET_SIZE(obj); + for (Py_ssize_t i = 0; i != n; ++i) { + const char* name = PyUnicode_AsUTF8(PyList_GET_ITEM(obj, i)); + if (name == NULL) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.load: Element in the list must be a filename"); + return -1; + } + data.push_back(string(name)); + } + } else if (PyDict_Check(obj)) { + isDict = true; + } else { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.load: The second argument must be either a filename, a " + "list of filenames or a dictionary of data"); + return -1; + } + } + vector models{py_string}; + _m = parse(models, data, *includePaths, false, false, false, errorStream); + _e = new Env(_m); + } else { + char* kwlist[] = {"string", "error", "options"}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|ss", kwlist, &py_string, &errorFile, + &options)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Model.load: Keyword parsing error"); + return -1; + } + _m = parseFromString(string(py_string), errorFile, *includePaths, false, false, false, + errorStream); + _e = new Env(_m); + } + if (_m) { + delete saveModel; + if (isDict) { + stringstream assignments; + Py_ssize_t pos = 0; + PyObject* key; + PyObject* value; + GCLock lock; + while (PyDict_Next(obj, &pos, &key, &value)) { + const char* name = PyUnicode_AsUTF8(key); + if (addData(name, value) == -1) { + // addData handles the error message + return -1; + } + } + } + loaded = true; + return 0; + } else { + const std::string tmp = "MiniZinc: Model.load: " + errorStream.str(); + PyErr_SetString(PyExc_RuntimeError, tmp.c_str()); + return -1; + } +} + +PyObject* MznModel::solve(PyObject* args, PyObject* kwds) { + if (!loaded) { + PyErr_SetString(PyExc_RuntimeError, + "MiniZinc: Model.solve: No data has been loaded into the model"); + return NULL; + } + + PyObject* dict = NULL; + + static char* kwlist[] = {"dict", "solver", "time", NULL}; + + char* solverName = ""; + unsigned long newTimeLimit = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Osk", kwlist, &dict, &solverName, &newTimeLimit)) { + PyErr_SetString(PyExc_RuntimeError, "MiniZinc: Model.solve: Parsing error"); + return NULL; + } + + Model* saveModel; + { + GCLock lock; + saveModel = copy(_e->envi(), _m); + Py_ssize_t pos = 0; + PyObject* key; + PyObject* value; + if (dict) { + while (PyDict_Next(dict, &pos, &key, &value)) { + const char* name = PyUnicode_AsUTF8(key); + if (addData(name, value) == -1) { + // addData handled the error message + goto SOLVE__ERROR_HANDLING; + } + } + } + if (newTimeLimit > 0) { + timeLimit = newTimeLimit; + } + if (strlen(solverName)) { + if (set_solver(solverName) == NULL) { + // setSolver handled the error message + goto SOLVE__ERROR_HANDLING; + } + } + goto SOLVE__NO_ERROR; + SOLVE__ERROR_HANDLING: + delete _m; + _m = saveModel; + _e = new Env(_m); + return NULL; + } + +SOLVE__NO_ERROR: + vector typeErrors; + try { + MiniZinc::typecheck(*_e, _m, typeErrors, false); + } catch (LocationException& e) { + MZN_PYERR_SET_STRING(PyExc_RuntimeError, "MiniZinc: Model.solve: %s: %s", e.what(), + e.msg().c_str()); + return NULL; + } + if (typeErrors.size() > 0) { + stringstream errorLog; + for (unsigned int i = 0; i < typeErrors.size(); i++) { + errorLog << typeErrors[i].loc() << ":" << endl; + errorLog << typeErrors[i].what() << ": " << typeErrors[i].msg() << "\n"; + } + const std::string& tmp = errorLog.str(); + const char* cstr = tmp.c_str(); + PyErr_SetString(PyExc_TypeError, cstr); + return NULL; + } + MiniZinc::registerBuiltins(*_e, _m); + + Env* env = _e; + try { + FlatteningOptions fopts; + flatten(*env, fopts); + } catch (LocationException& e) { + stringstream errorLog; + errorLog << e.what() << ": " << std::endl; + env->dumpErrorStack(errorLog); + errorLog << " " << e.msg() << std::endl; + const std::string& tmp = errorLog.str(); + const char* cstr = tmp.c_str(); + PyErr_SetString(PyExc_RuntimeError, cstr); + return NULL; + } + if (env->warnings().size() != 0) { + stringstream warningLog; + warningLog << "MiniZinc: Model.solve: Warning:\n"; + for (unsigned int i = 0; i < env->warnings().size(); i++) { + warningLog << env->warnings()[i]; + } + const std::string& tmp = warningLog.str(); + const char* cstr = tmp.c_str(); + PyErr_WarnEx(PyExc_RuntimeWarning, cstr, 1); + } + optimize(*env); + oldflatzinc(*env); + GCLock lock; + Options options; + if (timeLimit != 0) options.setIntParam("time", timeLimit); + delete _m; + _m = saveModel; + _e = new Env(_m); + PyMznSolver* ret = reinterpret_cast(PyMznSolver_new(&PyMznSolver_Type, NULL, NULL)); + switch (sc) { + case SC_UNKNOWN: + delete env; + PyErr_SetString(PyExc_ValueError, "MiniZinc: Model.solve: Solver name is not set"); + return NULL; + + case SC_GECODE: + ret->solver = new GecodeSolverInstance(*env, options); + break; + } + ret->solver->processFlatZinc(); + ret->env = env; + return reinterpret_cast(ret); +} + +/* + * Description: Creates a minizinc constraint + * Note: Need an outer GCLock for this to work + */ +static PyObject* MznModel_Constraint(MznModel* self, PyObject* args) { + PyObject* obj; + if (!PyArg_ParseTuple(args, "O", &obj)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.Constraint: Requires an object of Minizinc Variable"); + return NULL; + } + + GCLock Lock; + ConstraintI* i; + if (PyObject_ExactTypeCheck(obj, &MznExpression_Type)) { + i = new ConstraintI(Location(), (reinterpret_cast(obj)->e)); + } else if (PyBool_Check(obj)) { + bool val = PyObject_IsTrue(obj); + i = new ConstraintI(Location(), new BoolLit(Location(), val)); + } else { + PyErr_SetString( + PyExc_TypeError, + "MiniZinc: Model.Constraint: Object must be a MiniZinc Variable or Python Boolean value"); + return NULL; + } + self->_m->addItem(i); + Py_RETURN_NONE; +} + +/* + * Description: Defines the type of solution of the model + */ +// The responsibility to check if the parsed argument is annotation or not is of the Python +// interface, not the C++ interface +static PyObject* MznModel_SolveItem(MznModel* self, PyObject* args) { + unsigned int solveType; + PyObject* PyExp = NULL; + PyObject* PyAnn = NULL; + Expression* e = NULL; + Expression* ann = NULL; + + if (!PyArg_ParseTuple(args, "I|OO", &solveType, &PyAnn, &PyExp)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.SolveItem: Requires a solver code, an annotation (can be " + "NULL) and an optional expression (for optimisation)"); + return NULL; + } + + if (solveType > 2) { + PyErr_SetString( + PyExc_ValueError, + "MiniZinc: Model.SolveItem: Invalid solver code (Must be a positive less than 3 integer)"); + return NULL; + } + if (solveType) { + if (PyExp == NULL) { + PyErr_SetString( + PyExc_TypeError, + "MiniZinc: Model.SolveItem: Optimisation solver requires an addition constraint object"); + return NULL; + } else if (PyObject_ExactTypeCheck(PyExp, &MznExpression_Type)) { + e = reinterpret_cast(PyExp)->e; + } else { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.SolveItem: Expression must be a Minizinc Variable Object"); + return NULL; + } + } + + GCLock Lock; + SolveI* i; + switch (solveType) { + case 0: + i = SolveI::sat(Location()); + break; + case 1: + i = SolveI::min(Location(), (e)); + break; + case 2: + i = SolveI::max(Location(), (e)); + break; + } + if (PyObject_IsTrue(PyAnn)) { + if (PyObject_TypeCheck(PyAnn, &MznExpression_Type)) { + ann = reinterpret_cast(PyAnn)->e; + i->ann().add(ann); + } else if (PyList_Check(PyAnn)) { + long n = PyList_GET_SIZE(PyAnn); + for (long idx = 0; idx != n; ++idx) { + PyObject* PyItem = PyList_GET_ITEM(PyAnn, idx); + if (!PyObject_TypeCheck(PyItem, &MznExpression_Type)) { + // XXX: CONSIDER REVIEW - should I delete i or it will be automatically deleted + delete i; + MZN_PYERR_SET_STRING( + PyExc_TypeError, + "MiniZinc: Model.SolveItem: Item at position %ld must be a MiniZinc Variable", idx); + return NULL; + } + ann = reinterpret_cast(PyItem)->e; + i->ann().add(ann); + } + } else if (PyTuple_Check(PyAnn)) { + long n = PyTuple_GET_SIZE(PyAnn); + for (long idx = 0; idx != n; ++idx) { + PyObject* PyItem = PyTuple_GET_ITEM(PyAnn, idx); + if (!PyObject_TypeCheck(PyItem, &MznExpression_Type)) { + // XXX: CONSIDER REVIEW + delete i; + MZN_PYERR_SET_STRING( + PyExc_TypeError, + "MiniZinc: Model.SolveItem: Item at position %ld must be a MiniZinc Variable", idx); + return NULL; + } + ann = reinterpret_cast(PyItem)->e; + i->ann().add(ann); + } + } else { + // XXX: CONSIDER REVIEW + delete i; + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.SolveItem: Annotation must be a single value of or a " + "list/tuple of MiniZinc Variable Object"); + return NULL; + } + } + self->_m->addItem(i); + Py_RETURN_NONE; +} + +static PyObject* MznModel_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + MznModel* self = reinterpret_cast(type->tp_alloc(type, 0)); + if (self == NULL) { + PyErr_SetString(PyExc_RuntimeError, "MiniZinc: Unable to create new model"); + return NULL; + } + self->includePaths = NULL; + self->_m = NULL; + self->_e = NULL; + return reinterpret_cast(self); +} + +static int MznModel_init(MznModel* self, PyObject* args = NULL) { + self->loaded = false; + string std_lib_dir; + if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) { + std_lib_dir = string(MZNSTDLIBDIR); + } else { + PyErr_SetString(PyExc_EnvironmentError, + "MiniZinc: Model.init: No MiniZinc library directory MZN_STDLIB_DIR defined."); + return -1; + } + stringstream libNames; + libNames << "include \"globals.mzn\";"; + if (args != NULL) { + PyObject* PyLibNames = NULL; + if (PyUnicode_Check(args)) { + libNames << "\ninclude \"" << PyUnicode_AsUTF8(args) << "\";"; + } else if (PyTuple_Check(args)) { + Py_ssize_t n = PyTuple_GET_SIZE(args); + if (n > 1) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Model.init: Accept at most 1 argument"); + return -1; + } else if (n == 1) { + PyLibNames = PyTuple_GET_ITEM(args, 0); + if (PyObject_IsTrue(PyLibNames)) { + if (PyUnicode_Check(PyLibNames)) { + libNames << "\ninclude \"" << PyUnicode_AsUTF8(PyLibNames) << "\";"; + } else if (PyList_Check(PyLibNames)) { + Py_ssize_t n = PyList_GET_SIZE(PyLibNames); + for (Py_ssize_t i = 0; i != n; ++i) { + PyObject* temp = PyList_GET_ITEM(PyLibNames, i); + if (!PyUnicode_Check(temp)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.init: Items in parsing list must be strings"); + return -1; + } + libNames << "\ninclude \"" << PyUnicode_AsUTF8(temp) << "\";"; + } + } else if (PyTuple_Check(PyLibNames)) { + Py_ssize_t n = PyTuple_GET_SIZE(PyLibNames); + for (Py_ssize_t i = 0; i != n; ++i) { + PyObject* temp = PyTuple_GET_ITEM(PyLibNames, i); + if (!PyUnicode_Check(temp)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.init: Items in parsing tuples must be strings"); + return -1; + } + libNames << "\ninclude \"" << PyUnicode_AsUTF8(temp) << "\";"; + } + } else { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.init: Parsing argument must be a string or " + "list/tuple of strings"); + return -1; + } + } + } + } + } + const std::string& libNamesStr = libNames.str(); + self->timeLimit = 0; + self->includePaths = new vector; + self->includePaths->push_back(std_lib_dir + "/gecode/"); + self->includePaths->push_back(std_lib_dir + "/std/"); + self->sc = MznModel::default_solver; + stringstream errorStream; + self->_m = parseFromString(libNamesStr, "error.txt", *(self->includePaths), false, false, false, + errorStream); + self->_e = new Env(self->_m); + if (!(self->_m)) { + const std::string& tmp = errorStream.str(); + const char* cstr = tmp.c_str(); + PyErr_SetString(PyExc_EnvironmentError, cstr); + return -1; + } + return 0; +} + +static void MznModel_dealloc(MznModel* self) { + if (self->_m) delete self->_m; + if (self->_m) delete self->includePaths; + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* MznModel_addData(MznModel* self, PyObject* args) { + PyObject* obj; + const char* name; + if (!PyArg_ParseTuple(args, "sO", &name, &obj)) { + PyErr_SetString(PyExc_RuntimeError, "MiniZinc: Model.addData: Parsing error"); + return NULL; + } + if (self->addData(name, obj) == -1) { + // addData set error string already + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject* MznModel_copy(MznModel* self) { + MznModel* ret = reinterpret_cast(MznModel_new(&MznModel_Type, NULL, NULL)); + GCLock lock; + ret->_m = copy(self->_e->envi(), self->_m); + ret->includePaths = new vector(*(self->includePaths)); + + ret->timeLimit = self->timeLimit; + ret->loaded = self->loaded; + return reinterpret_cast(ret); +} + +static PyObject* MznModel_debugprint(MznModel* self) { + debugprint(self->_m); + Py_RETURN_NONE; +} + +static PyObject* MznModel_load(MznModel* self, PyObject* args, PyObject* keywds) { + if (self->load(args, keywds, true) < 0) return NULL; + Py_RETURN_NONE; +} + +static PyObject* MznModel_load_from_string(MznModel* self, PyObject* args, PyObject* keywds) { + if (self->load(args, keywds, false) < 0) return NULL; + Py_RETURN_NONE; +} + +static PyObject* MznModel_solve(MznModel* self, PyObject* args, PyObject* kwds) { + return self->solve(args, kwds); +} + +static PyObject* MznModel_set_time_limit(MznModel* self, PyObject* args) { + unsigned long t; + if (!PyArg_ParseTuple(args, "k", &t)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Model.set_time_limit: Time limit must be an integer"); + return NULL; + } + self->timeLimit = t; + return Py_None; +} + +PyObject* MznModel::set_solver(const char* s) { + std::string name(s); + // lower characters in name + for (std::string::iterator i = name.begin(); i != name.end(); ++i) + if (*i <= 'Z' && *i >= 'A') *i = *i - ('Z' - 'z'); + if (name == "gecode") + sc = SC_GECODE; + else { + MZN_PYERR_SET_STRING(PyExc_ValueError, "MiniZinc: Model.set_solver: Unexpected solver name: %s", + name.c_str()); + return NULL; + } + return Py_None; +} + +static PyObject* MznModel_set_solver(MznModel* self, PyObject* args) { + const char* s; + if (!PyArg_ParseTuple(args, "s", &s)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Model.set_solver: Solver name must be a string"); + return NULL; + } + return (self->set_solver(s)); +} + +static PyObject* MznModel_Declaration(MznModel* self, PyObject* args) { + GCLock Lock; + enum TypeId { + PARINT, // 0 + PARBOOL, // 1 + PARFLOAT, // 2 + PARSTRING, // 3 + ANN, // 4 + PARSETINT, // 5 + PARSETBOOL, // 6 + PARSETFLOAT, // 7 + PARSETSTRING, // 8 + VARINT, // 9 + VARBOOL, // 10 + VARFLOAT, // 11 + VARSETINT, // 12 + VARBOT, // 13 + BOT, // 14 + TOP, // 15 + VARTOP, // 16 + OPTVARTOP // 17 + }; + + char* name; + long tid; + PyObject* pydim = NULL; + PyObject* pylb = NULL; + PyObject* pyub = NULL; + PyObject* pyval = NULL; + Type type; + Expression* domain = NULL; + Expression* initValue = NULL; + Py_ssize_t dim; + + vector ranges; + Type::BaseType code = Type::BT_UNKNOWN; + + if (!PyArg_ParseTuple(args, "sO|OOO", &name, &pyval, &pydim, &pylb, &pyub)) { + MZN_PYERR_SET_STRING(PyExc_TypeError, + "MiniZinc: MznModel.Declaration: Variable parsing error"); + return NULL; + } + // if only 2 arguments, second value is the initial value + if (pydim == NULL) { + vector > dimList; + initValue = python_to_minizinc(pyval, type, dimList); + dim = dimList.size(); + domain = NULL; + } else + // else if > 2 arguments, create a MiniZinc Variable + { +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(pyval)) { + tid = PyInt_AS_LONG(pyval); + pyval = NULL; + } else +#endif + if (PyLong_Check(pyval)) { + tid = PyLong_AsLong(pyval); + pyval = NULL; + } else { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: MznModel.Declaration: Type Id must be an integer"); + return NULL; + } + if (tid > 17 || tid < 0) { + PyErr_SetString(PyExc_ValueError, "MiniZinc: MznModel.Declaration: Type Id is from 0 to 17"); + return NULL; + } + + int errorOccurred; + ranges = pydim_to_minizinc_ranges(pydim, errorOccurred); + if (errorOccurred) return NULL; + dim = ranges.size(); + + // Process different types + switch (static_cast(tid)) { + case VARINT: + type = Type::varint(dim); + code = Type::BT_INT; + if (pyub == NULL) { + Type tempType; + vector > tempDimList; + domain = python_to_minizinc(pylb, tempType, tempDimList); + if (tempType.st() != Type::ST_SET) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: MznModel.Declaration: If 5th argument does not exist, 4th " + "argument must be a Minizinc Set"); + return NULL; + } + } else + domain = new BinOp(Location(), one_dim_python_to_minizinc(pylb, code), BOT_DOTDOT, + one_dim_python_to_minizinc(pyub, code)); + break; + case VARBOOL: + type = Type::varbool(dim); + break; + case VARFLOAT: + type = Type::varfloat(dim); + code = Type::BT_FLOAT; + domain = new BinOp(Location(), one_dim_python_to_minizinc(pylb, code), BOT_DOTDOT, + one_dim_python_to_minizinc(pyub, code)); + break; + case VARSETINT: + type = Type::varsetint(dim); + if (pyub == NULL) { + Type tempType; + vector > tempDimList; + domain = python_to_minizinc(pylb, tempType, tempDimList); + if (tempType.st() != Type::ST_SET) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: MznModel.Declaration: If 5th argument does not exist, 4th " + "argument must be a Minizinc Set"); + return NULL; + } + } else + domain = new BinOp(Location(), one_dim_python_to_minizinc(pylb, code), BOT_DOTDOT, + one_dim_python_to_minizinc(pyub, code)); + break; + default: + MZN_PYERR_SET_STRING(PyExc_ValueError, + "MiniZinc: MznModel.Declaration: Value code %li not supported", tid); + return NULL; + } + } + + VarDecl* vd = new VarDecl(Location(), new TypeInst(Location(), type, ranges, domain), + string(name), initValue); + self->_m->addItem(new VarDeclI(Location(), vd)); + self->loaded = true; + + PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL); + reinterpret_cast(ret)->e = vd->id(); + return ret; +} diff --git a/interfaces/python/Model.h b/interfaces/python/Model.h new file mode 100644 index 0000000..e263806 --- /dev/null +++ b/interfaces/python/Model.h @@ -0,0 +1,194 @@ +/* + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ +#ifndef __MODEL_H +#define __MODEL_H + +#include "global.h" + +struct MznModel { + PyObject_HEAD Env* _e; + Model* _m; + vector* includePaths; + + // SolverCode definition is in global.h + enum SolverCode { SC_UNKNOWN, SC_GECODE } sc; + + const static SolverCode default_solver = SC_UNKNOWN; + + unsigned long timeLimit; + bool loaded; + + MznModel(); + + int load(PyObject* args, PyObject* keywds, bool fromFile); + int addData(const char* const name, PyObject* value); + + PyObject* set_solver(const char* s); + PyObject* solve(PyObject* args, PyObject* kwds); +}; + +void MznModelDestructor(PyObject* o); +static PyObject* MznModel_new(PyTypeObject* type, PyObject* args, PyObject* kwds); +static int MznModel_init(MznModel* self, PyObject* args); +static void MznModel_dealloc(MznModel* self); + +/************************************************************ + Groups of function general functions + ************************************************************/ +/** Function arguments - allow keywords + dict: dictionary of name - value, used when loading from a minizinc file only + key: name of the value to be assigned + value: value to be assigned + solver: solver name + if solver_name == '', do nothing + else, solve the model using specified solver + time: time limit + if time_limit != 0, set new time limit + */ +static PyObject* MznModel_solve(MznModel* self, PyObject* args, PyObject* kwds); + +static PyObject* MznModel_set_time_limit(MznModel* self, PyObject* args); +static PyObject* MznModel_set_solver(MznModel* self, PyObject* args); + +/************************************************************ + Groups of function functions to interact with MiniZinc files + ************************************************************/ +static PyObject* MznModel_load(MznModel* self, PyObject* args, PyObject* keywds); +static PyObject* MznModel_load_from_string(MznModel* self, PyObject* args, PyObject* keywds); +// Take in a name and a Python value, add that data to the current model +static PyObject* MznModel_addData(MznModel* self, PyObject* args); + +/*************************************************************** + Groups of function functions to interact with Python Interface + ***************************************************************/ +/* Function: MznModel_Declaration + * Description: Take in a tuple of arguments, create a Variable in the Minizinc Model self + * Arguments: + * 1: name, TypeId, dimension vector, lower bound, upper bound + * name: string + * TypeId: see enum TypeId below + * dimension vector: empty if Variable is not an array + * holds value if it is an array + * Syntax: vector >, called dimList + * - dimList[i] is the lower bound and upper bound of dimension i + * - for example: + * dimList[0] = <1,5> + * dimList[1] = <2,4> + * dimList[2] = <0,5> + * means a 3d array [1..5,2..4,0..5] + * lower bound: can be MiniZinc Set, int or float + * upper bound: None if lower bound is MiniZinc Set, + * type(lower bound) otherwise + * + * 2: name, python value + * python value: Create a variable based on the existing python value + * Return: + * Id of the declared variable + * Note 1: + * Don't reuse this declaration if the model with this declaration has been destroyed + * Note 2: + * Python Interface holds the responsibility of checking which type of value is declared. + */ +static PyObject* MznModel_Declaration(MznModel* self, PyObject* args); + +/* Function: MznModel_Constraint + * Description: Take in a Python boolean or MiniZinc Expression, add it to the MiniZinc Model self + * Return: + * None + * Note: It is the Python Interface responsibility to check if parsed argument is of type bool or + * not + */ +static PyObject* MznModel_Constraint(MznModel* self, PyObject* args); + +/* Function: MznModel_SolveItem + * Description: Add a solve item into the model + * Arguments: + * Arg1: Solve Type: 0 ~ sat, 1 ~ min, 2 ~ max + * Arg2 (opt): Annotation + * Arg3 (opt): Expression to be minimize / maximize if Arg1 != 0 + * Return: + * None + * Note: + * Arg2 accepts all MiniZinc expression, but the Python Interface should ensures that + * Arg2 is an expression of MiniZinc Annotation. + */ +static PyObject* MznModel_SolveItem(MznModel* self, PyObject* args); + +// Returns an exact copy of the current model +static PyObject* MznModel_copy(MznModel* self); +// Print the MiniZinc representation of the model +static PyObject* MznModel_debugprint(MznModel* self); + +// Declare all the functions +static PyMethodDef MznModel_methods[] = { + {"load", (PyCFunction)MznModel_load, METH_KEYWORDS, "Load MiniZinc model from MiniZinc file"}, + {"load_from_string", (PyCFunction)MznModel_load_from_string, METH_KEYWORDS, + "Load MiniZinc model from standard input"}, + {"addData", (PyCFunction)MznModel_addData, METH_VARARGS, "Add data to a MiniZinc model"}, + {"solve", (PyCFunction)MznModel_solve, METH_VARARGS | METH_KEYWORDS, + "Solve a loaded MiniZinc model"}, + {"set_time_limit", (PyCFunction)MznModel_set_time_limit, METH_VARARGS, + "Limit the execution time of the model"}, + {"set_solver", (PyCFunction)MznModel_set_solver, METH_VARARGS, + "Choose which model will be used to solve the model"}, + {"Declaration", (PyCFunction)MznModel_Declaration, METH_VARARGS, + "Add a variable into the model"}, + {"Constraint", (PyCFunction)MznModel_Constraint, METH_VARARGS, + "Add a constraint into the model"}, + {"SolveItem", (PyCFunction)MznModel_SolveItem, METH_VARARGS, "Add a solve item into the model"}, + + {"copy", (PyCFunction)MznModel_copy, METH_NOARGS, "Returns a copy of current model"}, + {"debugprint", (PyCFunction)MznModel_debugprint, METH_NOARGS, + "Print the MiniZinc representation of the current model"}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef MznModel_members[] = { + {NULL} /* Sentinel */ +}; + +static PyTypeObject MznModel_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc_internal.Model", /* tp_name */ + sizeof(MznModel), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznModel_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Minizinc Model", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + MznModel_methods, /* tp_methods */ + MznModel_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)MznModel_init, /* tp_init */ + 0, /* tp_alloc */ + MznModel_new, /* tp_new */ +}; + +#endif diff --git a/interfaces/python/Object.h b/interfaces/python/Object.h new file mode 100644 index 0000000..2a778e5 --- /dev/null +++ b/interfaces/python/Object.h @@ -0,0 +1,86 @@ +#ifndef __Mzn_OBJECT_H +#define __Mzn_OBJECT_H + +enum Mzn_Object_Code { MOC_BASE, MOC_EXPR, MOC_ANN, MOC_VARSET, MOC_SET }; + +struct MznObject { + PyObject_HEAD Mzn_Object_Code tid; +}; + +static PyObject* MznObject_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + PyObject* self = type->tp_alloc(type, 0); + reinterpret_cast(self)->tid = MOC_BASE; + return self; +} + +static void MznObject_dealloc(MznObject* self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyTypeObject MznObject_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc.Object", /* tp_name */ + sizeof(MznObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznObject_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Minizinc Object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + MznObject_new, /* tp_new */ + 0, /* tp_free */ +}; + +#include "Annotation.h" +#include "Expression.h" +#include "Set.h" +#include "VarSet.h" + +// Note: I have tried adding a function Expression* e() to MznObject, +// but it didn't work well. The derived objects, after being initialized by PyObject_INIT +// returns unusable derived functions. +// While this maybe a little bit annoying, it is the best solution that I can come up with. +// +// Returns: MiniZinc expression Expression* +static Expression* MznObject_get_e(MznObject* self) { + switch (self->tid) { + case MOC_BASE: + return NULL; + case MOC_SET: + return reinterpret_cast(self)->e(); + case MOC_EXPR: + case MOC_ANN: + return reinterpret_cast(self)->e; + default: + throw logic_error("Unhandled type"); + } +} + +#endif \ No newline at end of file diff --git a/interfaces/python/Set.cpp b/interfaces/python/Set.cpp new file mode 100644 index 0000000..6f285e0 --- /dev/null +++ b/interfaces/python/Set.cpp @@ -0,0 +1,327 @@ +/* Python Interface for MiniZinc constraint modelling + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ + +#include "Set.h" + +static void MznSet_dealloc(MznSet* self) { + delete self->ranges; + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* MznSet_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + MznSet* self = reinterpret_cast(type->tp_alloc(type, 0)); + self->ranges = new list; + self->tid = MOC_SET; + return reinterpret_cast(self); +} + +static PyObject* MznSet_iter(PyObject* self) { + MznSetIter* iter = reinterpret_cast(MznSetIter_new(&MznSetIter_Type, NULL, NULL)); + iter->listBegin = reinterpret_cast(self)->ranges->begin(); + iter->listEnd = reinterpret_cast(self)->ranges->end(); + iter->listIndex = iter->listBegin; + iter->currentValue = iter->listBegin->min; + return reinterpret_cast(iter); +} + +bool MznSet::contains(long long val) { + list::iterator it = ranges->begin(); + list::iterator end = ranges->end(); + assert(it != end); + for (; it++ != end;) + if (val < it->min) + return false; + else if (val <= it->max) + return true; + return false; +} + +bool MznSet::continuous() { + if (ranges->empty()) + throw length_error("Ranges cannot be empty"); + else { + // returning ranges.size() == 1, with O(1) time complexity + list::iterator it = ranges->begin(); + return ++it == ranges->end(); + } +} + +void MznSet::push(long long min, long long max) { + if (ranges->empty()) { + MznRange toAdd(min, max); + ranges->push_front(toAdd); + return; + } + list::iterator it, last; + for (it = ranges->begin(); it != ranges->end(); ++it) { + if (min <= it->max + 1) { + if (it->min < min) min = it->min; + if (max <= it->max) { + ranges->insert(it, MznRange(min, max)); + return; + } + goto FOUNDMIN; + } + continue; + FOUNDMIN: + last = it; + while (++it != ranges->end()) { + if (max < it->min - 1) { + break; + } else { + ranges->erase(last); + last = it; + if (max <= it->max) { + break; + } else + ++it; + } + } + last->min = min; + last->max = max; + return; + } + ranges->push_back(MznRange(min, max)); +} + +void MznSet::push(long long v) { + list::iterator it, last; + last = ranges->begin(); + if (ranges->empty()) { + MznRange toAdd(v, v); + ranges->push_front(toAdd); + return; + } + for (it = last; it != ranges->end(); ++it) { + if (v < it->min) { + if (v == it->min - 1) { + it->min = v; + if (last->max == v) { + last->max = it->max; + ranges->erase(it); + return; + } + } else { + // + if (last->max < v) { + MznRange toAdd(v, v); + ranges->insert(it, toAdd); + } + return; + } + } else if (v > it->max) { + if (v == it->max + 1) { + it->max = v; + } + last = it; + continue; + } else { + return; + } + } + if (last->max < v) { + MznRange toAdd(v, v); + ranges->insert(it, toAdd); + } + return; +} + +long long MznSet::min() { + assert(ranges->begin() != ranges->end()); + return ranges->front().min; +} + +long long MznSet::max() { + assert(ranges->begin() != ranges->end()); + return ranges->back().max; +} + +static PyObject* MznSet_min(MznSet* self) { return c_to_py_number(self->min()); } + +static PyObject* MznSet_max(MznSet* self) { return c_to_py_number(self->max()); } + +static PyObject* MznSet_continuous(MznSet* self) { return PyBool_FromLong(self->continuous()); } + +static PyObject* MznSet_contains(MznSet* self, PyObject* args) { + PyObject* py_val; + if (!PyArg_ParseTuple(args, "O", &py_val)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Set.contains: Parsing error"); + return NULL; + } + long long c_val = py_to_c_number(py_val); + if (PyErr_Occurred()) { + PyObject *ptype, *pmessage, *ptraceback; + PyErr_Fetch(&ptype, &pmessage, &ptraceback); + const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage); + string error = "MiniZinc: Set.contains: " + string(pStrErrorMessage); + PyErr_SetString(ptype, error.c_str()); + return NULL; + } + + return PyBool_FromLong(self->contains(c_val)); +} + +static PyObject* MznSet_push(MznSet* self, PyObject* isv) { + /*PyObject* isv; + if (!PyArg_ParseTuple(args,"O",&isv)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Set.push: Parsing error"); + return NULL; + }*/ + // if (isv == NULL) + // Py_RETURN_NONE; + + Py_ssize_t size = PyTuple_Size(isv); + + for (Py_ssize_t i = 0; i != size; ++i) { + PyObject* elem = PyTuple_GetItem(isv, i); + + Mzn_PyContainer_Type elem_type = Mzn_PyContainer_Check(elem); + Py_ssize_t elem_size = Mzn_PyContainer_Size(elem, elem_type); + + switch (elem_size) { + // elem_size == -1 means it is neither list nor tuple + case -1: { + int overflow; + long long c_val = py_to_c_number(elem, &overflow); + if (PyErr_Occurred()) { + if (overflow) { + MZN_PYERR_SET_STRING(PyExc_OverflowError, + "MiniZinc: Set.push: Overflow at tuple element at pos %li", i); + return NULL; + } else { + MZN_PYERR_SET_STRING(PyExc_TypeError, + "MiniZinc: Set.push: Type mismatched at tuple element pos %li: " + "expected an integer or list of integers", + i); + return NULL; + } + } + self->push(c_val); + break; + } + case 1: { + PyObject* py_val = Mzn_PyContainer_GetItem(elem, 0, elem_type); + long long c_val = py_to_c_number(py_val); + if (PyErr_Occurred()) { + PyObject *ptype, *pmessage, *ptraceback; + PyErr_Fetch(&ptype, &pmessage, &ptraceback); + const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage); + string error = "MiniZinc: Set.push: tuple item %li: " + string(pStrErrorMessage); + MZN_PYERR_SET_STRING(ptype, error.c_str(), i); + return NULL; + } + self->push(c_val); + break; + } + case 2: { + PyObject* p_min = Mzn_PyContainer_GetItem(elem, 0, elem_type); + PyObject* p_max = Mzn_PyContainer_GetItem(elem, 1, elem_type); + long long c_min = py_to_c_number(p_min); + if (PyErr_Occurred()) { + PyObject *ptype, *pmessage, *ptraceback; + PyErr_Fetch(&ptype, &pmessage, &ptraceback); + const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage); + string error = + "MiniZinc: Set.push: tuple item %li, first argument: " + string(pStrErrorMessage); + MZN_PYERR_SET_STRING(ptype, error.c_str(), i); + return NULL; + } + + long long c_max = py_to_c_number(p_max); + if (PyErr_Occurred()) { + PyObject *ptype, *pmessage, *ptraceback; + PyErr_Fetch(&ptype, &pmessage, &ptraceback); + const char* pStrErrorMessage = PyUnicode_AsUTF8(pmessage); + string error = + "MiniZinc: Set.push: tuple item %li, second argument: " + string(pStrErrorMessage); + MZN_PYERR_SET_STRING(ptype, error.c_str(), i); + return NULL; + } + + if (c_min < c_max) + self->push(c_min, c_max); + else if (c_min > c_max) + self->push(c_max, c_min); + else + self->push(c_min); + break; + } + default: + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Set.push: The sublist size can only be 1 or 2"); + return NULL; + } + } + Py_RETURN_NONE; +} + +static int MznSet_init(MznSet* self, PyObject* args) { + if (MznSet_push(self, args) == NULL) return -1; + return 0; +} + +static PyObject* MznSet_clear(MznSet* self) { + self->clear(); + Py_RETURN_NONE; +} + +static PyObject* MznSet_output(MznSet* self) { + stringstream s; + for (list::iterator it = self->ranges->begin(); it != self->ranges->end(); ++it) { + s << it->min << ".." << it->max << " "; + } + const std::string& tmp = s.str(); + const char* cstr = tmp.c_str(); + PyObject* result = PyUnicode_FromString(cstr); + return result; +} + +static PyObject* MznSet_repr(PyObject* self) { + stringstream output; + list* r = (reinterpret_cast(self))->ranges; + if (r->begin() == r->end()) + output << "Empty Set"; + else + for (list::iterator it = r->begin();;) { + if (it->min == it->max) + output << it->min; + else + output << it->min << ".." << it->max; + if (++it == r->end()) + break; + else + output << ", "; + } + const std::string& tmp = output.str(); + return PyUnicode_FromString(tmp.c_str()); +} + +static void MznSetIter_dealloc(MznSetIter* self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* MznSetIter_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + return type->tp_alloc(type, 0); +} + +static PyObject* MznSetIter_iternext(PyObject* self) { + MznSetIter* iter = reinterpret_cast(self); + if (iter->listIndex == iter->listEnd) { + iter->listIndex = iter->listBegin; + iter->currentValue = iter->listBegin->min; + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } else { + PyObject* result = c_to_py_number(iter->currentValue); + iter->currentValue++; + if (iter->currentValue > iter->listIndex->max) { + iter->listIndex++; + if (iter->listIndex != iter->listEnd) iter->currentValue = iter->listIndex->min; + } + return result; + } +} \ No newline at end of file diff --git a/interfaces/python/Set.h b/interfaces/python/Set.h new file mode 100644 index 0000000..5316152 --- /dev/null +++ b/interfaces/python/Set.h @@ -0,0 +1,234 @@ +/* Python Interface for MiniZinc constraint modelling + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ + +#ifndef __MZNSET_H +#define __MZNSET_H + +#include "Object.h" + +#include +#include +#include + +#if PY_MAJOR_VERSION >= 3 +#define Py_TPFLAGS_HAVE_ITER 0 +#endif + +using namespace std; +using namespace MiniZinc; + +struct MznRange { + long long min; + long long max; + MznRange(long long min, long long max) : min(min), max(max) {} +}; + +/* + * My custom defined Set + * + * MznSet_init and MznSet_push take a variable list of arguments: + * (item1, item2, ..., itemn) + * For each item, it can be either a value or a list/tuple of min and max values: + * item? = 1,4 or 5 .... + * or item? = [1,10] or [5,6] + * or item? = (1,10) or (5,6) + * For example, a Set of 1..5, 7, 10..15 can be defined like this: + * minizinc_internal.Set( [1,5], 7, [10,15] ) + * + * The set will automatically merge ranges if it can. + * + * XXX: should a Set of {1,2,6,35} be written as 1..2, 6, 35 + * or 1, 2, 6, 35? + */ + +struct MznSet : MznObject { + list* ranges; + + void clear() { ranges->clear(); } + + void push(long long min, long long max); + void push(long long v); + + long long min(); + long long max(); + + long long size() { return ranges->size(); } + bool continuous(); + bool contains(long long val); + + // Requires an outer GC Lock + Expression* e() { + vector setRanges; + for (list::const_iterator it = ranges->begin(); it != ranges->end(); ++it) { + setRanges.push_back(IntSetVal::Range(IntVal(it->min), IntVal(it->max))); + } + Expression* rhs = new SetLit(Location(), IntSetVal::a(setRanges)); + return rhs; + } +}; + +enum Mzn_PyContainer_Type { MZN_T_LIST, MZN_T_TUPLE, MZN_T_OTHER }; + +inline Mzn_PyContainer_Type Mzn_PyContainer_Check(PyObject* o) { + if (PyList_Check(o)) + return MZN_T_LIST; + else if (PyTuple_Check(o)) + return MZN_T_TUPLE; + else + return MZN_T_OTHER; +} + +inline Py_ssize_t Mzn_PyContainer_Size(PyObject* o, Mzn_PyContainer_Type t) { + switch (t) { + case MZN_T_LIST: + return PyList_Size(o); + case MZN_T_TUPLE: + return PyTuple_Size(o); + default: + return -1; + } +} + +inline PyObject* Mzn_PyContainer_GetItem(PyObject* o, Py_ssize_t i, Mzn_PyContainer_Type t) { + switch (t) { + case MZN_T_LIST: + return PyList_GetItem(o, i); + case MZN_T_TUPLE: + return PyTuple_GetItem(o, i); + default: + return NULL; + } +} + +// Set representation on terminal command +// XXX: should be redefined as MznSet_str +static PyObject* MznSet_repr(PyObject* self); + +// Push accepts 1 argument, which is exactly like the Set initialization +static PyObject* MznSet_push(MznSet* self, PyObject* args); +static PyObject* MznSet_output(MznSet* self); +static PyObject* MznSet_min(MznSet* self); +static PyObject* MznSet_max(MznSet* self); +static PyObject* MznSet_continuous(MznSet* self); +static PyObject* MznSet_contains(MznSet* self, PyObject* args); +static PyObject* MznSet_clear(MznSet* self); + +static int MznSet_init(MznSet* self, PyObject* args); +static PyObject* MznSet_new(PyTypeObject* type, PyObject* args, PyObject* kwds); +static void MznSet_dealloc(MznSet* self); +static PyObject* MznSet_iter(PyObject* self); + +static PyMethodDef MznSet_methods[] = { + {"output", (PyCFunction)MznSet_output, METH_NOARGS, "Return all values in the set"}, + {"push", (PyCFunction)MznSet_push, METH_VARARGS, "Expand the set"}, + {"min", (PyCFunction)MznSet_min, METH_NOARGS, "Lower bound of the set"}, + {"max", (PyCFunction)MznSet_max, METH_NOARGS, "Upper bound of the set"}, + {"continuous", (PyCFunction)MznSet_continuous, METH_NOARGS, + "Check whether the set is continous"}, + {"contains", (PyCFunction)MznSet_contains, METH_VARARGS, + "Check whether a python value is in the Set"}, + {"clear", (PyCFunction)MznSet_clear, METH_NOARGS, "Clear the set"}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef MznSet_members[] = { + {NULL} /* Sentinel */ +}; + +static PyTypeObject MznSet_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc.Set", /* tp_name */ + sizeof(MznSet), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznSet_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + MznSet_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ + "Minizinc Set Object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + MznSet_iter, /* tp_iter */ + 0, /* tp_iternext */ + MznSet_methods, /* tp_methods */ + MznSet_members, /* tp_members */ + 0, /* tp_getset */ + &MznObject_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)MznSet_init, /* tp_init */ + 0, /* tp_alloc */ + MznSet_new, /* tp_new */ +}; + +struct MznSetIter { + // To support python iteration + PyObject_HEAD list::const_iterator listIndex; + list::const_iterator listBegin; + list::const_iterator listEnd; + long long currentValue; +}; + +static PyObject* MznSetIter_new(PyTypeObject* type, PyObject* args, PyObject* kwds); +static void MznSetIter_dealloc(MznSetIter* self); +static PyObject* MznSetIter_iternext(PyObject* self); + +static PyTypeObject MznSetIter_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc.SetIterator", /* tp_name */ + sizeof(MznSetIter), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznSetIter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ + "Minizinc Set Iterator", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)MznSetIter_iternext, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /*MznModel_getseters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +#endif \ No newline at end of file diff --git a/interfaces/python/Solver.cpp b/interfaces/python/Solver.cpp new file mode 100644 index 0000000..90ac4d2 --- /dev/null +++ b/interfaces/python/Solver.cpp @@ -0,0 +1,124 @@ +/* Python Interface for MiniZinc constraint modelling + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ + +#include "Solver.h" + +static PyObject* PyMznSolver_get_value_helper(PyMznSolver* self, const char* const name) { + for (unsigned int i = 0; i < self->_m->size(); ++i) { + if (VarDeclI* vdi = (*(self->_m))[i]->dyn_cast()) { + if (strcmp(vdi->e()->id()->str().c_str(), name) == 0) { + GCLock Lock; + if (PyObject* PyValue = minizinc_to_python(vdi->e())) + return PyValue; + else { + char buffer[50]; + sprintf(buffer, "Cannot retrieve the value of '%s'", name); + PyErr_SetString(PyExc_RuntimeError, buffer); + return NULL; + } + } + } + } + char buffer[50]; + sprintf(buffer, "'%s' not found", name); + PyErr_SetString(PyExc_RuntimeError, buffer); + return NULL; +} + +static PyObject* PyMznSolver_get_value(PyMznSolver* self, PyObject* args) { + const char* name; + PyObject* obj; + if (!(self->_m)) { + PyErr_SetString(PyExc_RuntimeError, "No model (maybe you need to call Model.next() first"); + return NULL; + } + if (!PyArg_ParseTuple(args, "O", &obj)) { + PyErr_SetString(PyExc_TypeError, "Accept 1 argument of strings or list/tuple of strings"); + return NULL; + } + if (PyUnicode_Check(obj)) { + name = PyUnicode_AsUTF8(obj); + return PyMznSolver_get_value_helper(self, name); + ; + } else + // XXX: INEFFICIENT function to retrieve values, consider optimize it later + // Python Dictionary would be good + if (PyList_Check(obj)) { + Py_ssize_t n = PyList_GET_SIZE(obj); + PyObject* ret = PyList_New(n); + for (Py_ssize_t i = 0; i != n; ++i) { + PyObject* item = PyList_GET_ITEM(obj, i); + if (!PyUnicode_Check(item)) { + Py_DECREF(ret); + PyErr_SetString(PyExc_RuntimeError, "Elements must be strings"); + return NULL; + } + name = PyUnicode_AsUTF8(item); + PyObject* value = PyMznSolver_get_value_helper(self, name); + if (value == NULL) { + Py_DECREF(ret); + return NULL; + } + PyList_SET_ITEM(ret, i, value); + } + return ret; + } else if (PyTuple_Check(obj)) { + Py_ssize_t n = PyTuple_GET_SIZE(obj); + PyObject* ret = PyTuple_New(n); + for (Py_ssize_t i = 0; i != n; ++i) { + PyObject* item = PyTuple_GET_ITEM(obj, i); + if (!PyUnicode_Check(item)) { + Py_DECREF(ret); + PyErr_SetString(PyExc_RuntimeError, "Elements must be strings"); + return NULL; + } + name = PyUnicode_AsUTF8(item); + PyObject* value = PyMznSolver_get_value_helper(self, name); + if (value == NULL) { + Py_DECREF(ret); + return NULL; + } + PyTuple_SET_ITEM(ret, i, value); + } + return ret; + } else { + PyErr_SetString(PyExc_TypeError, "Object must be a string or a list/tuple of strings"); + return NULL; + } +} + +PyObject* PyMznSolver::next() { + if (solver == NULL) throw runtime_error("Solver Object not found"); + GCLock lock; + SolverInstance::Status status = solver->solve(); + if (status == SolverInstance::SAT || status == SolverInstance::OPT) { + _m = env->output(); + Py_RETURN_NONE; + } + if (_m == NULL) + return PyUnicode_FromString("Unsatisfied"); + else + return PyUnicode_FromString("Reached last solution"); +} + +static void PyMznSolver_dealloc(PyMznSolver* self) { + if (self->env) delete self->env; + if (self->solver) delete self->solver; + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyObject* PyMznSolver_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + PyMznSolver* self = reinterpret_cast(type->tp_alloc(type, 0)); + self->solver = NULL; + self->_m = NULL; + self->env = NULL; + return reinterpret_cast(self); +} + +static int PyMznSolver_init(PyMznSolver* self, PyObject* args) { return 0; } + +static PyObject* PyMznSolver_next(PyMznSolver* self) { return self->next(); } diff --git a/interfaces/python/Solver.h b/interfaces/python/Solver.h new file mode 100644 index 0000000..de1dda4 --- /dev/null +++ b/interfaces/python/Solver.h @@ -0,0 +1,88 @@ +/* Python Interface for MiniZinc constraint modelling + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ + +#ifndef __SOLVER_H +#define __SOLVER_H + +#include "global.h" + +struct PyMznSolver { + PyObject_HEAD MiniZinc::SolverInstanceBase* solver; + MiniZinc::Env* env; + MiniZinc::Model* _m; + + PyObject* next(); +}; + +static PyObject* PyMznSolver_new(PyTypeObject* type, PyObject* args, PyObject* kwds); +static int PyMznSolver_init(PyMznSolver* self, PyObject* args); +static void PyMznSolver_dealloc(PyMznSolver* self); + +// Ask the solver to give the next available solution +// Return Py_None if no error +// Return a string with "Unsatisfied" or "Reached last solution" if error +static PyObject* PyMznSolver_next(PyMznSolver* self); + +static PyObject* PyMznSolver_get_value_helper(PyMznSolver* self, PyObject* args); +// get_value accepts 1 string or a list of strings, +// which are the name of values to be retrieved. +// returns a value or a tuple of value depending on which type argument was parsed. +static PyObject* PyMznSolver_get_value(PyMznSolver* self, PyObject* args); + +static PyMemberDef PyMznSolver_members[] = { + {NULL} /* Sentinel */ +}; + +// static PyMethodDef PyMznSolver_methods[3]; + +static PyMethodDef PyMznSolver_methods[] = { + {"next", (PyCFunction)PyMznSolver_next, METH_NOARGS, "Next Solution"}, + {"get_value", (PyCFunction)PyMznSolver_get_value, METH_VARARGS, "Get value of a variable"}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject PyMznSolver_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc_internal.solver", /* tp_name */ + sizeof(PyMznSolver), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)PyMznSolver_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Minizinc Solver", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PyMznSolver_methods, /* tp_methods */ + PyMznSolver_members, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)PyMznSolver_init, /* tp_init */ + 0, /* tp_alloc */ + PyMznSolver_new, /* tp_new */ +}; + +#endif diff --git a/interfaces/python/VarSet.h b/interfaces/python/VarSet.h new file mode 100644 index 0000000..5edbf3e --- /dev/null +++ b/interfaces/python/VarSet.h @@ -0,0 +1,60 @@ +#ifndef __Mzn_VARSETOBJECT_H +#define __Mzn_VARSETOBJECT_H + +#include "Expression.h" + +struct MznVarSet : MznExpression {}; + +static PyObject* MznVarSet_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + MznVarSet* self = reinterpret_cast(type->tp_alloc(type, 0)); + reinterpret_cast(self)->tid = MOC_VARSET; + self->e = NULL; + return reinterpret_cast(self); +} + +static void MznVarSet_dealloc(MznVarSet* self) { + Py_TYPE(self)->tp_free(reinterpret_cast(self)); +} + +static PyTypeObject MznVarSet_Type = { + PyVarObject_HEAD_INIT(NULL, 0) "minizinc.VarSet", /* tp_name */ + sizeof(MznVarSet), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)MznVarSet_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + "Minizinc VarSet Object (derived from MznExpression)", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /*MznVarSet_getseters, /* tp_getset */ + &MznExpression_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + MznVarSet_new, /* tp_new */ + 0, /* tp_free */ +}; + +#endif \ No newline at end of file diff --git a/interfaces/python/annotation/__init__.py b/interfaces/python/annotation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/interfaces/python/annotation/__init__.pyc b/interfaces/python/annotation/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de312e39493ed5bb83428c0d2d172c0523c717fc GIT binary patch literal 109 zcmZSn%*(Z-;aNyB0~9baFfceUFfbIeFfcHrFfasbfJFQ>K+TZlX-=vg$be#yegMb66O#Y{ literal 0 HcmV?d00001 diff --git a/interfaces/python/global.cpp b/interfaces/python/global.cpp new file mode 100644 index 0000000..7f0b469 --- /dev/null +++ b/interfaces/python/global.cpp @@ -0,0 +1,489 @@ +#include "global.h" + +using namespace MiniZinc; +using namespace std; + +inline PyObject* c_to_py_number(long long c_val) { +#if PY_MAJOR_VERSION < 3 + if (c_val > LONG_MIN || c_val < LONG_MAX) + return PyInt_FromLong(static_cast(c_val)); + else +#endif + return PyLong_FromLongLong(c_val); +} + +inline long long py_to_c_number(PyObject* py_val) { +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(py_val)) { + return PyInt_AS_LONG(py_val); + } else +#endif + if (PyLong_Check(py_val)) { + int overflow; + long long c_val = PyLong_AsLongLongAndOverflow(py_val, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, "Python value is overflown"); + return -1; + } + return c_val; + } else { + PyErr_SetString(PyExc_TypeError, "Python value must be an integer"); + return -1; + } +} + +inline long long py_to_c_number(PyObject* py_val, int* overflow) { +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(py_val)) { + *overflow = 0; + return PyInt_AS_LONG(py_val); + } else +#endif + if (PyLong_Check(py_val)) { + long long c_val = PyLong_AsLongLongAndOverflow(py_val, overflow); + if (*overflow) { + PyErr_SetString(PyExc_OverflowError, "Python value is overflown"); + return -1; + } + *overflow = 0; + return c_val; + } else { + *overflow = 0; + PyErr_SetString(PyExc_TypeError, "Python value must be an integer"); + return -1; + } +} + +bool compareType(const Type& type1, const Type& type2) { + return (type1.bt() == type2.bt() && type1.st() == type2.st() && type1.dim() == type2.dim()); +} + +string typePresentation(const Type& type) { + string baseTypeString; + switch (type.bt()) { + case Type::BT_BOOL: + baseTypeString = "bool"; + break; + case Type::BT_INT: + baseTypeString = "int"; + break; + case Type::BT_FLOAT: + baseTypeString = "float"; + break; + case Type::BT_STRING: + baseTypeString = "string"; + break; + default: + baseTypeString = "UNSET"; + } + if (type.st() == Type::ST_SET) + return "a set of " + baseTypeString; + else if (type.dim() == 0) { + if (type.bt() == Type::BT_INT) + return "an " + baseTypeString; + else + return "a " + baseTypeString; + } else { + string ret = "an array of ["; + for (int i = type.dim(); i--;) { + ret += baseTypeString; + if (i - 1 > 0) ret += ", "; + } + ret += ']'; + return ret; + } +} + +inline PyObject* one_dim_minizinc_to_python(Expression* e, const Type& type) { + Env env(NULL); + + if (type.st() == Type::ST_SET) { + IntSetVal* isv = eval_intset(env.envi(), e); + MznSet* newSet = reinterpret_cast(MznSet_new(&MznSet_Type, NULL, NULL)); + for (IntSetRanges isr(isv); isr(); ++isr) { + newSet->push(isr.min().toInt(), isr.max().toInt()); + } + return reinterpret_cast(newSet); + } else + switch (type.bt()) { + case Type::BT_BOOL: + return PyBool_FromLong(eval_bool(env.envi(), e)); + case Type::BT_INT: + return c_to_py_number(eval_int(env.envi(), e).toInt()); + case Type::BT_FLOAT: + return PyFloat_FromDouble(eval_float(env.envi(), e)); + case Type::BT_STRING: { + string temp(eval_string(env.envi(), e)); + return PyUnicode_FromString(temp.c_str()); + } + default: + throw logic_error("MiniZinc: one_dim_minizinc_to_python: Unexpected type code"); + } +} + +/* + * Convert minizinc expression to python value + */ +PyObject* minizinc_to_python(VarDecl* vd) { + GCLock Lock; + if (vd == NULL) { + PyErr_SetString(PyExc_ValueError, "MiniZinc_to_Python: Value is not set"); + return NULL; + } + Type type = vd->type(); + if (type.dim() == 0) { + return one_dim_minizinc_to_python(vd->e(), type); + } else { + Env env(NULL); + ArrayLit* al = eval_par(env.envi(), vd->e())->cast(); + int dim = vd->type().dim(); + + // Maximum size of each dimension + vector dmax; + // Current size of each dimension + vector d; + // p[0] holds the final array, p[1+] builds up p[0] + vector p(dim); + + for (int i = 0; i < dim; ++i) { + d.push_back(0); + Py_ssize_t dtemp = al->max(i) - al->min(i) + 1; + dmax.push_back(dtemp); + p[i] = PyList_New(dtemp); + } + + int i = dim - 1; + + // next item to be put onto the final array + unsigned int currentPos = 0; + do { + PyList_SetItem(p[i], d[i], one_dim_minizinc_to_python(al->v()[currentPos], type)); + currentPos++; + d[i]++; + while (d[i] >= dmax[i] && i > 0) { + PyList_SetItem(p[i - 1], d[i - 1], p[i]); + Py_DECREF(p[i]); + d[i] = 0; + p[i] = PyList_New(dmax[i]); + i--; + d[i]++; + } + i = dim - 1; + } while (d[0] < dmax[0]); + for (int i = 1; i < dim; i++) Py_DECREF(p[i]); + return p[0]; + } +} + +inline Expression* one_dim_python_to_minizinc(PyObject* pvalue, Type::BaseType& code) { + /*enum BaseType { BT_TOP, BT_BOOL, BT_INT, BT_FLOAT, BT_STRING, BT_ANN, + BT_BOT, BT_UNKNOWN };*/ + switch (code) { + case Type::BT_UNKNOWN: + if (PyObject_TypeCheck(pvalue, &MznObject_Type)) { + return MznObject_get_e(reinterpret_cast(pvalue)); + // return reinterpret_cast(pvalue)->e(); + } else if (PyBool_Check(pvalue)) { + BT_BOOLEAN_PROCESS: + Expression* rhs = new BoolLit(Location(), PyObject_IsTrue(pvalue)); + code = Type::BT_BOOL; + return rhs; + } else +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(pvalue)) { + BT_INTEGER_PROCESS_2X_VERSION: + Expression* rhs = IntLit::a(IntVal(PyInt_AS_LONG(pvalue))); + code = Type::BT_INT; + return rhs; + } else +#endif + if (PyLong_Check(pvalue)) { + BT_INTEGER_PROCESS: + int overflow; + Expression* rhs = IntLit::a(IntVal(PyLong_AsLongLongAndOverflow(pvalue, &overflow))); + if (overflow) { + if (overflow > 0) + PyErr_SetString(PyExc_OverflowError, + "MiniZinc: Python integer value is larger than 2^63-1"); + else + PyErr_SetString(PyExc_OverflowError, + "MiniZinc: Python integer value is smaller than -2^63"); + return NULL; + } + code = Type::BT_INT; + return rhs; + } else if (PyFloat_Check(pvalue)) { + BT_FLOAT_PROCESS: + Expression* rhs = new FloatLit(Location(), PyFloat_AS_DOUBLE(pvalue)); + code = Type::BT_FLOAT; + return rhs; + } else if (PyUnicode_Check(pvalue)) { + BT_STRING_PROCESS: + Expression* rhs = new StringLit(Location(), PyUnicode_AsUTF8(pvalue)); + code = Type::BT_STRING; + return rhs; + } else { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Unexpected python type"); + return NULL; + } + break; + + case Type::BT_INT: +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(pvalue)) + goto BT_INTEGER_PROCESS_2X_VERSION; + else +#endif + if (!PyLong_Check(pvalue)) { + PyErr_SetString( + PyExc_TypeError, + "MiniZinc: Object in an array must be of the same type: Expected an integer"); + return NULL; + } + goto BT_INTEGER_PROCESS; + + case Type::BT_FLOAT: + if (!PyFloat_Check(pvalue)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Object in an array must be of the same type: Expected an float"); + return NULL; + } + goto BT_FLOAT_PROCESS; + + case Type::BT_STRING: + if (!PyUnicode_Check(pvalue)) { + PyErr_SetString( + PyExc_TypeError, + "MiniZinc: Object in an array must be of the same type: Expected an string"); + return NULL; + } + goto BT_STRING_PROCESS; + + case Type::BT_BOOL: + if (!PyBool_Check(pvalue)) { + PyErr_SetString( + PyExc_TypeError, + "MiniZinc: Object in an array must be of the same type: Expected an boolean"); + return NULL; + } + goto BT_BOOLEAN_PROCESS; + + default: + throw std::invalid_argument("MiniZinc: Internal Error: Received unexpected base type code"); + } +} + +Expression* python_to_minizinc(PyObject* pvalue, const ASTExprVec& ranges) { + if (PyObject_TypeCheck(pvalue, &MznObject_Type)) { + return MznObject_get_e(reinterpret_cast(pvalue)); + } else if (PyList_Check(pvalue)) { + vector dimensions; + vector simpleArray; + if (getList(pvalue, dimensions, simpleArray, 0) == -1) + // getList should already set the error string + return NULL; + if (ranges.size() != dimensions.size()) { + PyErr_SetString( + PyExc_ValueError, + "MiniZinc: python_to_minizinc: size of declared array and actual array not matched"); + return NULL; + } + vector callArgument(dimensions.size() + 1); + vector onedArray(simpleArray.size()); + + stringstream buffer; + buffer << "array" << dimensions.size() << "d"; + string callName(buffer.str()); + for (int i = 0; i != dimensions.size(); ++i) { + Expression* domain = ranges[i]->domain(); + if (domain == NULL) { + Expression* e0 = IntLit::a(IntVal(1)); + Expression* e1 = IntLit::a(IntVal(dimensions[i])); + callArgument[i] = new BinOp(Location(), e0, BOT_DOTDOT, e1); + } else { + callArgument[i] = domain; + } + } + Type::BaseType code = Type::BT_UNKNOWN; + for (int i = 0; i != simpleArray.size(); ++i) { + PyObject* temp = simpleArray[i]; + Expression* rhs = one_dim_python_to_minizinc(temp, code); + if (rhs == NULL) return NULL; + onedArray[i] = rhs; + } + callArgument[dimensions.size()] = new ArrayLit(Location(), onedArray); + Expression* rhs = new Call(Location(), callName, callArgument); + return rhs; + } else { + Type::BaseType code = Type::BT_UNKNOWN; + Expression* rhs = one_dim_python_to_minizinc(pvalue, code); + return rhs; + } +} + +Expression* python_to_minizinc(PyObject* pvalue, Type& returnType, + vector >& dimList) { + Type::BaseType code = Type::BT_UNKNOWN; + if (PyObject_TypeCheck(pvalue, &MznObject_Type)) { + returnType = Type::parsetint(); + return MznObject_get_e(reinterpret_cast(pvalue)); + } else if (PyList_Check(pvalue)) { + vector dimensions; + vector simpleArray; + if (getList(pvalue, dimensions, simpleArray, 0) == -1) { + // getList should set error string already + return NULL; + } + if (dimList.empty()) + for (int i = 0; i != dimensions.size(); i++) + dimList.push_back(pair(0, dimensions[i] - 1)); + else { + if (dimList.size() != dimensions.size()) { + PyErr_SetString( + PyExc_ValueError, + "MiniZinc: python_to_minizinc: size of declared and actual array not matched"); + return NULL; + } + for (int i = 0; i != dimensions.size(); i++) { + if ((dimList[i].second - (dimList[i].first) + 1) != dimensions[i]) { + PyErr_SetString( + PyExc_ValueError, + "MiniZinc: python_to_minizinc: size of each dimension of python array not matched"); + return NULL; + } + } + } + vector v; + for (int i = 0; i != simpleArray.size(); ++i) { + PyObject* temp = simpleArray[i]; + Expression* rhs = one_dim_python_to_minizinc(temp, code); + if (rhs == NULL) return NULL; + v.push_back(rhs); + } + returnType = Type(); + returnType.bt(code); + returnType.dim(dimList.size()); + Expression* rhs = new ArrayLit(Location(), v, dimList); + return rhs; + } else { + Expression* rhs = one_dim_python_to_minizinc(pvalue, code); + returnType = Type(); + returnType.bt(code); + return rhs; + } +} + +vector pydim_to_minizinc_ranges(PyObject* pydim, int& errorOccurred) { + if (!PyList_Check(pydim)) { + errorOccurred = 1; + PyErr_SetString(PyExc_TypeError, "MiniZinc: python_to_dimList: argument must be a python list"); + return vector(); + } + Py_ssize_t dim = PyList_GET_SIZE(pydim); + vector ranges(dim); + for (Py_ssize_t i = 0; i != dim; ++i) { + PyObject* temp = PyList_GET_ITEM(pydim, i); + if (!PyList_Check(temp)) { + for (int j = 0; j != i; ++j) delete ranges[j]; + ranges.clear(); + errorOccurred = 1; + PyErr_SetString(PyExc_TypeError, + "MiniZinc: python_to_dimList: objects in the list must be range lists"); + return ranges; + } + PyObject* Py_bound[2]; + Py_bound[0] = PyList_GetItem(temp, 0); + Py_bound[1] = PyList_GetItem(temp, 1); + if (PyErr_Occurred()) { + for (int j = 0; j != i; ++j) delete ranges[j]; + ranges.clear(); + errorOccurred = 1; + PyErr_SetString(PyExc_TypeError, + "MiniZinc: python_to_dimList: a range must consist of 2 values"); + return ranges; + } + + long long c_bound[2]; + for (int k = 0; k != 2; ++k) { +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(Py_bound[k])) + c_bound[k] = PyInt_AS_LONG(Py_bound[k]); + else +#endif + if (PyLong_Check(Py_bound[k])) { + int overflow; + c_bound[k] = PyLong_AsLongLongAndOverflow(Py_bound[k], &overflow); + if (overflow) { + switch (k) { + case 0: + MZN_PYERR_SET_STRING( + PyExc_OverflowError, + "MiniZinc: python_to_dimList: Range at pos %i: First argument is overflown", i); + break; + case 1: + MZN_PYERR_SET_STRING( + PyExc_OverflowError, + "MiniZinc: python_to_dimList: Range at pos %i: Second argument is overflown", i); + break; + default: + throw logic_error("pydim_to_dimList: Unexpected iterator value"); + } + for (int j = 0; j != i; ++j) delete ranges[j]; + ranges.clear(); + errorOccurred = 1; + return ranges; + } + } else { + switch (k) { + case 0: + MZN_PYERR_SET_STRING(PyExc_OverflowError, + "MiniZinc: python_to_dimList: Range at pos %i: First argument " + "type is mismatched: expected a number", + i); + break; + case 1: + MZN_PYERR_SET_STRING(PyExc_OverflowError, + "MiniZinc: python_to_dimList: Range at pos %i: Second argument " + "type is mismatched: expected a number", + i); + break; + default: + throw logic_error("pydim_to_dimList: Unexpected iterator value"); + } + for (int j = 0; j != i; ++j) delete ranges[j]; + ranges.clear(); + errorOccurred = 1; + return ranges; + } + } + if (c_bound[0] > c_bound[1]) swap(c_bound[0], c_bound[1]); + Expression* e0 = IntLit::a(IntVal(c_bound[0])); + Expression* e1 = IntLit::a(IntVal(c_bound[1])); + Expression* domain = new BinOp(Location(), e0, BOT_DOTDOT, e1); + ranges[i] = new TypeInst(Location(), Type(), domain); + } + errorOccurred = 0; + return ranges; +} + +int getList(PyObject* value, vector& dimensions, vector& simpleArray, + const int layer) { + for (Py_ssize_t i = 0; i < PyList_Size(value); i++) { + if (dimensions.size() <= layer) { + dimensions.push_back(PyList_Size(value)); + } else if (dimensions[layer] != PyList_Size(value)) { + PyErr_SetString(PyExc_ValueError, + "MiniZinc: Inconsistency in size of multidimensional array"); + return -1; + } + PyObject* li = PyList_GetItem(value, i); + if (PyList_Check(li)) { + if (getList(li, dimensions, simpleArray, layer + 1) == -1) { + return -1; + } + } else { + simpleArray.push_back(li); + } + } + return 0; +} diff --git a/interfaces/python/global.h b/interfaces/python/global.h new file mode 100644 index 0000000..142201c --- /dev/null +++ b/interfaces/python/global.h @@ -0,0 +1,174 @@ +/* + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ + +// This header must be included above all other files + +#ifndef __GLOBAL_H +#define __GLOBAL_H + +#include + +#if PY_MAJOR_VERSION < 3 +#define PyUnicode_Check PyString_Check +#define PyUnicode_AsUTF8 PyString_AS_STRING +#define PyUnicode_FromString PyString_FromString +#define PyUnicode_Type PyString_Type +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "structmember.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// These two files are included at the end of the header +//#include "Object.h" +//#include "Set.h" + +using namespace std; +using namespace MiniZinc; + +static PyObject* MznModel_new_error; +static PyObject* MznModel_init_error; +static PyObject* MznModel_solve_error; +static PyObject* MznModel_load_error; +static PyObject* MznModel_loadFromString_error; +static PyObject* MznModel_solve_warning; +static PyObject* MznVariable_init_error; +static PyObject* MznSet_error; + +// Macro that combines snprintf and PyErr_SetString into one. +// First argument is PyExc_*Error, Second argument is like ordinary printf function +#define MZN_PYERR_SET_STRING(py_type_error, ...) \ + do { \ + char buffer[150]; \ + snprintf(buffer, 150, __VA_ARGS__); \ + PyErr_SetString(py_type_error, buffer); \ + } while (0) + +// Converts C++ long long to appropriate Python integer type +inline PyObject* c_to_py_number(long long); +// Converts Python integer type to C++ value, returns -1 if error occurred +inline long long py_to_c_number(PyObject*); +// Converts Python integer type to C++ value, set int* to -1 or 1 if overflown, returns -1 if other +// errors occurred +inline long long py_to_c_number(PyObject*, int*); + +// Nicely presenting the type, for example: set of int or array of [int, int] +string typePresentation(const Type& type); + +// For internal use, only compare TypeInst, BaseType, SetType +bool compareType(const Type& type1, const Type& type2); + +// Converts a minizinc expression of dim()==0 to python value. +// Note 1: e = vd->e(), type = vd->type() +// Note 2: e must be not NULL +inline PyObject* one_dim_minizinc_to_python(Expression* e, const Type& type); + +// Convert minizinc expression to python value, return a Python list if vd is an array +PyObject* minizinc_to_python(VarDecl* vd); + +/* + * Description: Helper function for python_to_minizinc + * converts a python value (not an array) to minizinc expression + * also returns the type of that value + * Parameters: A python value - pvalue + * A minizinc BaseType code + * Note: If code == Type::BT_UNKNOWN, it will be changed to the corresponding type of pvalue + * If code is initialized to specific type, an error will be thrown if type mismatched + * Accepted code type: + * - Type::BT_UNKNOWN: Unknown type, will be changed later + * - Type::BT_INT + * - Type::BT_FLOAT + * - Type::BT_STRING + * - Type::BT_BOOL + * Note 2: Need an outer GCLock for this to work + */ +inline Expression* one_dim_python_to_minizinc(PyObject* pvalue, Type::BaseType& code); + +/* + * Description: Converts a python value to minizinc expression + * also returns the type of python value + * and the dimension list if it is an array + * Return: MiniZinc expression, NULL if error occurred + * + * Note 1: If you parse a list of MiniZinc Object, remember to parse the object only, not its + *wrapper class For current implementation, the python wrapper class must be replaced with a call to + * minizinc_internal.Id(wrapper.name) (remember to wrap this function with lock and + *unlock()) Note 2: Need an outer GCLock for this to work Note 3: if dimList is empty, any array + *created by this function will have its index start from 0 else, the array created will have the + *dimension correspondingly to dimList, and it will complain if dimension sizes mismatched + */ +Expression* python_to_minizinc(PyObject* pvalue, Type& type, vector >& dimList); + +/* + * Used only when importing model from MiniZinc file + * Return: MiniZinc expression, NULL if error occurred + * Note: Need an outer GCLock + */ +Expression* python_to_minizinc(PyObject* pvalue, const ASTExprVec& ranges); + +/* + * A Python dimension list is like this + * [ [1,5], [2,5], [3,5] ] + * (1st dimension) + * (2nd dimension) + * (3rd dimension) + * + * This function converts such dimension list to minizinc ranges list + * [ <1..5>, <2..5>, <3..5> ] + * + * Returns: If error, return empty vector, also set errorOccurred to 1 + * Else, return a vector of MiniZinc dimension range + */ +vector pydim_to_minizinc_ranges(PyObject* pydim); + +// Helper functions +// XXX: change the function name to a more meaningful name later +// starting layer should be 0 +/* converts a python array(list) to: + dimensions: an array holding the size of each dimension + simpleArray: a 1d-array of Python value + return 0 if success, -1 if it is an incomplete array + ( incomplete array example: + X X X X X X + X X X X X + X X X X X X + X X X X X X + ) +*/ +int getList(PyObject* value, vector& dimensions, vector& simpleArray, + const int layer); + +inline bool PyObject_ExactTypeCheck(PyObject* ob, PyTypeObject* type) { + return Py_TYPE(ob) == type; +} + +#include "Object.h" +#include "Set.h" + +#endif \ No newline at end of file diff --git a/interfaces/python/minizinc.py b/interfaces/python/minizinc.py new file mode 100644 index 0000000..cfb4f54 --- /dev/null +++ b/interfaces/python/minizinc.py @@ -0,0 +1,1415 @@ +##@mainpage MiniZinc Python Interface +# @author Tai Tran +# @supervisor Guido Tack + + +#@Things to add: +# Improve the interface so that there is no need to assign MZN_STDLIB_DIR when starting python +# args_ret_dict: some functions type checking are not written +# Hide some internal functions such as flatten or type_presentation + +#@Suggestion: +# Remove the user-defined name when declaring variable +# Consider replacing it with i, where i is the number of arguments created +# for example: calling [a,b,c] = m.Variable(1,100,3) + +''' +@Usage: +definition: + lb: lower bound, + ub: upper bound, + set:minizinc.Model.Set) + + +* Declaration * + +minizinc.Model: + accepts 1 optional argument: + a string or list of strings indicating libraries to be included. + returns: + model to be used, for example: + m = minizinc.Model + i = m.Variable + s = m.Set + m.Constraint + m.satisfy + etc.. +minizinc.Model.Set: + accepts multiple arguments: + item1, item2, ... itemn + item?: a number - single element + or a list/tuple of 2 elements - elements ranging from lb to ub + returns: + an object to be used + functions: + push - push more elements onto the set + clear - clear all elements in the set +minizinc.Model.Variable: + accepts up to 2 arguments: + : a boolean variable + ub : a variable ranging from 0 to ub - 1 + lb, ub : a variable ranging from lb to ub + set : a variable based on the set +minizinc.Model.Array: +minizinc.Model.Construct: + + + + +* Functions * +minizinc.Model.satisfy +minizinc.Model.next +''' + +import sys +import minizinc_internal +import predicate +import annotation +#import inspect + +if sys.version < '3': + integer_types = (int, long, ) + python_types = (int, long, float, bool, str) + int_t = long + def longify(x): + return long(x) +else: + integer_types = (int, ) + python_types = (int, float, bool, str) + int_t = int + def longify(x): + return x + +# turns a complex structure of items into a list of items +def flatten(x): + result = [] + for el in x: + if isinstance(el, (list, tuple)): + result.extend(flatten(el)) + else: + result.append(el) + return result + +# nicely presents the type of a variable +def type_presentation(x): + if x is None: + return "Any_Type" + if type(x) is type: + name = x.__name__ + if name[:9] == 'minizinc.': + name = name[9:] + return name + else: + if type(x) is tuple: + ret = '(' + for i, t in enumerate(x): + if i != 0: + ret += ', ' + ret += type_presentation(t) + ret += ')' + elif type(x) is list: + ret = 'array' + str(len(x)) + 'd of ' + type_presentation(x[0]) + else: + raise TypeError('Type Presentation: Unexpected type: ' + type(x)) + return ret + + + +# All MiniZinc objects derived from here +class Expression(object): + def __init__(self, model = None): + self.model = model + + def __str__(self): + return str(self.get_value()) + + def is_decl(self): + return isinstance(self, Declaration) + + def is_pre(self): + return isinstance(self, Predicate) + + def evaluate(self, var): + if isinstance(var, Expression): + ret = var.get_value() + if ret == None: + raise ValueError("'" + var.name + "'Variable value is not set") + return ret + else: + return var + + def eval_type(self, args): + if isinstance(args, Expression): + return args.type + elif type(args) in integer_types: + return int_t + elif type(args) in (float, bool, str): + return type(args) + elif type(args) is list: + t = None + for i in args: + if t is None: + t = self.eval_type(i) + else: + type_i = self.eval_type(i) + if t != type_i: + raise TypeError("Type of arguments in an array must be the same: expected: " + + type_presentation((t,)) + ", received: " + type_presentation((type_i,)) ) + return [t] + elif type(args) is tuple: + t = [] + for i in args: + t.append(self.eval_type(i)) + return tuple(t) + else: + raise TypeError("Unexpected Type: " + type_presentation(type(args)) ) + + def __and__(self, pred): + return And( self, pred ) + + def __rand__(self, pred): + return And( self, pred ) + + def __or__(self, pred): + return Or( self, pred ) + + def __xor__(self, pred): + return Xor( self, pred ) + + def __ror__(self, pred): + return Or( self, pred ) + + def __add__(self, pred): + return Add( self, pred ) + + def __radd__(self, pred): + return Add( pred, self ) + + def __sub__(self, pred): + return Sub( self, pred ) + + def __rsub__(self, pred): + return Sub( pred, self ) + + def __div__(self, pred): + return Div( self, pred ) + + def __rdiv__(self, pred): + return Div( pred, self ) + + def __floordiv__(self, pred): + return FloorDiv( self, pred ) + + def __rfloordiv__(self, pred): + return FloorDiv( pred, self ) + + def __mul__(self, pred): + return Mul( self, pred ) + + def __rmul__(self, pred): + return Mul( self, pred ) + + def __mod__(self, pred): + return Mod( self, pred ) + + def __rmod__(self, pred): + return Mod( pred, self ) + + def __pow__(self, pred): + return Pow( self, pred ) + + def __rpow__(self, pred): + return Pow( pred, self ) + + def __eq__(self, pred): + return Eq( self, pred ) + + def __ne__(self, pred): + return Ne( self, pred ) + + def __lt__(self, pred): + return Lt( self, pred ) + + def __gt__(self, pred): + return Gt( self, pred ) + + def __le__(self, pred): + return Le( self, pred ) + + def __ge__(self, pred): + return Ge( self, pred ) + + def __neg__(self): + return Neg( self ) + + def __pos__(self): + return Pos( self ) + + def __invert__(self): + return Invert( self ) + + def __abs__(self): + return Abs( self ) + + +# Temporary container for Expression before evaluating to minizinc_internal object +# Calling Predicate.init automatically check: +# if the arguments belong to the same model +class Predicate(Expression): + def __init__(self, vars, args_and_return_type_tuple = None, name = None, model = None): + self.vars = vars + self.name = str(name) + + def eval_model(args): + model = None + for val in args: + if isinstance(val, Expression): + if hasattr(val, 'model'): + if val.model is not None: + if model is None: + model = val.model + elif model != val.model: + raise TypeError("Arguments in the Predicate don't belong to the same model") + return model + + self.vars_type = self.eval_type(vars) + self.model = eval_model(vars) + if args_and_return_type_tuple is not None: + for t in args_and_return_type_tuple: + def check_type(expected, actual): + if expected is None: + return True + if len(expected) != len(actual): + return False + for i, val in enumerate(expected): + if (val is None): + pass + elif type(val) is list and type(actual[i]) is list: + if check_type(val, actual[i]) == False: + return False + elif type(val) != type(actual[i]): + return False + return True + if check_type(t[0], self.vars_type): + #self.vars_type == t[0]: + self.type = t[1] + break + else: + s = "MiniZinc: function '" + self.name + "': Argument type does not match, expected: " + for i, t in enumerate(args_and_return_type_tuple): + if i != 0: + s += ' or ' + s += type_presentation(t[0]) + s += ', received: ' + type_presentation(self.vars_type) + raise TypeError(s) + else: + self.type = None + Expression.__init__(self) + + +class BinOp(Predicate): + def __init__(self, vars, code, args_and_return_type_tuple, name): + Predicate.__init__(self,vars,args_and_return_type_tuple, name) + self.BinOpCode = code + +class UnOp(Predicate): + def __init__(self, vars, code, args_and_return_type_tuple, name): + Predicate.__init__(self,vars, args_and_return_type_tuple, name) + self.UnOpCode = code + +class Call(Predicate): + def __init__(self, vars, code, args_and_return_type_tuple = None, name = None, model_list = None): + Predicate.__init__(self,vars, args_and_return_type_tuple, name) + self.CallCode = code + self.model_list = model_list + + + +args_ret_dict = {} + +to_assign = [ ((int_t, int_t), int_t ), + ((float, float), float ), + ((int_t, float), float ), + ((float, int_t), float)] +args_ret_dict['add'] = to_assign +args_ret_dict['sub'] = to_assign +args_ret_dict['mul'] = to_assign +args_ret_dict['div'] = to_assign +args_ret_dict['floordiv'] = to_assign +args_ret_dict['pow'] = to_assign + + +args_ret_dict['mod'] =[ ((int_t, int_t), int_t )] + +to_assign = [ ((int_t, int_t), bool ), + ((float, float), bool ), + ((bool, bool), bool ), + ((str, str), bool ), + ((minizinc_internal.VarSet, minizinc_internal.VarSet), bool)] +args_ret_dict['lt'] = to_assign +args_ret_dict['le'] = to_assign +args_ret_dict['gt'] = to_assign +args_ret_dict['ge'] = to_assign +args_ret_dict['eq'] = to_assign +args_ret_dict['ne'] = to_assign + + + +args_ret_dict['in'] = [ ((int_t, minizinc_internal.VarSet), bool) ] + + +to_assign = [ ((bool, bool), bool) ] +args_ret_dict['or'] = to_assign +args_ret_dict['and'] = to_assign +args_ret_dict['xor'] = to_assign + +args_ret_dict['abort'] = [ ((), bool)] +to_assign = [ ((int_t,), int_t), + ((float,), float) ] +args_ret_dict['abs'] = to_assign +args_ret_dict['neg'] = to_assign +args_ret_dict['pos'] = to_assign +args_ret_dict['invert'] = to_assign + +args_ret_dict['array1d'] = [ (None, [int_t]) ] #the function has checked its argument already +args_ret_dict['array2d'] = [ (None, [int_t, int_t]) ] +args_ret_dict['array3d'] = [ (None, [int_t, int_t, int_t]) ] +args_ret_dict['array4d'] = [ (None, [int_t, int_t, int_t, int_t])] +args_ret_dict['array5d'] = [ (None, [int_t, int_t, int_t, int_t, int_t])] +args_ret_dict['array6d'] = [ (None, [int_t, int_t, int_t, int_t, int_t, int_t])] + +to_assign = [ ((float,), float) ] +args_ret_dict['sin'] = to_assign +args_ret_dict['cos'] = to_assign +args_ret_dict['tan'] = to_assign +args_ret_dict['asin'] = to_assign +args_ret_dict['acos'] = to_assign +args_ret_dict['atan'] = to_assign +args_ret_dict['sinh'] = to_assign +args_ret_dict['cosh'] = to_assign +args_ret_dict['tanh'] = to_assign +args_ret_dict['asinh'] = to_assign +args_ret_dict['acosh'] = to_assign +args_ret_dict['atanh'] = to_assign +args_ret_dict['sqrt'] = to_assign +args_ret_dict['ln'] = to_assign +args_ret_dict['log2'] = to_assign +args_ret_dict['log10'] = to_assign +args_ret_dict['exp'] = to_assign + +args_ret_dict['log'] = [ ((float, float), float) ] + + +args_ret_dict['assert'] = [ ((bool, str), bool) ] +args_ret_dict['bool2int'] = [ ((bool,), int_t) ] +# XXX what to do with cardinality? +args_ret_dict['card'] = None + +to_assign = [ ((float,), int_t) ] +args_ret_dict['ceil'] = to_assign +args_ret_dict['floor'] = to_assign +args_ret_dict['round'] = to_assign + +to_assign = [ ((int_t,), float) ] +args_ret_dict['int2float'] = to_assign +# XXX consider remove +args_ret_dict['concat'] = None + + +to_assign = [ ((int_t, int_t), int_t), + ((float, float), float), + (([int_t],), int_t), + (([float],), float) ] +args_ret_dict['min'] = to_assign +args_ret_dict['max'] = to_assign + +to_assign = [ (([int_t], ), int_t), + (([float], ), float) ] +args_ret_dict['product'] = to_assign +args_ret_dict['sum'] = to_assign + + +del to_assign + + + +class Add(BinOp): + def __init__(self, *vars): + BinOp.__init__(self, vars, 0, args_ret_dict['add'], '+') + + def get_symbol(self): + return '+' + + def get_value(self): + lhs = self.evaluate(self.vars[0]) + rhs = self.evaluate(self.vars[1]) + if isinstance(rhs, str): + lhs = str(lhs) + elif isinstance(lhs, str): + rhs = str(rhs) + return lhs + rhs + +class Sub(BinOp): + def __init__(self, *vars): + BinOp.__init__(self, vars, 1, args_ret_dict['sub'], '-') + + def get_symbol(self): + return '-' + + def get_value(self): + return self.evaluate(self.vars[0]) - self.evaluate(self.vars[1]) + +class Mul(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 2, args_ret_dict['mul'], '*') + + def get_symbol(self): + return '*' + + def get_value(self): + return self.evaluate(self.vars[0]) * self.evaluate(self.vars[1]) + +class Div(BinOp): + + def __init__(self, *vars) : + BinOp.__init__(self, vars, 3, args_ret_dict['div'], '/') + + def get_symbol(self): + return '/' + + def get_value(self): + return self.evaluate(self.vars[0]) / self.evaluate(self.vars[1]) + +class FloorDiv(BinOp): + + def __init__(self, *vars) : + BinOp.__init__(self, vars, 4, args_ret_dict['floordiv'], '//') + + def get_symbol(self): + return '//' + + def get_value(self): + return self.evaluate(self.vars[0]) // self.evaluate(self.vars[1]) + + +class Mod(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 5, args_ret_dict['mod'], '%') + + def get_symbol(self): + return '%' + + def get_value(self): + return self.evaluate(self.vars[0]) % self.evaluate(self.vars[1]) + + +class Lt(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 6, args_ret_dict['lt'], '<') + + def get_symbol(self): + return '<' + + def get_value(self): + return self.evaluate(self.vars[0]) < self.evaluate(self.vars[1]) + +class Le(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 7, args_ret_dict['le'], '<=') + + def get_symbol(self): + return '<=' + + def get_value(self): + return self.evaluate(self.vars[0]) <= self.evaluate(self.vars[1]) + +class Gt(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 8, args_ret_dict['gt'], '>') + + def get_symbol(self): + return '>' + + def get_value(self): + return self.evaluate(self.vars[0]) > self.evaluate(self.vars[1]) + +class Ge(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 9, args_ret_dict['ge'], '>=') + + def get_symbol(self): + return '>=' + + def get_value(self): + return self.evaluate(self.vars[0]) >= self.evaluate(self.vars[1]) + + +class Eq(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 10, args_ret_dict['eq'], '==') + + def get_symbol(self): + return '==' + + def get_value(self): + return self.evaluate(self.vars[0]) == self.evaluate(self.vars[1]) + +class Ne(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 11, args_ret_dict['ne'], '!=') + + def get_symbol(self): + return '!=' + + def get_value(self): + return self.evaluate(self.vars[0]) != self.evaluate(self.vars[1]) + +class In(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 12, args_ret_dict['in'], 'in') + + def get_symbol(self): + return 'in' + + def get_value(self): + return self.evaluate(self.vars[0]) in self.evaluate(self.vars[1]) + + def __nonzero__(self): + return self + + +class Or(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 23, args_ret_dict['or'], 'or') + + def get_symbol(self): + return 'or' + + def get_value(self): + return self.evaluate(self.vars[0]) or self.evaluate(self.vars[1]) + +class And(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 24, args_ret_dict['and'], 'and') + + def get_symbol(self): + return '&' + + def get_value(self): + return self.evaluate(self.vars[0]) & self.evaluate(self.vars[1]) + +class Xor(BinOp): + + def __init__(self, *vars): + BinOp.__init__(self, vars, 25, args_ret_dict['xor'], 'xor') + + def get_symbol(self): + return 'xor' + + def get_value(self): + return bool(self.evaluate(self.vars[0])) != bool(self.evaluate(self.vars[1])) + +class Pow(Call): + def __init__(self, *vars): + Call.__init__(self, vars, "pow", args_ret_dict['pow'], '^') + + def get_symbol(self): + return '**' + + def get_value(self): + return self.evaluate(self.vars[0]) ** self.evaluate(self.vars[1]) + +class Abort(Call): + def __init__(self, *vars): + Call.__init__(self, vars, "abort", args_ret_dict['abort'], 'abort') + +class Abs(Call): + def __init__(self, *vars): + Call.__init__(self, vars, "abs", args_ret_dict['abs'], 'abs') + + +class Array1d(Call): + def __init__(self, dim1, array): + Call.__init__(self,(dim1, array),"array1d", args_ret_dict['array1d'], 'array1d') + +class Array2d(Call): + def __init__(self, dim1, dim2, array): + Call.__init__(self, (dim1, dim2, array), "array2d", args_ret_dict['array2d'], 'array2d') + +class Array3d(Call): + def __init__(self, dim1, dim2, dim3, array): + Call.__init__(self,[dim1, dim2, dim3, array],"array3d", args_ret_dict['array3d'], 'array3d') + +class Array4d(Call): + def __init__(self, dim1, dim2, dim3, dim4, array): + Call.__init__(self,[dim1, dim2, dim3, dim4, array],"array4d", args_ret_dict['array4d'], 'array4d') + +class Array5d(Call): + def __init__(self, dim1, dim2, dim3, dim4, dim5, array): + Call.__init__(self,[dim1, dim2, dim3, dim4, dim5, array],"array5d", args_ret_dict['array5d'], 'array5d') + +class Array6d(Call): + def __init__(self, dim1, dim2, dim3, dim4, dim5, dim6, array): + Call.__init__(self,[dim1, dim2, dim3, dim4, dim5, dim6, array],"array6d", args_ret_dict['array6d'], 'array6d') + +class Acos(Call): + def __init__(self, var): + Call.__init__(self, (var,), "acos", args_ret_dict['acos'], 'acos') + +class Acosh(Call): + def __init__(self, var): + Call.__init__(self, (var,), "acosh", args_ret_dict['acosh'], 'acosh') + +class Asin(Call): + def __init__(self, var): + Call.__init__(self, (var,), "asin", args_ret_dict['asin'], 'asin') + +class Asinh(Call): + def __init__(self, var): + Call.__init__(self, (var,), "asinh", args_ret_dict['asinh'], 'asinh') + +class Atan(Call): + def __init__(self, var): + Call.__init__(self, (var,), "atan", args_ret_dict['atan'], 'atan') + +class Atanh(Call): + def __init__(self, var): + Call.__init__(self, (var,), "atanh", args_ret_dict['atanh'], 'atanh') + +class Assert(Call): + def __init__(self, constraint, message): + Call.__init__(self, (constraint, message), "assert", args_ret_dict['assert'], 'assert') + +class Bool2Int(Call): + def __init__(self, var): + Call.__init__(self, (var,), "bool2int", args_ret_dict['bool2int'], 'bool2int') + +class Card(Call): + def __init__(self, var): + Call.__init__(self, (var,), "card", args_ret_dict['card'], 'card') + +class Ceil(Call): + def __init__(self, var): + Call.__init__(self, (var,), "ceil", args_ret_dict['ceil'], 'ceil') + +class Concat(Call): + def __init__(self, var): + Call.__init__(self, (var,), "concat",) + +class Cos(Call): + def __init__(self, var): + Call.__init__(self, (var,), "cos", args_ret_dict['cos'], 'cos') + +class Cosh(Call): + def __init__(self, var): + Call.__init__(self, (var,), "cosh", args_ret_dict['cosh'], 'cosh') + +class Dom(Call): + def __init__(self, var): + Call.__init__(self,[var],"dom") + +class Dom_Array(Call): + def __init__(self, var): + Call.__init__(self,[var],"dom_array") + +class Dom_Size(Call): + def __init__(self, var): + Call.__init__(self,[var],"dom_size") + +class Fix(Call): + def __init__(self, var): + Call.__init__(self,[var],"fix") + +class Floor(Call): + def __init__(self, var): + Call.__init__(self, (var,),"floor", args_ret_dict['floor'], 'floor') + +class Exp(Call): + def __init__(self, var): + Call.__init__(self, (var,),"exp", args_ret_dict['exp'], 'exp') + +class Int2Float(Call): + def __init__(self, var): + Call.__init__(self, (var,),"int2float", args_ret_dict['int2float'], 'int2float') + +class Is_Fixed(Call): + def __init__(self, var): + Call.__init__(self,[var],"is_fixed") + +class Join(Call): + def __init__(self, var): + Call.__init__(self,[var],"join") + +class Lb(Call): + def __init__(self, var): + Call.__init__(self,[var],"lb") + +class Lb_array(Call): + def __init__(self, var): + Call.__init__(self,[var],"lb_array") + +class Length(Call): + def __init__(self, var): + Call.__init__(self,[var],"length") + +class Ln(Call): + def __init__(self, var): + Call.__init__(self, (var,),"ln", args_ret_dict['ln'], 'ln') + +class Log(Call): + def __init__(self, var1, var2): + Call.__init__(self, (var1, var2),"log", args_ret_dict['log'], 'log') + +class Log2(Call): + def __init__(self, var): + Call.__init__(self, (var,), "log2", args_ret_dict['log2'], 'log2') + +class Log10(Call): + def __init__(self, var): + Call.__init__(self, (var,), "log10", args_ret_dict['log10'], 'log10') + +class Min(Call): + def __init__(self, var): + Call.__init__(self, (var,), "min", args_ret_dict(['min']), 'min') + +class Max(Call): + def __init__(self, var): + Call.__init__(self, (var,), "max", args_ret_dict(['max']), 'max') + +class Product(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"product", args_ret_dict['product'], 'product') + +class Round(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"round", args_ret_dict['round'], 'round') + +class Set2array(Call): + def __init__(self, var): + Call.__init__(self,[var],"set2array") + +class Sin(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"sin", args_ret_dict['sin'], 'sin') + +class Sinh(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"sinh", args_ret_dict['sinh'], 'sinh') + +class Sqrt(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"sqrt", args_ret_dict['sqrt'], 'sqrt') + +class Sum(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"sum", args_ret_dict['sum'], 'sum') + +class Tan(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"tan", args_ret_dict['tan'], 'tan') + +class Tanh(Call): + def __init__(self, var): + Call.__init__(self, (var, ),"tanh", args_ret_dict['tanh'], 'tanh') + +class Trace(Call): + def __init__(self, var): + Call.__init__(self,[var],"trace") + +class Ub(Call): + def __init__(self, var): + Call.__init__(self,[var],"ub") + +class Ub_Array(Call): + def __init__(self, var): + Call.__init__(self,[var],"ub_array") + + +class Neg(UnOp): + def __init__(self, var): + UnOp.__init__(self, (var, ), 2, args_ret_dict['neg'], 'neg') + +class Pos(UnOp): + def __init__(self, var): + UnOp.__init__(self, (var, ), 1, args_ret_dict['pos'], 'pos') + +class Invert(Predicate): + def __init__(self, vars): + UnOp.__init__(self, (var, ), 0, args_ret_dict['invert'], 'invert') + +class Id(Expression): + def __init__(self, name, model_list = None): + self.name = name + self.model_list = model_list + self.type = minizinc_internal.Annotation + + +# Temporary container for Variable Declaration +class Declaration(Expression): + def __init__(self, model): + if not isinstance(model, Model): + raise TypeError('Warning: First argument must be a Model Object') + Expression.__init__(self, model) + self.name = '"' + str(id(self)) + '"' + self.solve_counter = -1 + self.next_counter = -1 + self.value = None + + def get_value(self): + if self.solve_counter == self.model.solve_counter and self.next_counter == self.model.next_counter: + return self.value + self.solve_counter = self.model.solve_counter + self.next_counter = self.model.next_counter + self.value = self.model.mznsolver.get_value(self.name) + return self.value + + def __str__(self): + return str(self.get_value()) + + def __repr__(self): + return 'A Minizinc ' + self.__class__.__name__ + ' with the value of ' + self.__str__() + + +class Construct(Declaration): + def __init__(self, model, arg1): + Declaration.__init__(self, model) + self.type = self.eval_type(arg1) + self.has_minizinc_objects = False + + def unwrap(arg): + if isinstance(arg, Expression): + self.has_minizinc_objects = True + return arg.obj + elif type(arg) in (list, tuple): + ret = [] + for val in arg: + ret.append(unwrap(val)) + return ret + elif type(arg) in python_types: + return arg + else: + raise TypeError('Unexpected type: argument should be a single/list/tuple of MiniZinc objects or Python basic types') + + try: + self.obj = model.mznmodel.Declaration(self.name, unwrap(arg1)) + except: + print sys.exc_info()[0] + raise + + if self.has_minizinc_objects: + self.value = arg1 + else: + self.wrapped_value = arg1 + self.class_name = 'Construct' + + ''' + imagine we have: + a = Variable(1,100) undefined + b = Variable(1,100) undefined + + c = Construct([a,b]) [ undefined, undefined ] + when the model is solved, c's value should be changed + ''' + + def get_value(self): + if self.has_minizinc_objects: + def get_value_helper(arg): + if isinstance(arg, Expression): + return arg.get_value() + elif type(arg) is list: + ret = [] + for val in arg: + ret.append(get_value_helper(val)) + return ret + elif type(arg) in python_types: + return arg + else: + raise TypeError('Internal error: Unexpected type') + if not (self.solve_counter == self.model.solve_counter and self.next_counter == self.obj.next_counter): + self.value = get_value_helper(self.wrapped_value) + return self.value + + + + +class Variable(Declaration): + def __init__(self, model, arg1=None, arg2=None): + Declaration.__init__(self, model) + lb, ub = False, True + if arg1 is not None: + typearg1 = type(arg1) + if arg2 is None: + if typearg1 is Set: + lb = arg1 + ub = None + elif typearg1 in (list,tuple): + if len(arg1) != 2: + raise ValueError('Requires a list or tuple of exactly 2 numbers') + lb,ub = sorted(arg1)[0,1] + else: + ub = arg1 - 1 + lb = typearg1(lb) + else: + lb,ub = arg1, arg2 + + typelb, typeub = type(lb), type(ub) + if not typelb is Set: + if typelb not in (bool, float, ) and typelb not in integer_types and not isinstance(typelb, Declaration): + raise TypeError('Lower bound must be a boolean, an int, a float or a set') + if typeub not in (bool, float, ) and typeub not in integer_types and not isinstance(typelb, Declaration): + raise TypeError('Upper bound must be a boolean, an int or a float') + if typelb != typeub: + raise TypeError('Upper bound an dlower bound is of different type') + if lb > ub: + raise ValueError('Lower bound cannot be greater than upper bound') + + self.dim_list = [] + + if typelb is bool: + self.obj = model.mznmodel.Declaration(self.name, 10, []) + self.type = bool + elif typelb in integer_types: + self.obj = model.mznmodel.Declaration(self.name, 9, [], lb, ub) + self.type = int_t + elif typelb is float: + self.obj = model.mznmodel.Declaration(self.name, 11, [], lb, ub) + self.type = float + elif typelb is Set: + self.obj = model.mznmodel.Declaration(self.name, 9, [], lb.obj) + self.type = int_t + else: + raise TypeError('Internal: Unexpected type') + + self.class_name = 'Variable' + +class VariableConstruct(Variable, Construct): + def __init__(self, model, arg1, arg2 = None): + Construct.__init__(self, model, arg1, arg2) + +class Array(Variable): + def __init__(self, model, argopt1, argopt2, *args): + Declaration.__init__(self, model) + dim_list = [] + lb = None + ub = None + + def add_to_dim_list(i): + if type(i) is int: + if i > 0: + dim_list.append([0,i-1]) + else: + raise TypeError('Single value must be a positive integer') + elif type(i) is list: + if type(i[0]) in integer_types and type(i[-1]) in integer_types: + dim_list.append([ i[0],i[-1]] ) + else: + raise TypeError('Range boundaries must be integers') + elif isinstance(i, Set): + if i.continuous(): + dim_list.append([i.min(), i.max()]) + else: + raise TypeError('Array ranges must be continuous') + else: + raise TypeError('Unknown type') + + if type(argopt1) is Set: + lb = argopt1 + ub = None + add_to_dim_list(argopt2) + elif type(argopt1) is bool and type(argopt2) is bool: + lb = argopt1 + ub = argopt2 + elif type(argopt2) not in integer_types: + if type(argopt1) not in integer_types: + raise TypeError('Range values must be integers') + lb = 0 + ub = argopt1 - 1 + add_to_dim_list(argopt2) + else: + if type(argopt1) not in integer_types or type(argopt2) not in integer_types: + raise TypeError('Lower bound and upper bound must be integers') + lb = argopt1 + ub = argopt2 + + for i in args: + add_to_dim_list(i) + + self.lb = lb + self.ub = ub + if dim_list == []: + raise AttributeError('Initialising an Array without dimension list') + self.dim_list = dim_list + tlb = type(argopt1) + if tlb is bool: + self.type = [bool] * len(dim_list) + self.obj = model.mznmodel.Declaration(self.name,10,dim_list,lb,ub) + elif tlb in integer_types: + self.type = [int_t] * len(dim_list) + self.obj = model.mznmodel.Declaration(self.name,9,dim_list, lb, ub) + elif tlb is float: #isinstance(lb, float): + self.type = [float] * len(dim_list) + self.obj = model.mznmodel.Declaration(self.name,11,dim_list,lb,ub) + elif tlb is Set: + self.type = [int_t] * len(dim_list) + self.obj = model.mznmodel.Declaration(self.name,9,dim_list,lb.obj) + else: + raise TypeError('Unexpected type') + + self.class_name = 'Array' + + def __getitem__(self, *args): + return ArrayAccess(self.model, self, args[0]) + + +# XXX: to be rewritten later +class ArrayAccess(Array): + def __init__(self, model, array, idx): + Declaration.__init__(self, model) + if type(idx) is not tuple: + idx = [idx] + else: + idx = flatten(idx) + if len(idx) != len(array.dim_list): + raise IndexError('Requires exactly ' + str(len(self.dim_list)) + ' index values') + for i, value in enumerate(idx): + if not isinstance(value, Expression): + if value < array.dim_list[i][0] or value > array.dim_list[i][1]: + raise IndexError('Index at pos ' + str(i) + ' is out of range') + + self.array = array + self.idx = idx + self.type = array.type[0] + self.value = None + self.class_name = 'Array Item' + + def get_value(self): + if hasattr(self, 'solve_counter'): + if self.solve_counter == self.model.solve_counter and self.next_counter == self.model.next_counter: + return self.value + self.solve_counter = self.model.solve_counter + self.next_counter = self.model.next_counter + arrayvalue = self.array.get_value() + if arrayvalue is None: + return None + else: + for i,value in enumerate(self.idx): + arrayvalue = arrayvalue[value - self.array.dim_list[i][0]] + self.value = arrayvalue + return self.value + return self.value + +class ArrayConstruct(Array, Construct): + def __init__(self, model, arg1, arg2 = None): + Construct.__init__(self, model, arg1, arg2) + + +class Set(Declaration): + # Set is model independent and can be reused in multiple models + # Thus, Set.model = None + def __init__(self, *args): + self.model = None + self.name = '"' + str(id(self)) + '"' + ''' + lb, ub = None, None + set_list = None + + if argopt2 is not None: + lb,ub = argopt1, argopt2 + else: + if type(argopt1) is list: + set_list = argopt1 + else: + ub = argopt1 - 1 + lb = 0 + + if set_list is None: + if not (type(lb) in integer_types and type(ub) in integer_types): + raise TypeError('Lower bound and upper bound must be integers') + set_list = [[lb, ub]] + ''' + self.obj = minizinc_internal.Set(*args) + self.type = minizinc_internal.Set + self.class_name = 'Set' + + def push(self, *args): + self.obj.push(*args) + + def clear(self): + self.obj.clear() + + def continuous(self): + return self.obj.continuous() + + def min(self): + return self.obj.min() + + def max(self): + return self.obj.max() + + def get_value(self): + return self.obj + + def __iter__(self): + return self.obj.__iter__() + + def __contains__(self, val): + return self.obj.contains(val) + +class VarSet(Variable): + def __init__(self, model, argopt1, argopt2 = None): + self.model = model + self.name = '"' + str(id(self)) + '"' + + lb, ub = None, None + set_list = None + if argopt2 is not None: + lb,ub = argopt1, argopt2 + else: + if type(argopt1) is list: + set_list = argopt1 + elif type(argopt1) is Set: + lb = argopt1.min() + ub = argopt1.max() + else: + ub = argopt1 - 1 + lb = 0 + + if set_list is None: + if not (type(lb) in integer_types and type(ub) in integer_types): + raise TypeError('Lower bound and upper bound must be integers') + + model.mznmodel.Declaration(self.name, 12, [], lb, ub) + self.type = minizinc_internal.VarSet + self.class_name = 'Var Set' + + ''' Python automatically evaluate a __contains__ return object to boolean + thus, we have to use separate In class instead of + for i in VarSet + def __contains__(self, argopt): + return In([argopt, self]) + ''' + +variable_model_dict = {} +function_model_dict = {} +name_model_dict = {} +def handlerFunctionClosure(name, args_list, model_list): + def handlerFunction(*args): + return Call(args, name, args_list, name, model_list) + return handlerFunction + +def handlerFunction(name, model_list): + return Id(name, model_list) + +def init(args = None, model = None): + def assign_usable_names_to_model(name, model): + if name in name_model_dict: + if model is None: + name_model_dict[name] = None + else: + name_model_dict[name].append(model) + return False + else: + if model is None: + name_model_dict[name] = None + else: + name_model_dict[name] = [model] + return True + + names = minizinc_internal.retrieveNames(args) + for name, args_and_return_type_tuple in names["boolfuncs"].items(): + if assign_usable_names_to_model(name, model): + setattr(predicate, name, handlerFunctionClosure(name, args_and_return_type_tuple, name_model_dict[name])) + + for name, args_and_return_type_tuple in names["annfuncs"].items(): + if assign_usable_names_to_model(name, model): + setattr(predicate, name, handlerFunctionClosure(name, args_and_return_type_tuple, name_model_dict[name])) + + for name in names["annvars"]: + if assign_usable_names_to_model(name, model): + setattr(annotation, name, handlerFunction(name, name_model_dict[name])) + +init() + +class Model(object): + def __init__(self, args = None): + self.loaded = False + self.mznsolver = None + self.mznmodel = minizinc_internal.Model(args) + if args: + init(args, self) + self.solve_counter = -1 + self.next_counter = -1 + + + ''' + # not used anymore + def get_name(self,var): + itemList = [var_name for var_name, var_val in self.frame if var_val is var] + if len(itemList) == 1: + return itemList[0] + elif len(itemList) == 0: + raise LookupError('Internal Error: variable name not found') + else: + raise LookupError('The object pointed to was assigned to different names') + ''' + + def add_recursive(self, expr): + if isinstance(expr, (tuple, list)): + for i in expr: + self.add_recursive(i) + else: + if issubclass(type(expr), Expression): + if expr.is_pre(): + self.mznmodel.Constraint(self.evaluate(expr)) + else: + raise(TypeError, "Unexpected Expression") + else: + self.mznmodel.Constraint(expr) + + def Constraint(self, *expr): + minizinc_internal.lock() + if len(expr)>0: + self.loaded = True + #self.frame = inspect.currentframe().f_back.f_locals.items() + self.add_recursive(expr) + #del self.frame + minizinc_internal.unlock() + + def evaluate(self, expr): + if not isinstance(expr, Expression): + if isinstance(expr, (list, tuple)): + for i,item in enumerate(expr): + expr[i] = self.evaluate(item) + return expr + if isinstance(expr, Id): + if expr.model_list is not None: + if not self in expr.model_list: + raise TypeError("The annotation '" + expr.name + "' does not belong to the model") + return minizinc_internal.Id(expr.name) + if isinstance(expr, Call): + variables = [] + model = None + for i in expr.vars: + variables.append(self.evaluate(i)) + if expr.model_list is not None: + if not self in expr.model_list: + raise TypeError("The function '" + expr.name + "' does not belong to the model") + return minizinc_internal.Call(expr.CallCode, variables, expr.type) + elif isinstance(expr, ArrayAccess): + for i,value in enumerate(expr.idx): + if isinstance(value, Expression): + expr.idx[i] = self.evaluate(value) + return minizinc_internal.at(expr.array.obj, expr.idx) + elif isinstance(expr, Declaration): + if expr.model != self: + raise TypeError("The Declaration not belongs to this model") + return expr.obj + elif isinstance(expr, BinOp): + if expr.model is not None and expr.model != self: + raise TypeError("The Declaration not belongs to this model") + lhs = self.evaluate(expr.vars[0]) + rhs = self.evaluate(expr.vars[1]) + return minizinc_internal.BinOp(lhs, expr.BinOpCode, rhs) + elif isinstance(expr, UnOp): + if expr.model is not None and expr.model != self: + raise TypeError("The Declaration not belongs to this model") + rhs = self.evaluate(expr.vars[0]) + return minizinc_internal.UnOp(expr.UnOpCode, rhs) + else: + raise TypeError('Variable Type unspecified') + + + + def Variable(self, argopt1=None, argopt2=None): + return Variable(self, argopt1, argopt2) + + def Array(self, argopt1, argopt2, *args): + list_ = [] + for i in args: + list_.append(i) + return Array(self, argopt1, argopt2, *list_) + + def Set(self, *args): + return Set(*args) + + def Range(self, arg1, arg2 = None): + if (arg2 is None): + return Set((0, arg1 - 1)) + else: + return Set((arg1, arg2)) + + def VarSet(self, argopt1, argopt2 = None): + return VarSet(self, argopt1, argopt2) + + def Construct(self, argopt1): + if isinstance(argopt1, list): + return ArrayConstruct(self, argopt1) + else: + return VariableConstruct(self, argopt1) + + def __solve(self, code, expr, ann, data, solver, time): + minizinc_internal.lock() + + if ann is not None: + if not hasattr(ann, 'type') or ann.type != minizinc_internal.Annotation: + raise TypeError('Unexpected type of annotation') + eval_ann = self.evaluate(ann) + eval_expr = self.evaluate(expr) + + savedModel = self.mznmodel.copy() + + # The model with declared variable cannot be deleted. + self.mznmodel, savedModel = savedModel, self.mznmodel + self.mznmodel.SolveItem(code, eval_ann, eval_expr) + if data is not None: + self.add_recursive(data) + minizinc_internal.unlock() + + self.mznsolver = self.mznmodel.solve(solver = solver, time = time) + self.mznmodel = savedModel + self.solve_counter = self.solve_counter + 1 + self.next_counter = -1 + + + def satisfy(self, ann = None, data = None, solver = 'gecode', time = 0): + self.__solve(0, None, ann, data, solver, time) + def maximize(self, expr, ann = None, data = None, solver = 'gecode', time = 0): + self.__solve(2, expr, ann, data, solver, time) + def minimize(self, expr, ann = None, data = None, solver = 'gecode', time = 0): + self.__solve(1, expr, ann, data, solver, time) + def reset(self): + self.__init__() + + def next(self): + if self.mznsolver is None: + raise ValueError('Model is not solved yet') + self.status = self.mznsolver.next() + self.next_counter = self.next_counter + 1 + return (self.status is None) + + def is_loaded(self): + return self.loaded + + def is_solved(self): + return self.mznsolver != None + + def set_time_limit(self, time): + self.mznmodel.set_time_limit(time) + + def set_solver(self, solver): + self.mznmodel.set_solver(solver) + + def _debugprint(self): + self.mznmodel.debugprint() + diff --git a/interfaces/python/policy.py b/interfaces/python/policy.py new file mode 100644 index 0000000..9d0eff7 --- /dev/null +++ b/interfaces/python/policy.py @@ -0,0 +1,53 @@ +# A political problem + +# Suppose that you are a politician trying to win an election. Your district has three +# different types of areas - urban, suburban, and rural. These areas have, respectively, +# 100,000, 200,000, and 50,000 registered voters. Although not all the registered voters +# actually go to the polls, you decide that to govern effectively, you would like at least +# half the registered voters in each of the three regions to vote for you. You are honorable +# and would never consider supporting policies in which you do not believe. You realize, +# however, that certain issues may be more effective in winning votes in certain places. +# Your primary issues are building more roads, gun control, farm subsidies, and a gasoline tax +# dedicated to improved public transit. +# According to your campaign staff's research, you can estimate how many votes you win or lose +# from each population segment by spending $1,000 on advertising on each issue. +# POLICY | URBAN SUBURBAN RURAL +# ----------------------------------------- +# build roads | -2 5 3 +# gun control | 8 2 -5 +# farm subsidies | 0 0 10 +# gasoline tax | 10 0 -2 +# +# In this table, each entry indicates the number of thousands of either urban, suburban, +# or rural voters who would be won over by spending $1,000 on advertising in support of +# a particular issue. Negative entries denote votes that would be lost. +# Your task is to figure out the minimum amount of mony that you need to spend in order to win +# 50,000 urban votes, 10,000 suburban votes, and 25,000 rural votes. + + +from minizinc import * + +m = Model() + +x1 = m.Variable(0.0,10000.0) +x2 = m.Variable(0.0,10000.0) +x3 = m.Variable(0.0,10000.0) +x4 = m.Variable(0.0,10000.0) + +m.Constraint( -2.0*x1 + 8.0*x2 + 0.0*x3 + 10.0*x4 >= 50.0, + 5.0*x1 + 2.0*x2 + 0.0*x3 + 0.0*x4 >= 100.0, + 3.0*x1 - 5.0*x2 + 10.0*x3 - 2.0*x4 >= 25.0, + ) + +m._debugprint() +m.mznmodel.set_time_limit(10) + +m.minimize(x1+x2+x3+x4) + +m.next() + +print "Minimize cost = ", x1 + x2 + x3 + x4 +print "where x1 = ", x1 +print " x2 = ", x2 +print " x3 = ", x3 +print " x4 = ", x4 diff --git a/interfaces/python/predicate/__init__.py b/interfaces/python/predicate/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/interfaces/python/predicate/__init__.pyc b/interfaces/python/predicate/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb1cacd1e6b49ef11fa77a94e7ad22eb326cdd8c GIT binary patch literal 108 zcmZSn%*(Z-;aNyB0~9baFfceUFfbIeFfcHrFfasbfJFQ>K+PO2S9e=$fq0IAs%2mk;8 literal 0 HcmV?d00001 diff --git a/interfaces/python/pyinterface.cpp b/interfaces/python/pyinterface.cpp new file mode 100644 index 0000000..dcf755f --- /dev/null +++ b/interfaces/python/pyinterface.cpp @@ -0,0 +1,385 @@ +/* Python Interface for MiniZinc constraint modelling + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ + +#include "pyinterface.h" + +using namespace MiniZinc; +using namespace std; + +static PyObject* Mzn_Call(PyObject* self, PyObject* args) { + const char* name; + PyObject* variableTuple; + PyTypeObject* returnType; + if (!PyArg_ParseTuple(args, "sO|O", &name, &variableTuple, &returnType)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Mzn_Call: Accepts 2 values: a string, a list of minizinc variable"); + PyErr_Print(); + return NULL; + } + + if (!PyList_Check(variableTuple)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Mzn_Call: Second argument must be a list"); + return NULL; + } + + long len = PyList_GET_SIZE(variableTuple); + vector expressionList(len); + for (long i = 0; i != len; ++i) { + PyObject* pyval = PyList_GET_ITEM(variableTuple, i); + if (PyObject_TypeCheck(pyval, &MznObject_Type)) { + expressionList[i] = MznObject_get_e(reinterpret_cast(pyval)); + } else { + Type type; + vector > dimList; + expressionList[i] = python_to_minizinc(pyval, type, dimList); + if (expressionList[i] == NULL) { + MZN_PYERR_SET_STRING(PyExc_RuntimeError, + "MiniZinc: MznCall: Second argument, item at position %li must be a " + "MiniZinc Object or Python int/float/string/list/tuple", + i); + return NULL; + } + } + } + + PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL); + + reinterpret_cast(ret)->e = new Call(Location(), string(name), expressionList); + + return ret; +} + +static PyObject* Mzn_Id(PyObject* self, PyObject* args) { + const char* name; + if (!PyArg_ParseTuple(args, "s", &name)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Mzn_Id: Argument must be a string"); + return NULL; + } + PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL); + reinterpret_cast(ret)->e = new Id(Location(), name, NULL); + return ret; +} + +static PyObject* Mzn_at(PyObject* self, PyObject* args) { + PyObject* Py_array; + PyObject* Py_idx; + if (!PyArg_ParseTuple(args, "OO", &Py_array, &Py_idx)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: at: Parsing error"); + return NULL; + } + + if (!PyObject_TypeCheck(Py_array, &MznExpression_Type)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: at: First argument must be a MiniZinc Expression"); + return NULL; + } + + if (!PyList_Check(Py_idx)) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: at: Second argument must be a list of indices"); + return NULL; + } + + Py_ssize_t n = PyList_GET_SIZE(Py_idx); + + vector idx(n); + for (Py_ssize_t i = 0; i != n; ++i) { + PyObject* obj = PyList_GetItem(Py_idx, i); +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(obj)) { + long index = PyInt_AS_LONG(obj); + idx[i] = IntLit::a(IntVal(index)); + } else +#endif + if (PyLong_Check(obj)) { + int overflow; + long long index = PyLong_AsLongLongAndOverflow(obj, &overflow); + if (overflow) { + MZN_PYERR_SET_STRING(PyExc_OverflowError, "MiniZinc: at: Index at pos %li overflowed", i); + return NULL; + } + idx[i] = IntLit::a(IntVal(index)); + } else if (PyObject_TypeCheck(obj, &MznExpression_Type)) { + idx[i] = reinterpret_cast(obj)->e; + } else { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: ArrayAccess: Indices must be integers or MiniZinc Expression"); + return NULL; + } + } + + MznExpression* ret = + reinterpret_cast(MznExpression_new(&MznExpression_Type, NULL, NULL)); + ret->e = new ArrayAccess(Location(), reinterpret_cast(Py_array)->e, idx); + return reinterpret_cast(ret); +} + +static PyObject* Mzn_UnOp(PyObject* self, PyObject* args) { + /* + enum UnOpType { + UOT_NOT, // 0 + UOT_PLUS, // 1 + UOT_MINUS // 2 + };*/ + PyObject* r; + unsigned int op; + if (!PyArg_ParseTuple(args, "IO", &op, &r)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Mzn_UnOp: Requires a MiniZinc object and an integer"); + return NULL; + } + Expression* rhs; + + if (PyObject_TypeCheck(r, &MznExpression_Type)) { + rhs = reinterpret_cast(r)->e; + } else if (PyBool_Check(r)) { + rhs = new BoolLit(Location(), PyObject_IsTrue(r)); + } else +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(r)) { + rhs = IntLit::a(IntVal(PyInt_AS_LONG(r))); + } else +#endif + if (PyLong_Check(r)) { + int overflow; + long long c_val = PyLong_AsLongLongAndOverflow(r, &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, "MiniZinc: Mzn_UnOp: Object is overflowed"); + return NULL; + } + rhs = IntLit::a(IntVal(c_val)); + } else if (PyFloat_Check(r)) { + rhs = new FloatLit(Location(), PyFloat_AS_DOUBLE(r)); + } else if (PyUnicode_Check(r)) { + rhs = new StringLit(Location(), string(PyUnicode_AsUTF8(r))); + } else { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Mzn_UnOp: Object must be a Python value or a MiniZinc object"); + return NULL; + } + + GCLock Lock; + + PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL); + reinterpret_cast(ret)->e = new UnOp(Location(), static_cast(op), rhs); + return ret; +} + +/* + * Description: Creates a minizinc BinOp expression + * Note: Need an outer GCLock for this to work + */ +static PyObject* Mzn_BinOp(PyObject* self, PyObject* args) { + /* + enum BinOpType { + BOT_PLUS, // 0 + BOT_MINUS, // 1 + BOT_MULT, // 2 + BOT_DIV, // 3 + BOT_IDIV, // 4 + BOT_MOD, // 5 + BOT_LE, // 6 + BOT_LQ, // 7 + BOT_GR, // 8 + BOT_GQ, // 9 + BOT_EQ, //10 + BOT_NQ, //11 + BOT_IN, //12 + BOT_SUBSET, //13 + BOT_SUPERSET, //14 + BOT_UNION, //15 + BOT_DIFF, //16 + BOT_SYMDIFF, //17 + BOT_INTERSECT, //18 + BOT_PLUSPLUS, //19 + BOT_EQUIV, //20 + BOT_IMPL, //21 + BOT_RIMPL, //22 + BOT_OR, //23 + BOT_AND, //24 + BOT_XOR, //25 + BOT_DOTDOT //26 + };*/ + PyObject* PyPre[2]; + unsigned int op; + if (!PyArg_ParseTuple(args, "OIO", &PyPre[0], &op, &PyPre[1])) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Mzn_BinOp: Requires two MiniZinc objects and an integer"); + return NULL; + } + Expression* pre[2]; + // pre[0]: lhs; + // pre[1]: rhs; + for (int i = 0; i != 2; ++i) { + if (PyObject_TypeCheck(PyPre[i], &MznExpression_Type)) { + pre[i] = reinterpret_cast(PyPre[i])->e; + } else if (PyBool_Check(PyPre[i])) { + pre[i] = new BoolLit(Location(), PyObject_IsTrue(PyPre[i])); + } else +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(PyPre[i])) { + pre[i] = IntLit::a(IntVal(PyInt_AS_LONG(PyPre[i]))); + } else +#endif + + if (PyLong_Check(PyPre[i])) { + int overflow; + long long c_val = PyLong_AsLongLongAndOverflow(PyPre[i], &overflow); + if (overflow) { + PyErr_SetString(PyExc_OverflowError, "MiniZinc: Mzn_UnOp: Object is overflowed"); + return NULL; + } + pre[i] = IntLit::a(IntVal(c_val)); + } else if (PyFloat_Check(PyPre[i])) { + pre[i] = new FloatLit(Location(), PyFloat_AS_DOUBLE(PyPre[i])); + } else if (PyUnicode_Check(PyPre[i])) { + pre[i] = new StringLit(Location(), string(PyUnicode_AsUTF8(PyPre[i]))); + } else { + if (i == 0) + PyErr_SetString( + PyExc_TypeError, + "MiniZinc: Mzn_BinOp: Left hand side object must be a Python value or MiniZinc object"); + else + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Mzn_BinOp: Right hand side object must be a Python value or " + "MiniZinc object"); + return NULL; + } + } + + GCLock Lock; + + PyObject* ret = MznExpression_new(&MznExpression_Type, NULL, NULL); + reinterpret_cast(ret)->e = + (new BinOp(Location(), pre[0], static_cast(op), pre[1])); + return ret; +} + +static PyObject* Mzn_load(PyObject* self, PyObject* args, PyObject* keywds) { + PyObject* model = MznModel_new(&MznModel_Type, NULL, NULL); + if (MznModel_init(reinterpret_cast(model), NULL) < 0) return NULL; + if (MznModel_load(reinterpret_cast(model), args, keywds) == NULL) return NULL; + return model; +} + +static PyObject* Mzn_load_from_string(PyObject* self, PyObject* args, PyObject* keywds) { + PyObject* model = MznModel_new(&MznModel_Type, NULL, NULL); + if (model == NULL) return NULL; + if (MznModel_init(reinterpret_cast(model), NULL) < 0) return NULL; + if (MznModel_load_from_string(reinterpret_cast(model), args, keywds) == NULL) + return NULL; + return model; +} + +static PyObject* Mzn_retrieveNames(PyObject* self, PyObject* args) { + PyObject* boolfuncs = PyDict_New(); + PyObject* annfuncs = PyDict_New(); + PyObject* annvars = PyList_New(0); + PyObject* libName = NULL; + + { + Py_ssize_t n = PyTuple_GET_SIZE(args); + if (n > 1) { + PyErr_SetString(PyExc_TypeError, "MiniZinc: Mzn_retrieveNames: accepts at most 1 argument"); + return NULL; + } else if (n == 1) { + libName = PyTuple_GET_ITEM(args, 0); + if (PyObject_IsTrue(libName)) { + if (!PyUnicode_Check(libName)) { + PyErr_SetString(PyExc_TypeError, + "MiniZinc: Mzn_retrieveNames: first argument must be a string"); + return NULL; + } + } else + libName = NULL; + } + } + + // If a library name is specified here, it means that this function is called at least once + // already. If that's the case, functions in globals.mzn and stdlib.mzn will be already defined, + // so we dont want to reinclude it + bool include_global_mzn = (libName == NULL); + + MznModel* tempModel = reinterpret_cast(MznModel_new(&MznModel_Type, NULL, NULL)); + + if (MznModel_init(tempModel, libName) != 0) { + return NULL; + } + CollectBoolFuncNames bool_fv(boolfuncs, include_global_mzn); + CollectAnnNames ann_fv(annfuncs, annvars, include_global_mzn); + iterItems(bool_fv, tempModel->_m); + iterItems(ann_fv, tempModel->_m); + MznModel_dealloc(tempModel); + + PyObject* dict = PyDict_New(); + PyDict_SetItemString(dict, "boolfuncs", boolfuncs); + PyDict_SetItemString(dict, "annfuncs", annfuncs); + PyDict_SetItemString(dict, "annvars", annvars); + + Py_DECREF(boolfuncs); + Py_DECREF(annfuncs); + Py_DECREF(annvars); + return dict; +} + +#if PY_MAJOR_VERSION >= 3 + +#define INITERROR return NULL + +// PyObject* +PyMODINIT_FUNC PyInit_minizinc_internal(void) +#else +#define INITERROR return + +PyMODINIT_FUNC initminizinc_internal(void) + +#endif + +{ +#if PY_MAJOR_VERSION >= 3 + PyObject* module = PyModule_Create(&moduledef); +#else + PyObject* module = Py_InitModule3("minizinc_internal", Mzn_methods, + "A python interface for MiniZinc constraint modeling"); +#endif + + if (module == NULL) INITERROR; + + if (PyType_Ready(&MznObject_Type) < 0) INITERROR; + Py_INCREF(&MznObject_Type); + PyModule_AddObject(module, "Object", reinterpret_cast(&MznObject_Type)); + + if (PyType_Ready(&MznSetIter_Type) < 0) INITERROR; + Py_INCREF(&MznSetIter_Type); + PyModule_AddObject(module, "Set_Iter", reinterpret_cast(&MznSetIter_Type)); + + if (PyType_Ready(&MznExpression_Type) < 0) INITERROR; + Py_INCREF(&MznExpression_Type); + PyModule_AddObject(module, "Expression", reinterpret_cast(&MznExpression_Type)); + + if (PyType_Ready(&MznAnnotation_Type) < 0) INITERROR; + Py_INCREF(&MznAnnotation_Type); + PyModule_AddObject(module, "Annotation", reinterpret_cast(&MznAnnotation_Type)); + + if (PyType_Ready(&MznSet_Type) < 0) INITERROR; + Py_INCREF(&MznSet_Type); + PyModule_AddObject(module, "Set", reinterpret_cast(&MznSet_Type)); + + if (PyType_Ready(&MznVarSet_Type) < 0) INITERROR; + Py_INCREF(&MznVarSet_Type); + PyModule_AddObject(module, "VarSet", reinterpret_cast(&MznVarSet_Type)); + + if (PyType_Ready(&MznModel_Type) < 0) INITERROR; + Py_INCREF(&MznModel_Type); + PyModule_AddObject(module, "Model", reinterpret_cast(&MznModel_Type)); + + if (PyType_Ready(&PyMznSolver_Type) < 0) INITERROR; + Py_INCREF(&PyMznSolver_Type); + PyModule_AddObject(module, "Solver", reinterpret_cast(&PyMznSolver_Type)); + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} diff --git a/interfaces/python/pyinterface.h b/interfaces/python/pyinterface.h new file mode 100644 index 0000000..142edeb --- /dev/null +++ b/interfaces/python/pyinterface.h @@ -0,0 +1,255 @@ +/* + * Author: + * Tai Tran + * Supervisor: + * Guido Tack + */ +#ifndef __PYINTERFACE_H +#define __PYINTERFACE_H + +#include "Annotation.h" +#include "Expression.h" +#include "Model.cpp" +#include "Model.h" +#include "Object.h" +#include "Set.cpp" +#include "Set.h" +#include "Solver.cpp" +#include "Solver.h" +#include "VarSet.h" +#include "global.cpp" +#include "global.h" + +/* Helper functions that load data into a new model + * For example: instead of + import minizinc_internal + model = minizinc_internal.Model() + model.load(...) + we can do this: + import minizinc_internal + model = minizinc_internal.load(...) + */ +static PyObject* Mzn_load(PyObject* self, PyObject* args, PyObject* keywds); +static PyObject* Mzn_load_from_string(PyObject* self, PyObject* args, PyObject* keywds); + +// GC::lock() and GC::unlock() +static PyObject* Mzn_lock(PyObject* self) { + GC::lock(); + Py_RETURN_NONE; +} +static PyObject* Mzn_unlock(PyObject* self) { + GC::unlock(); + Py_RETURN_NONE; +} + +/************************************************************ + Groups of function that need an outer GCLock to work + ************************************************************/ +// Note: Please see Note 1 of the first python_to_minizinc declaration in global.h first + +// Requires: +// - An UnOpType integer opcode +// - A MiniZinc expression or Python value +static PyObject* Mzn_UnOp(PyObject* self, PyObject* args); + +// Requires: +// - A left hand sided MiniZinc expression or Python value +// - A BinOpType integer opcode +// - A right hand sided MiniZinc expression or Python value +static PyObject* Mzn_BinOp(PyObject* self, PyObject* args); + +// Requires: +// - Name of the MiniZinc function to be called (string) +// - List of MiniZinc object or Python value +// - (Optional, deprecated) Return type of Mzn_Call +static PyObject* Mzn_Call(PyObject* self, PyObject* args); + +// Requires a name(string) +static PyObject* Mzn_Id(PyObject* self, PyObject* args); +// Requires: +// - A MiniZinc expression of array type +// - A list of integer indices (can be MiniZinc expression as well) +static PyObject* Mzn_at(PyObject* self, PyObject* args); + +// ***************** END OF GROUPS ***************** + +/* Definition: Returns a dictionary of keys: "boolfuncs", "annfuncs" and "annvars" (currently) + For each '*funcs': + Itself is another dictionary: + key: name of function + value: a list of all possible combination of parsing argument types and return type: + for each item in that list: + item[0] is a tuple argument types: + if a function accepts (int, int), it should be (, ) + + if a function accepts (array of [int, int]), it should be + ( [, ], ) + (notice the appearance of a list here) + item[1] is the return type: + MiniZinc Type - Python Type + int - long + float - float + bool - bool + string - str + VarSet - minizinc.VarSet + Ann - minizinc.Annotation + Top - NoneType (accepts everything) + others - minizinc.Object (means an error) + + For 'annvars': + Just a list of ann names + + */ +static PyObject* Mzn_retrieveNames(PyObject* self, PyObject* args); + +/********************* SKIP ******************************* */ +PyObject* eval_type(TypeInst* ti) { + ASTExprVec ranges = ti->ranges(); + PyObject* v; + switch (ti->type().bt()) { + case Type::BT_BOOL: + v = reinterpret_cast(&PyBool_Type); + break; + case Type::BT_INT: + if (ti->type().st() == Type::ST_SET) + v = reinterpret_cast(&MznVarSet_Type); + else + v = reinterpret_cast(&PyLong_Type); + break; + case Type::BT_FLOAT: + v = reinterpret_cast(&PyFloat_Type); + break; + case Type::BT_STRING: + v = reinterpret_cast(&PyUnicode_Type); + break; + case Type::BT_ANN: + v = reinterpret_cast(&MznAnnotation_Type); + break; + case Type::BT_TOP: + v = Py_None; + break; + default: // v = reinterpret_cast(&MznVariable_Type); + // cout << ti->type().bt() << endl; + v = reinterpret_cast(&MznObject_Type); + break; + throw runtime_error("CollectBoolFunctionNames: unexpected type"); + } + + if (ranges.size() == 0) { + Py_INCREF(v); + return v; + } else { + PyObject* args_tuple = PyList_New(ranges.size()); + for (int i = 0; i != ranges.size(); ++i) { + Py_INCREF(v); + PyList_SET_ITEM(args_tuple, i, v); + } + return args_tuple; + } +} + +void add_to_dictionary(FunctionI* fi, PyObject* toAdd) { + ASTExprVec params = fi->params(); + const char* str = fi->id().str().c_str(); + PyObject* key = PyUnicode_FromString(str); + + PyObject* args_and_return_type_tuple = PyTuple_New(2); + PyObject* args_tuple = PyTuple_New(params.size()); + for (unsigned int i = 0; i < params.size(); ++i) { + PyTuple_SET_ITEM(args_tuple, i, eval_type(params[i]->ti())); + } + PyObject* return_type = eval_type(fi->ti()); + + PyTuple_SET_ITEM(args_and_return_type_tuple, 0, args_tuple); + PyTuple_SET_ITEM(args_and_return_type_tuple, 1, return_type); + + PyObject* toAdd_item = PyDict_GetItem(toAdd, key); + if (toAdd_item == NULL) { + toAdd_item = PyList_New(1); + PyList_SET_ITEM(toAdd_item, 0, args_and_return_type_tuple); + if (PyDict_SetItem(toAdd, key, toAdd_item) != 0) + throw runtime_error("CollectBoolFunctionNames: cannot set new key to the dictionary"); + Py_DECREF(toAdd_item); + } else { + if (PyList_Append(toAdd_item, args_and_return_type_tuple) != 0) + throw runtime_error("CollectBoolFunctionNames: cannot append item to the list"); + } +} + +class CollectBoolFuncNames : public ItemVisitor { +protected: + bool include_global_mzn; + PyObject* _boolfuncs; + +public: + CollectBoolFuncNames(PyObject* boolfuncs, bool include_global_mzn0) + : _boolfuncs(boolfuncs), include_global_mzn(include_global_mzn0) {} + bool enterModel(Model* m) { + return m->filename() != "stdlib.mzn" && (include_global_mzn || m->filename() != "globals.mzn"); + } + void vFunctionI(FunctionI* fi) { + if (fi->ti()->type().isvarbool() == false) return; + + add_to_dictionary(fi, _boolfuncs); + } +}; + +class CollectAnnNames : public ItemVisitor { +protected: + PyObject* _annfuncs; + PyObject* _annvars; + bool include_global_mzn; + +public: + CollectAnnNames(PyObject* annfuncs, PyObject* annvars, bool include_global_mzn0) + : _annfuncs(annfuncs), _annvars(annvars), include_global_mzn(include_global_mzn0) {} + bool enterModel(Model* m) { + return include_global_mzn || (m->filename() != "globals.mzn" && m->filename() != "stdlib.mzn"); + } + void vFunctionI(FunctionI* fi) { + if (fi->ti()->type().isann() == false) return; + + add_to_dictionary(fi, _annfuncs); + } + void vVarDeclI(VarDeclI* vdi) { + if (vdi->e()->ti()->type().isann()) { + PyList_Append(_annvars, PyUnicode_FromString(vdi->e()->id()->str().c_str())); + } + } +}; + +/********************* END OF SKIP ******************************* */ + +static PyMethodDef Mzn_methods[] = { + {"load", (PyCFunction)Mzn_load, METH_KEYWORDS, "Load MiniZinc model from MiniZinc file"}, + {"load_from_string", (PyCFunction)Mzn_load_from_string, METH_KEYWORDS, + "Load MiniZinc model from stdin"}, + {"BinOp", (PyCFunction)Mzn_BinOp, METH_VARARGS, "Add a binary expression into the model"}, + {"UnOp", (PyCFunction)Mzn_UnOp, METH_VARARGS, "Add a unary expression into the model"}, + {"Id", (PyCFunction)Mzn_Id, METH_VARARGS, + "Return a MiniZinc Variable containing the given name"}, + {"Call", (PyCFunction)Mzn_Call, METH_VARARGS, "MiniZinc Call"}, + + {"at", (PyCFunction)Mzn_at, METH_VARARGS, "Array Access"}, + {"retrieveNames", (PyCFunction)Mzn_retrieveNames, METH_VARARGS, + "Returns names of MiniZinc functions and variables"}, + {"lock", (PyCFunction)Mzn_lock, METH_NOARGS, "Internal: Create a lock for garbage collection"}, + {"unlock", (PyCFunction)Mzn_lock, METH_NOARGS, + "Internal: Unlock a lock for garbage collection"}, + {NULL}}; + +#if PY_MAJOR_VERSION >= 3 + +static struct PyModuleDef moduledef = {PyModuleDef_HEAD_INIT, + "minizinc", + "A python interface for MiniZinc constraint modeling", + -1, + Mzn_methods, + NULL, + NULL, + NULL, + NULL}; + +#endif + +#endif \ No newline at end of file diff --git a/interfaces/python/setup.py b/interfaces/python/setup.py new file mode 100644 index 0000000..f0e3c1e --- /dev/null +++ b/interfaces/python/setup.py @@ -0,0 +1,42 @@ +from distutils.core import setup, Extension +import sys + +EXTRA_COMPILE_ARGS = [ + '-O3', + #'-Wno-unused-label', + #'-Wno-unused-function', + '-I', '../../include' +] +EXTRA_LINK_ARGS = [] + +if sys.platform == 'darwin': + EXTRA_COMPILE_ARGS.extend([ + '-stdlib=libc++', + '-arch', 'x86_64' + '-mmacosx-version-min=10.8', + ]) + EXTRA_LINK_ARGS.extend(['-arch', 'x86_64', '-stdlib=libc++', '-mmacosx-version-min=10.8']) +elif sys.platform.startswith('linux'): + EXTRA_COMPILE_ARGS.extend(['-std=c++11']) + pass + +module1 = Extension('minizinc_internal', + sources = ['pyinterface.cpp'], + libraries = ['minizinc_gecode', 'minizinc', + 'gecodedriver', + 'gecodeminimodel', + 'gecodesearch', + 'gecodeset', + 'gecodefloat', + 'gecodeint', + 'gecodekernel', + 'gecodesupport' + ], + extra_compile_args= EXTRA_COMPILE_ARGS + ['-fPIC'], + extra_link_args = EXTRA_LINK_ARGS + ) + +setup (name = 'MiniZinc', + version = '2.0', + description = 'A Python interface to the MiniZinc constraint modelling language', + ext_modules = [module1]) diff --git a/lib/MIPdomains.cpp b/lib/MIPdomains.cpp new file mode 100644 index 0000000..243642f --- /dev/null +++ b/lib/MIPdomains.cpp @@ -0,0 +1,2120 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was ! distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#undef ERROR // MICROsoft. +#undef min +#undef max +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +// temporary +#include +//#include + +#include +#include +#include + +/// TODOs +/// TODO Not going to work for float vars because of round-offs in the domain interval sorting... +/// set_in etc. are ! propagated between views +/// CLEANUP after work: ~destructor +/// Also check initexpr of all vars? DONE +/// In case of only_range_domains we'd need to register inequalities +/// - so better turn that off TODO +/// CSE for lineq coefs TODO + +/// TODO use integer division instead of INT_EPS +#define INT_EPS 1e-5 // the absolute epsilon for integrality of integer vars. + +#define __MZN__MIPDOMAINS__PRINTMORESTATS +#define MZN_DBG_CHECK_ITER_CUTOUT + +// #define __MZN__DBGOUT__MIPDOMAINS__ +#ifdef __MZN__DBGOUT__MIPDOMAINS__ +#define DBGOUT_MIPD(s) std::cerr << s << std::endl +#define DBGOUT_MIPD__(s) std::cerr << s << std::flush +#define DBGOUT_MIPD_SELF(op) op +#else +#define DBGOUT_MIPD(s) \ + do { \ + } while (false) +#define DBGOUT_MIPD__(s) \ + do { \ + } while (false) +#define DBGOUT_MIPD_SELF(op) \ + do { \ + } while (false) +#endif + +namespace MiniZinc { + +enum EnumStatIdx__MIPD { + N_POSTs__all, // N all POSTs in the model + N_POSTs__intCmpReif, + N_POSTs__floatCmpReif, // in detail + N_POSTs__intNE, + N_POSTs__floatNE, + N_POSTs__setIn, + N_POSTs__domain, + N_POSTs__setInReif, + N_POSTs__eq_encode, + N_POSTs__intAux, + N_POSTs__floatAux, + // Kind of equality connections between involved variables + N_POSTs__eq2intlineq, + N_POSTs__eq2floatlineq, + N_POSTs__int2float, + N_POSTs__internalvarredef, + N_POSTs__initexpr1id, + N_POSTs__initexpr1linexp, + N_POSTs__initexprN, + N_POSTs__eqNlineq, + N_POSTs__eqNmapsize, + // other + N_POSTs__varsDirect, + N_POSTs__varsInvolved, + N_POSTs__NSubintvMin, + N_POSTs__NSubintvSum, + N_POSTs__NSubintvMax, // as N subintervals + N_POSTs__SubSizeMin, + N_POSTs__SubSizeSum, + N_POSTs__SubSizeMax, // subintv. size + N_POSTs__linCoefMin, + N_POSTs__linCoefMax, + N_POSTs__cliquesWithEqEncode, + N_POSTs__clEEEnforced, + N_POSTs__clEEFound, + N_POSTs__size +}; +extern std::vector MIPD__stats; + +enum EnumReifType { RIT_None, RIT_Static, RIT_Reif, RIT_Halfreif }; +enum EnumConstrType { CT_None, CT_Comparison, CT_SetIn, CT_Encode }; +enum EnumCmpType { + CMPT_None = 0, + CMPT_LE = -4, + CMPT_GE = 4, + CMPT_EQ = 1, + CMPT_NE = 3, + CMPT_LT = -5, + CMPT_GT = 5, + CMPT_LE_0 = -6, + CMPT_GE_0 = 6, + CMPT_EQ_0 = 2, + CMPT_LT_0 = -7, + CMPT_GT_0 = 7 +}; +enum EnumVarType { VT_None, VT_Int, VT_Float }; + +/// struct DomainCallType describes & characterizes a possible domain constr call +struct DCT { + const char* sFuncName = 0; + const std::vector& aParams; + // unsigned iItem; // call's item number in the flat + EnumReifType nReifType = RIT_None; // 0/static/halfreif/reif + EnumConstrType nConstrType = CT_None; // + EnumCmpType nCmpType = CMPT_None; + EnumVarType nVarType = VT_None; + FunctionI*& pfi; + // double dEps = -1.0; + DCT(const char* fn, const std::vector& prm, EnumReifType er, EnumConstrType ec, + EnumCmpType ecmp, EnumVarType ev, FunctionI*& pfi__) + : sFuncName(fn), + aParams(prm), + nReifType(er), + nConstrType(ec), + nCmpType(ecmp), + nVarType(ev), + pfi(pfi__) {} +}; + +template +struct Interval { + N left = infMinus(), right = infPlus(); + mutable VarDecl* varFlag = 0; + /*constexpr*/ static N infMinus() { + return (std::numeric_limits::has_infinity) ? -std::numeric_limits::infinity() + : std::numeric_limits::lowest(); + } + /*constexpr*/ static N infPlus() { + return (std::numeric_limits::has_infinity) ? std::numeric_limits::infinity() + : std::numeric_limits::max(); + } + Interval(N a = infMinus(), N b = infPlus()) : left(a), right(b) {} + bool operator<(const Interval& intv) const { + if (left < intv.left) { + // assert( right <= intv.left ); // assume disjoint + return true; + } + return false; + } +}; +typedef Interval IntvReal; + +template +std::ostream& operator<<(std::ostream& os, const Interval& ii) { + os << "[ " << ii.left << ", " << ii.right << " ] "; + return os; +} + +template +class SetOfIntervals : public std::multiset > { +public: + using Intv = Interval; + typedef std::multiset > Base; + typedef typename Base::iterator iterator; + SetOfIntervals() : Base() {} + SetOfIntervals(std::initializer_list > il) : Base(il) {} + template + SetOfIntervals(Iter i1, Iter i2) : Base(i1, i2) {} + /// Number of integer values in all the intervals + /// Assumes the interval bounds are ints + int card_int() const; + /// Max interval length + N max_interval() const; + /// Special insert function: check if interval is ok + iterator insert(const Interval& iv) { + if (iv.left > iv.right) { + DBGOUT_MIPD("Interval " << iv.left << ".." << iv.right + << " is empty, difference: " << (iv.right - iv.left) << ". Skipping"); + return Base::end(); + } + return Base::insert(iv); + } + template + void intersect(const SetOfIntervals& s2); + /// Assumes open intervals to cut out from closed + template + void cutDeltas(const SetOfIntervals& s2, N1 delta); + template + void cutDeltas(N1 left, N1 right, N1 delta) { + SetOfIntervals soi; + soi.insert(Interval(left, right)); + cutDeltas(soi, delta); + } + /// Cut out an open interval from a set of closed ones (except for infinities) + void cutOut(const Interval& intv); + typedef std::pair SplitResult; + SplitResult split(iterator& it, N pos); + bool checkFiniteBounds(); + /// Check there are no useless interval splittings + bool checkDisjunctStrict(); + Interval getBounds() const; + /// Split domain into the integer values + /// May assume integer bounds + void split2Bits(); +}; // class SetOfIntervals +typedef SetOfIntervals SetOfIntvReal; + +template +std::ostream& operator<<(std::ostream& os, const SetOfIntervals& soi) { + os << "[[ "; + for (auto& ii : soi) { + os << "[ " << ii.left << ", " << ii.right; + if (ii.varFlag) os << " @" << ii.varFlag; + os << " ] "; + } + os << "]]"; + return os; +} + +template +class LinEq__ { +public: + Coefs coefs; + Vars vd; + double rhs; +}; + +template +static std::ostream& operator<<(std::ostream& os, LinEq__& led) { + os << "( ["; + for (auto c : led.coefs) os << c << ' '; + os << " ] * [ "; + for (auto v : led.vd) os << v->id()->str() << ' '; + os << " ] ) == " << led.rhs; + return os; +} + +typedef LinEq__, std::array > LinEq2Vars; +typedef LinEq__, std::vector > LinEq; +// struct LinEq2Vars { +// std::array coefs; +// std::array vd = { { 0, 0 } }; +// double rhs; +// }; +// +// struct LinEq { +// std::vector coefs; +// std::vector vd; +// double rhs; +// }; + +std::vector MIPD__stats(N_POSTs__size); + +template +static std::vector make_vec(T t1, T t2) { + T c_array[] = {t1, t2}; + std::vector result(c_array, c_array + sizeof(c_array) / sizeof(c_array[0])); + return result; +} +template +static std::vector make_vec(T t1, T t2, T t3) { + T c_array[] = {t1, t2, t3}; + std::vector result(c_array, c_array + sizeof(c_array) / sizeof(c_array[0])); + return result; +} +template +static std::vector make_vec(T t1, T t2, T t3, T t4) { + T c_array[] = {t1, t2, t3, t4}; + std::vector result(c_array, c_array + sizeof(c_array) / sizeof(c_array[0])); + return result; +} + +class MIPD { +public: + MIPD(Env* env, bool fV, int nmi, double dmd) + : nMaxIntv2Bits(nmi), dMaxNValueDensity(dmd), __env(env) { + getEnv(); + fVerbose = fV; + } + static bool fVerbose; + const int nMaxIntv2Bits = 0; // Maximal interval length to enforce equality encoding + const double dMaxNValueDensity = 3.0; // Maximal ratio card_int() / size() of a domain + // to enforce ee + bool MIPdomains() { + MIPD__stats[N_POSTs__NSubintvMin] = 1e100; + MIPD__stats[N_POSTs__SubSizeMin] = 1e100; + + if (!registerLinearConstraintDecls()) return true; + if (!register__POSTconstraintDecls()) // not declared => no conversions + return true; + register__POSTvariables(); + if (vVarDescr.empty()) return true; + constructVarViewCliques(); + if (!decomposeDomains()) return false; + if (fVerbose) printStats(std::cerr); + return true; + } + +private: + Env* __env = 0; + Env* getEnv() { + MZN_MIPD__assert_hard(__env); + return __env; + } + + typedef VarDecl* PVarDecl; + + FunctionI* int_lin_eq; + FunctionI* int_lin_le; + FunctionI* float_lin_eq; + FunctionI* float_lin_le; + FunctionI* int2float; + FunctionI* lin_exp_int; + FunctionI* lin_exp_float; + + std::vector int_lin_eq_t = make_vec(Type::parint(1), Type::varint(1), Type::parint()); + std::vector float_lin_eq_t = + make_vec(Type::parfloat(1), Type::varfloat(1), Type::parfloat()); + std::vector t_VIVF = make_vec(Type::varint(), Type::varfloat()); + + // double float_lt_EPS_coef__ = 1e-5; + + bool registerLinearConstraintDecls() { + EnvI& env = getEnv()->envi(); + GCLock lock; + + int_lin_eq = env.model->matchFn(env, constants().ids.int_.lin_eq, int_lin_eq_t, false); + DBGOUT_MIPD(" int_lin_eq = " << int_lin_eq); + // MZN_MIPD__assert_hard(fi); + // int_lin_eq = (fi && fi->e()) ? fi : NULL; + int_lin_le = env.model->matchFn(env, constants().ids.int_.lin_le, int_lin_eq_t, false); + float_lin_eq = env.model->matchFn(env, constants().ids.float_.lin_eq, float_lin_eq_t, false); + float_lin_le = env.model->matchFn(env, constants().ids.float_.lin_le, float_lin_eq_t, false); + int2float = env.model->matchFn(env, constants().ids.int2float, t_VIVF, false); + + lin_exp_int = env.model->matchFn(env, constants().ids.lin_exp, int_lin_eq_t, false); + lin_exp_float = env.model->matchFn(env, constants().ids.lin_exp, float_lin_eq_t, false); + + if (!(int_lin_eq && int_lin_le && float_lin_eq && float_lin_le)) { + // say something... + return false; + } + return true; + + // std::cerr << " lin_exp_int=" << lin_exp_int << std::endl; + // std::cerr << " lin_exp_float=" << lin_exp_float << std::endl; + // For this to work, need to define a function, see mzn_only_range_domains() + // { + // GCLock lock; + // Call* call_EPS_for_LT = + // new Call(Location(),"mzn_float_lt_EPS_coef__", std::vector()); + // call_EPS_for_LT->type(Type::parfloat()); + // call_EPS_for_LT->decl(env.model->matchFn(getEnv()->envi(), call_EPS_for_LT)); + // float_lt_EPS_coef__ = eval_float(getEnv()->envi(), call_EPS_for_LT); + // } + } + // bool matchAndMarkFunction(); + // std::set funcs; + + /// Possible function param sets + std::vector t_VII = make_vec(Type::varint(), Type::parint()); + std::vector t_VIVI = make_vec(Type::varint(), Type::varint()); + std::vector t_VIIVI = make_vec(Type::varint(), Type::parint(), Type::varint()); + std::vector t_VFVI = make_vec(Type::varfloat(), Type::varint()); + std::vector t_VFVF = make_vec(Type::varfloat(), Type::varfloat()); + std::vector t_VFFVI = make_vec(Type::varfloat(), Type::parfloat(), Type::varint()); + std::vector t_VFFVIF = + make_vec(Type::varfloat(), Type::parfloat(), Type::varint(), Type::parfloat()); + std::vector t_VFVIF = make_vec(Type::varfloat(), Type::varint(), Type::parfloat()); + std::vector t_VFVIVF = make_vec(Type::varfloat(), Type::varint(), Type::varfloat()); + std::vector t_VFVIVFF = + make_vec(Type::varfloat(), Type::varint(), Type::varfloat(), Type::parfloat()); + std::vector t_VFVFF = make_vec(Type::varfloat(), Type::varfloat(), Type::parfloat()); + std::vector t_VFFF = make_vec(Type::varfloat(), Type::parfloat(), Type::parfloat()); + // std::vector t_VFVFVIF({ Type::varfloat(), Type::varfloat(), Type::varint(), + // Type::parfloat() }); + + std::vector t_VIAVI = make_vec(Type::varint(), Type::varint(1)); + std::vector t_VISI = make_vec(Type::varint(), Type::parsetint()); + std::vector t_VISIVI = make_vec(Type::varint(), Type::parsetint(), Type::varint()); + + // std::vector t_intarray(1); + // t_intarray[0] = Type::parint(-1); + + typedef std::unordered_map M__POSTCallTypes; + M__POSTCallTypes mCallTypes; // actually declared in the input + std::vector aCT; // all possible + + // Fails: + // DomainCallType a = { NULL, t_VII, RIT_Halfreif, CT_Comparison, CMPT_EQ, VT_Float }; + + /// struct VarDescr stores some info about variables involved in domain constr + struct VarDescr { + typedef unsigned char boolShort; + VarDescr(VarDecl* vd_, boolShort fi, double l_ = 0.0, double u_ = 0.0) + : lb(l_), ub(u_), vd(vd_), fInt(fi) {} + double lb, ub; + VarDecl* vd = 0; + int nClique = -1; // clique number + // std::vector aCalls; + std::vector aCalls; + boolShort fInt = 0; + ConstraintI* pEqEncoding = 0; + boolShort fDomainConstrProcessed = 0; + // boolShort fPropagatedViews=0; + // boolShort fPropagatedLargerEqns=0; + }; + + std::vector vVarDescr; + + FunctionI *int_le_reif__POST = 0, *int_ge_reif__POST = 0, *int_eq_reif__POST = 0, + *int_ne__POST = 0, *float_le_reif__POST = 0, *float_ge_reif__POST = 0, + *aux_float_lt_zero_iff_1__POST = 0, *float_eq_reif__POST = 0, *float_ne__POST = 0, + *aux_float_eq_zero_if_1__POST = 0, *aux_int_le_zero_if_1__POST = 0, + *aux_float_le_zero_if_1__POST = 0, *aux_float_lt_zero_if_1__POST = 0, + *equality_encoding__POST = 0, *set_in__POST = 0, *set_in_reif__POST = 0; + + bool register__POSTconstraintDecls() { + EnvI& env = getEnv()->envi(); + GCLock lock; + + aCT.clear(); + aCT.push_back(DCT("int_le_reif__POST", t_VIIVI, RIT_Reif, CT_Comparison, CMPT_LE, VT_Int, + int_le_reif__POST)); + aCT.push_back(DCT("int_ge_reif__POST", t_VIIVI, RIT_Reif, CT_Comparison, CMPT_GE, VT_Int, + int_ge_reif__POST)); + aCT.push_back(DCT("int_eq_reif__POST", t_VIIVI, RIT_Reif, CT_Comparison, CMPT_EQ, VT_Int, + int_eq_reif__POST)); + aCT.push_back( + DCT("int_ne__POST", t_VII, RIT_Static, CT_Comparison, CMPT_NE, VT_Int, int_ne__POST)); + + aCT.push_back(DCT("float_le_reif__POST", t_VFFVIF, RIT_Reif, CT_Comparison, CMPT_LE, VT_Float, + float_le_reif__POST)); + aCT.push_back(DCT("float_ge_reif__POST", t_VFFVIF, RIT_Reif, CT_Comparison, CMPT_GE, VT_Float, + float_ge_reif__POST)); + aCT.push_back(DCT("aux_float_lt_zero_iff_1__POST", t_VFVIF, RIT_Reif, CT_Comparison, CMPT_LT, + VT_Float, aux_float_lt_zero_iff_1__POST)); + aCT.push_back(DCT("float_eq_reif__POST", t_VFFVIF, RIT_Reif, CT_Comparison, CMPT_EQ, VT_Float, + float_eq_reif__POST)); + aCT.push_back(DCT("float_ne__POST", t_VFFF, RIT_Static, CT_Comparison, CMPT_NE, VT_Float, + float_ne__POST)); + + aCT.push_back(DCT("aux_float_eq_zero_if_1__POST", t_VFVIVF, RIT_Halfreif, CT_Comparison, + CMPT_EQ_0, VT_Float, aux_float_eq_zero_if_1__POST)); + aCT.push_back(DCT("aux_int_le_zero_if_1__POST", t_VIVI, RIT_Halfreif, CT_Comparison, CMPT_LE_0, + VT_Int, aux_int_le_zero_if_1__POST)); + aCT.push_back(DCT("aux_float_le_zero_if_1__POST", t_VFVIVF, RIT_Halfreif, CT_Comparison, + CMPT_LE_0, VT_Float, aux_float_le_zero_if_1__POST)); + aCT.push_back(DCT("aux_float_lt_zero_if_1__POST", t_VFVIVFF, RIT_Halfreif, CT_Comparison, + CMPT_LT_0, VT_Float, aux_float_lt_zero_if_1__POST)); + + aCT.push_back(DCT("equality_encoding__POST", t_VIAVI, RIT_Static, CT_Encode, CMPT_None, VT_Int, + equality_encoding__POST)); + aCT.push_back( + DCT("set_in__POST", t_VISI, RIT_Static, CT_SetIn, CMPT_None, VT_Int, set_in__POST)); + aCT.push_back(DCT("set_in_reif__POST", t_VISIVI, RIT_Reif, CT_SetIn, CMPT_None, VT_Int, + set_in_reif__POST)); + /// Registering all declared & compatible __POST constraints + /// (First, cleanup FunctionIs' payload: -- ! doing now) + for (int i = 0; i < aCT.size(); ++i) { + FunctionI* fi = env.model->matchFn(env, ASTString(aCT[i].sFuncName), aCT[i].aParams, false); + if (fi) { + mCallTypes[fi] = aCT.data() + i; + aCT[i].pfi = fi; + // fi->pPayload = (void*)this; + // std::cerr << " FOund declaration: " << aCT[i].sFuncName << std::endl; + } else { + aCT[i].pfi = 0; + DBGOUT_MIPD(" MIssing declaration: " << aCT[i].sFuncName); + return false; + } + } + return true; + } + + /// Registering all __POST calls' domain-constrained variables + void register__POSTvariables() { + EnvI& env = getEnv()->envi(); + GCLock lock; + Model& mFlat = *getEnv()->flat(); + // First, cleanup VarDecls' payload which stores index in vVarDescr + for (VarDeclIterator ivd = mFlat.begin_vardecls(); ivd != mFlat.end_vardecls(); ++ivd) { + ivd->e()->payload(-1); + } + // Now add variables with non-contiguous domain + for (VarDeclIterator ivd = mFlat.begin_vardecls(); ivd != mFlat.end_vardecls(); ++ivd) { + VarDecl* vd0 = ivd->e(); + bool fNonCtg = 0; + if (vd0->type().isint()) { // currently only for int vars TODO + if (Expression* eDom = vd0->ti()->domain()) { + IntSetVal* dom = eval_intset(env, eDom); + fNonCtg = (dom->size() > 1); + } + } + if (fNonCtg) { + DBGOUT_MIPD(" Variable " << vd0->id()->str() << ": non-contiguous domain " + << (*(vd0->ti()->domain()))); + if (vd0->payload() == -1) { // ! yet visited + vd0->payload(static_cast(vVarDescr.size())); + vVarDescr.push_back(VarDescr(vd0, vd0->type().isint())); // can use /prmTypes/ as well + if (vd0->e()) checkInitExpr(vd0); + } else { + DBGOUT_MIPD__(" (already touched)"); + } + ++MIPD__stats[N_POSTs__domain]; + ++MIPD__stats[N_POSTs__all]; + } + } + // Iterate thru original __POST constraints to mark constrained vars: + for (ConstraintIterator ic = mFlat.begin_constraints(); ic != mFlat.end_constraints(); ++ic) { + if (ic->removed()) continue; + if (Call* c = ic->e()->dyn_cast()) { + auto ipct = mCallTypes.find(c->decl()); + if (ipct != mCallTypes.end()) { + // No ! here because might be deleted immediately in later versions. + // ic->remove(); // mark removed at once + MZN_MIPD__assert_hard(c->n_args() > 1); + ++MIPD__stats[N_POSTs__all]; + VarDecl* vd0 = expr2VarDecl(c->arg(0)); + if (0 == vd0) { + DBGOUT_MIPD__(" Call " << *c << ": 1st arg not a VarDecl, removing if eq_encoding..."); + /// Only allow literals as main argument for equality_encoding + if (equality_encoding__POST == + ipct->first) // was MZN_MIPD__assert_hard before MZN 2017 + ic->remove(); + continue; // ignore this call + } + DBGOUT_MIPD__(" Call " << c->id().str() << " uses variable " << vd0->id()->str()); + if (vd0->payload() == -1) { // ! yet visited + vd0->payload(static_cast(vVarDescr.size())); + vVarDescr.push_back(VarDescr(vd0, vd0->type().isint())); // can use /prmTypes/ as well + // bounds/domains later for each involved var TODO + if (vd0->e()) checkInitExpr(vd0); + } else { + DBGOUT_MIPD__(" (already touched)"); + } + DBGOUT_MIPD(""); + if (equality_encoding__POST == c->decl()) { + MZN_MIPD__assert_hard(!vVarDescr[vd0->payload()].pEqEncoding); + vVarDescr[vd0->payload()].pEqEncoding = &*ic; + DBGOUT_MIPD(" Variable " << vd0->id()->str() << " has eq_encode."); + } // + if has aux_ constraints? + else + vVarDescr[vd0->payload()].aCalls.push_back(&*ic); + } + } + } + MIPD__stats[N_POSTs__varsDirect] = static_cast(vVarDescr.size()); + } + + // Should only be called on a newly added variable + // OR when looking thru all non-touched vars + /// Checks init expr of a variable + /// Return true IFF new connection + /// The bool param requires RHS to be POST-touched + // Guido: can! be recursive in FZN + bool checkInitExpr(VarDecl* vd, bool fCheckArg = false) { + MZN_MIPD__assert_hard(vd->e()); + if (!vd->type().isint() && !vd->type().isfloat()) return false; + if (!fCheckArg) MZN_MIPD__assert_hard(vd->payload() >= 0); + if (Id* id = vd->e()->dyn_cast()) { + // const int f1 = ( vd->payload()>=0 ); + // const int f2 = ( id->decl()->payload()>=0 ); + if (!fCheckArg || (id->decl()->payload() >= 0)) { + DBGOUT_MIPD__(" Checking init expr "); + DBGOUT_MIPD_SELF(debugprint(vd)); + LinEq2Vars led; + // FAILS: + // led.vd = { vd, expr2VarDecl(id->decl()->e()) }; + led.vd = {{vd, expr2VarDecl(vd->e())}}; + led.coefs = {{1.0, -1.0}}; + led.rhs = 0.0; + put2VarsConnection(led, false); + ++MIPD__stats[N_POSTs__initexpr1id]; + if (id->decl()->e()) // no initexpr for initexpr FAILS on cc-base.mzn + checkInitExpr(id->decl()); + return true; // in any case + } + } else if (Call* c = vd->e()->dyn_cast()) { + if (lin_exp_int == c->decl() || lin_exp_float == c->decl()) { + // std::cerr << " !E call " << std::flush; + // debugprint(c); + MZN_MIPD__assert_hard(c->n_args() == 3); + // ArrayLit* al = c->args()[1]->dyn_cast(); + ArrayLit* al = follow_id(c->arg(1))->cast(); + MZN_MIPD__assert_hard(al); + MZN_MIPD__assert_hard(al->size() >= 1); + if (al->size() == 1) { // 1-term scalar product in the rhs + LinEq2Vars led; + led.vd = {{vd, expr2VarDecl((*al)[0])}}; + // const int f1 = ( vd->payload()>=0 ); + // const int f2 = ( led.vd[1]->payload()>=0 ); + if (!fCheckArg || (led.vd[1]->payload() >= 0)) { + // Can use a!her map here: + // if ( sCallLinEq2.end() != sCallLinEq2.find(c) ) + // continue; + // sCallLinEq2.insert(c); // memorize this call + DBGOUT_MIPD__(" REG 1-LINEXP "); + DBGOUT_MIPD_SELF(debugprint(vd)); + std::array coef0; + expr2Array(c->arg(0), coef0); + led.coefs = {{-1.0, coef0[0]}}; + led.rhs = -expr2Const(c->arg(2)); // MINUS + put2VarsConnection(led, false); + ++MIPD__stats[N_POSTs__initexpr1linexp]; + if (led.vd[1]->e()) // no initexpr for initexpr FAILS TODO + checkInitExpr(led.vd[1]); + return true; // in any case + } + } else if (true) { // check larger views always. OK? TODO + // if ( vd->payload()>=0 ) { // larger views + // TODO should be here? + // std::cerr << " LE_" << al->v().size() << ' ' << std::flush; + DBGOUT_MIPD(" REG N-LINEXP "); + DBGOUT_MIPD_SELF(debugprint(vd)); + // Checking all but adding only touched defined vars? + return findOrAddDefining(vd->id(), c); + } + } + } + return false; + } + + /// Build var cliques (i.e. of var pairs viewing each other) + void constructVarViewCliques() { + // std::cerr << " Model: " << std::endl; + // debugprint(getEnv()->flat()); + + // TAgenda agenda(vVarDescr.size()), agendaNext; + // for ( int i=0; i(vVarDescr.size()); + } + + void propagateViews(bool& fChanges) { + // EnvI& env = getEnv()->envi(); + GCLock lock; + + // Iterate thru original 2-variable equalities to mark views: + Model& mFlat = *getEnv()->flat(); + + DBGOUT_MIPD(" CHECK ALL INITEXPR if they access a touched variable:"); + for (VarDeclIterator ivd = mFlat.begin_vardecls(); ivd != mFlat.end_vardecls(); ++ivd) { + if (ivd->removed()) continue; + if (ivd->e()->e() && ivd->e()->payload() < 0 // untouched + && (ivd->e()->type().isint() || ivd->e()->type().isfloat())) // scalars + if (checkInitExpr(ivd->e(), true)) fChanges = true; + } + + DBGOUT_MIPD(" CHECK ALL CONSTRAINTS for 2-var equations:"); + for (ConstraintIterator ic = mFlat.begin_constraints(); ic != mFlat.end_constraints(); ++ic) { + // std::cerr << " SEE constraint: " << " "; + // debugprint(&*ic); + // debugprint(c->decl()); + if (ic->removed()) continue; + if (Call* c = ic->e()->dyn_cast()) { + const bool fIntLinEq = int_lin_eq == c->decl(); + const bool fFloatLinEq = float_lin_eq == c->decl(); + if (fIntLinEq || fFloatLinEq) { + // std::cerr << " !E call " << std::flush; + // debugprint(c); + MZN_MIPD__assert_hard(c->n_args() == 3); + ArrayLit* al = follow_id(c->arg(1))->cast(); + MZN_MIPD__assert_hard(al); + if (al->size() == 2) { // 2-term eqn + LinEq2Vars led; + expr2DeclArray(c->arg(1), led.vd); + // At least 1 touched var: + if (led.vd[0]->payload() >= 0 || led.vd[1]->payload() >= 0) { + if (sCallLinEq2.end() != sCallLinEq2.find(c)) continue; + sCallLinEq2.insert(c); // memorize this call + DBGOUT_MIPD(" REG 2-call "); + DBGOUT_MIPD_SELF(debugprint(c)); + led.rhs = expr2Const(c->arg(2)); + expr2Array(c->arg(0), led.coefs); + MZN_MIPD__assert_hard(2 == led.coefs.size()); + fChanges = true; + put2VarsConnection(led); + ++MIPD__stats[fIntLinEq ? N_POSTs__eq2intlineq : N_POSTs__eq2floatlineq]; + } + } else if (al->size() == 1) { + static int nn = 0; + if (++nn <= 7) { + std::cerr << " MIPD: LIN_EQ with 1 variable::: " << std::flush; + std::cerr << (*c) << std::endl; + } + } else { // larger eqns + // TODO should be here? + auto eVD = getAnnotation(c->ann(), constants().ann.defines_var); + if (eVD) { + if (sCallLinEqN.end() != sCallLinEqN.find(c)) continue; + sCallLinEqN.insert(c); // memorize this call + DBGOUT_MIPD(" REG N-call "); + DBGOUT_MIPD_SELF(debugprint(c)); + Call* pC = eVD->dyn_cast(); + MZN_MIPD__assert_hard(pC); + MZN_MIPD__assert_hard(pC->n_args()); + // Checking all but adding only touched defined vars? Seems too long. + VarDecl* vd = expr2VarDecl(pC->arg(0)); + if (vd->payload() >= 0) // only if touched + if (findOrAddDefining(pC->arg(0), c)) fChanges = true; + } + } + } else + // const bool fI2F = (int2float==c->decl()); + // const bool fIVR = (constants().var_redef==c->decl()); + // if ( fI2F || fIVR ) { + if (int2float == c->decl() || constants().var_redef == c->decl()) { + // std::cerr << " !E call " << std::flush; + // debugprint(c); + MZN_MIPD__assert_hard(c->n_args() == 2); + LinEq2Vars led; + // led.vd.resize(2); + led.vd[0] = expr2VarDecl(c->arg(0)); + led.vd[1] = expr2VarDecl(c->arg(1)); + // At least 1 touched var: + if (led.vd[0]->payload() >= 0 || led.vd[1]->payload() >= 0) { + if (sCallInt2Float.end() != sCallInt2Float.find(c)) continue; + sCallInt2Float.insert(c); // memorize this call + DBGOUT_MIPD(" REG call "); + DBGOUT_MIPD_SELF(debugprint(c)); + led.rhs = 0.0; + led.coefs = {{1.0, -1.0}}; + fChanges = true; + put2VarsConnection(led); + ++MIPD__stats[int2float == c->decl() ? N_POSTs__int2float : N_POSTs__internalvarredef]; + } + } + } + } + } + + /// This vector stores the linear part of a general view + /// x = + rhs + typedef std::vector > TLinExpLin; + /// This struct has data describing the rest of a general view + struct NViewData { + VarDecl* pVarDefined = 0; + double coef0 = 1.0; + double rhs; + }; + typedef std::map NViewMap; + NViewMap mNViews; + + /// compare to an existing defining linexp, || just add it to the map + /// adds only touched defined vars + /// return true iff new linear connection + // linexp: z = a^T x+b + // _lin_eq: a^T x == b + bool findOrAddDefining(Expression* exp, Call* pC) { + Id* pId = exp->dyn_cast(); + MZN_MIPD__assert_hard(pId); + VarDecl* vd = pId->decl(); + MZN_MIPD__assert_hard(vd); + MZN_MIPD__assert_hard(pC->n_args() == 3); + + TLinExpLin rhsLin; + NViewData nVRest; + nVRest.pVarDefined = vd; + nVRest.rhs = expr2Const(pC->arg(2)); + + std::vector vars; + expr2DeclArray(pC->arg(1), vars); + std::vector coefs; + expr2Array(pC->arg(0), coefs); + MZN_MIPD__assert_hard(vars.size() == coefs.size()); + + int nVD = 0; + for (int i = 0; i < vars.size(); ++i) { + // MZN_MIPD__assert_hard( 0.0!=std::fabs + if (vd == vars[i]) { // the defined var + nVRest.coef0 = -coefs[i]; + nVRest.rhs = -nVRest.rhs; + ++nVD; + } else + rhsLin.push_back(std::make_pair(vars[i], coefs[i])); + } + MZN_MIPD__assert_hard(1 >= nVD); + std::sort(rhsLin.begin(), rhsLin.end()); + + // Divide the equation by the 1st coef + const double coef1 = rhsLin.begin()->second; + MZN_MIPD__assert_hard(0.0 != std::fabs(coef1)); + nVRest.coef0 /= coef1; + nVRest.rhs /= coef1; + for (auto& rhsL : rhsLin) rhsL.second /= coef1; + + auto it = mNViews.find(rhsLin); + if (mNViews.end() != it) { + LinEq2Vars leq; + leq.vd = {{nVRest.pVarDefined, it->second.pVarDefined}}; + leq.coefs = {{nVRest.coef0, -it->second.coef0}}; // +, - + leq.rhs = nVRest.rhs - it->second.rhs; + put2VarsConnection(leq, false); + ++MIPD__stats[nVD ? N_POSTs__eqNlineq : N_POSTs__initexprN]; + return true; + } else { + if (vd->payload() >= 0) // only touched + mNViews[rhsLin] = nVRest; + } + + return false; + } + + void propagateImplViews(bool& fChanges) { + // EnvI& env = getEnv()->envi(); + GCLock lock; + + // TODO + } + + /// Could be better to mark the calls instead: + std::unordered_set sCallLinEq2, sCallInt2Float, sCallLinEqN; + + class TClique : public std::vector { // need more info? + public: + /// This function takes the 1st variable && relates all to it + /// Return false if contrad / disconnected graph + // bool findRelations0() { + // return true; + // } + }; + typedef std::vector TCLiqueList; + TCLiqueList aCliques; + + /// register a 2-variable lin eq + /// add it to the var clique, joining the participants' cliques if needed + void put2VarsConnection(LinEq2Vars& led, bool fCheckinitExpr = true) { + MZN_MIPD__assert_hard(led.coefs.size() == led.vd.size()); + MZN_MIPD__assert_hard(led.vd.size() == 2); + DBGOUT_MIPD__(" Register 2-var connection: " << led); + // register if new variables + // std::vector fHaveClq(led.vd.size(), false); + int nCliqueAvailable = -1; + for (auto vd : led.vd) { + if (vd->payload() < 0) { // ! yet visited + vd->payload(static_cast(vVarDescr.size())); + vVarDescr.push_back(VarDescr(vd, vd->type().isint())); // can use /prmTypes/ as well + if (fCheckinitExpr && vd->e()) checkInitExpr(vd); + } else { + int nMaybeClq = vVarDescr[vd->payload()].nClique; + if (nMaybeClq >= 0) nCliqueAvailable = nMaybeClq; + // MZN_MIPD__assert_hard( nCliqueAvailable>=0 ); + // fHaveClq[i] = true; + } + } + if (nCliqueAvailable < 0) { // no clique found + nCliqueAvailable = static_cast(aCliques.size()); + aCliques.resize(aCliques.size() + 1); + } + DBGOUT_MIPD(" ...adding to clique " << nCliqueAvailable << " of size " + << aCliques[nCliqueAvailable].size()); + TClique& clqNew = aCliques[nCliqueAvailable]; + clqNew.push_back(led); + for (auto vd : led.vd) { // merging cliques + int& nMaybeClq = vVarDescr[vd->payload()].nClique; + if (nMaybeClq >= 0 && nMaybeClq != nCliqueAvailable) { + TClique& clqOld = aCliques[nMaybeClq]; + MZN_MIPD__assert_hard(clqOld.size()); + for (auto& eq2 : clqOld) { + for (auto vd : eq2.vd) { // point all the variables to the new clique + vVarDescr[vd->payload()].nClique = nCliqueAvailable; + } + } + clqNew.insert(clqNew.end(), clqOld.begin(), clqOld.end()); + clqOld.clear(); // Can use C++11 move TODO + DBGOUT_MIPD(" +++ Joining cliques"); + } + nMaybeClq = nCliqueAvailable; // Could mark as 'unused' TODO + } + } + + /// Finds a clique variable to which all domain constr are related + class TCliqueSorter { + MIPD& mipd; + const int iVarStart; // this is the first var to which all others are related + public: + // VarDecl* varRef0=0; // this is the first var to which all others are related + VarDecl* varRef1 = 0; // this is the 2nd main reference. + // it is a var with eq_encode, || + // an (integer if any) variable with the least rel. factor + bool fRef1HasEqEncode = false; + /// This map stores the relations y = ax+b of all the clique's vars to y + typedef std::unordered_map > TMapVars; + TMapVars mRef0, mRef1; // to the main var 0, 1 + + class TMatrixVars : public std::unordered_map { + public: + /// Check existing connection + template + bool checkExistingArc(IVarDecl begV, double A, double B, bool fReportRepeat = true) { + auto it1 = this->find(*begV); + if (this->end() != it1) { + auto it2 = it1->second.find(*(begV + 1)); + if (it1->second.end() != it2) { + MZN_MIPD__assert_hard(std::fabs(it2->second.first - A) < + 1e-6 * std::max(std::fabs(it2->second.first), std::fabs(A))); + MZN_MIPD__assert_hard(std::fabs(it2->second.second - B) < + 1e-6 * std::max(std::fabs(it2->second.second), std::fabs(B)) + + 1e-6); + MZN_MIPD__assert_hard(std::fabs(A) != 0.0); + MZN_MIPD__assert_soft(!fVerbose || std::fabs(A) > 1e-12, + " Very small coef: " << (*begV)->id()->str() << " = " << A + << " * " << (*(begV + 1))->id()->str() + << " + " << B); + if (fReportRepeat) + MZN_MIPD__assert_soft(!fVerbose, "LinEqGraph: eqn between " + << (*begV)->id()->str() << " && " + << (*(begV + 1))->id()->str() + << " is repeated. "); + return true; + } + } + return false; + } + }; + + class LinEqGraph : public TMatrixVars { + public: + static double dCoefMin, dCoefMax; + /// Stores the arc (x1, x2) as x1 = a*x2 + b + /// so that a constraint on x2, say x2<=c <-> f, + /// is equivalent to one for x1: x1 <=/>= a*c+b <-> f + //// ( the other way involves division: + //// so that a constraint on x1, say x1<=c <-> f, + //// can easily be converted into one for x2 as a*x2 <= c-b <-> f + //// <=> x2 (care for sign) (c-b)/a <-> f ) + + template + void addArc(ICoef begC, IVarDecl begV, double rhs) { + MZN_MIPD__assert_soft(!fVerbose || std::fabs(*begC) >= 1e-10, + " Vars " << (*begV)->id()->str() << " to " + << (*(begV + 1))->id()->str() << ": coef=" << (*begC)); + // Transform Ax+By=C into x = -B/Ay+C/A + const double negBA = -(*(begC + 1)) / (*begC); + const double CA = rhs / (*begC); + checkExistingArc(begV, negBA, CA); + (*this)[*begV][*(begV + 1)] = std::make_pair(negBA, CA); + const double dCoefAbs = std::fabs(negBA); + if (dCoefAbs < dCoefMin) dCoefMin = dCoefAbs; + if (dCoefAbs > dCoefMax) dCoefMax = dCoefAbs; + } + void addEdge(const LinEq2Vars& led) { + addArc(led.coefs.begin(), led.vd.begin(), led.rhs); + addArc(led.coefs.rbegin(), led.vd.rbegin(), led.rhs); + } + /// Propagate linear relations from the given variable + void propagate(iterator itStart, TMapVars& mWhereStore) { + MZN_MIPD__assert_hard(this->end() != itStart); + TMatrixVars mTemp; + mTemp[itStart->first] = itStart->second; // init with existing + DBGOUT_MIPD("Propagation started from " << itStart->first->id()->str() << " having " + << itStart->second.size() << " connections"); + propagate2(itStart, itStart, std::make_pair(1.0, 0.0), mTemp); + mWhereStore = mTemp.begin()->second; + MZN_MIPD__assert_hard_msg( + mWhereStore.size() == this->size() - 1, + "Variable " << (*(mTemp.begin()->first)) + << " should be connected to all others in the clique, but " + << "|edges|==" << mWhereStore.size() << ", |all nodes|==" << this->size()); + } + /// Propagate linear relations from it1 via it2 + void propagate2(iterator itSrc, iterator itVia, std::pair rel, + TMatrixVars& mWhereStore) { + for (auto itDst = itVia->second.begin(); itDst != itVia->second.end(); ++itDst) { + // Transform x1=A1x2+B1, x2=A2x3+B2 into x1=A1A2x3+A1B2+B1 + if (itDst->first == itSrc->first) continue; + const double A1A2 = rel.first * itDst->second.first; + const double A1B2plusB1 = rel.first * itDst->second.second + rel.second; + bool fDive = true; + if (itSrc != itVia) { + PVarDecl vd[2] = {itSrc->first, itDst->first}; + if (!mWhereStore.checkExistingArc(vd, A1A2, A1B2plusB1, false)) { + mWhereStore[vd[0]][vd[1]] = std::make_pair(A1A2, A1B2plusB1); + DBGOUT_MIPD(" PROPAGATING: " << vd[0]->id()->str() << " = " << A1A2 << " * " + << vd[1]->id()->str() << " + " << A1B2plusB1); + } else + fDive = false; + } + if (fDive) { + auto itDST = this->find(itDst->first); + MZN_MIPD__assert_hard(this->end() != itDST); + propagate2(itSrc, itDST, std::make_pair(A1A2, A1B2plusB1), mWhereStore); + } + } + } + }; + LinEqGraph leg; + + TCliqueSorter(MIPD* pm, int iv) : mipd(*pm), iVarStart(iv) {} + void doRelate() { + MZN_MIPD__assert_hard(mipd.vVarDescr[iVarStart].nClique >= 0); + const TClique& clq = mipd.aCliques[mipd.vVarDescr[iVarStart].nClique]; + for (auto& eq2 : clq) { + leg.addEdge(eq2); + } + DBGOUT_MIPD(" Clique " << mipd.vVarDescr[iVarStart].nClique << ": " << leg.size() + << " variables, " << clq.size() << " connections."); + for (auto it1 = leg.begin(); it1 != leg.end(); ++it1) + mipd.vVarDescr[it1->first->payload()].fDomainConstrProcessed = true; + + // Propagate the 1st var's relations: + leg.propagate(leg.begin(), mRef0); + + // Find a best main variable according to: + // 1. isInt 2. hasEqEncode 3. abs linFactor to ref0 + varRef1 = leg.begin()->first; + std::array aCrit = { + {(double)mipd.vVarDescr[varRef1->payload()].fInt, + static_cast(mipd.vVarDescr[varRef1->payload()].pEqEncoding != NULL), 1.0}}; + for (auto it2 = mRef0.begin(); it2 != mRef0.end(); ++it2) { + VarDescr& vard = mipd.vVarDescr[it2->first->payload()]; + std::array aCrit1 = {{(double)vard.fInt, + static_cast(vard.pEqEncoding != NULL), + std::fabs(it2->second.first)}}; + if (aCrit1 > aCrit) { + varRef1 = it2->first; + aCrit = aCrit1; + } + } + leg.propagate(leg.find(varRef1), mRef1); + } + }; // class TCliqueSorter + + /// Build a domain decomposition for a clique + /// a clique can consist of just 1 var without a clique object + class DomainDecomp { + public: + MIPD& mipd; + const int iVarStart; + TCliqueSorter cls; + SetOfIntvReal sDomain; + + DomainDecomp(MIPD* pm, int iv) : mipd(*pm), iVarStart(iv), cls(pm, iv) { + sDomain.insert(IntvReal()); // the decomposed domain. Init to +-inf + } + void doProcess() { + // Choose the main variable && relate all others to it + const int nClique = mipd.vVarDescr[iVarStart].nClique; + if (nClique >= 0) { + cls.doRelate(); + } else + cls.varRef1 = mipd.vVarDescr[iVarStart].vd; + // Adding itself: + cls.mRef1[cls.varRef1] = std::make_pair(1.0, 0.0); + + int iVarRef1 = cls.varRef1->payload(); + MZN_MIPD__assert_hard(nClique == mipd.vVarDescr[iVarRef1].nClique); + cls.fRef1HasEqEncode = (mipd.vVarDescr[iVarRef1].pEqEncoding != NULL); + + // First, construct the domain decomposition in any case + // projectVariableConstr( cls.varRef1, std::make_pair(1.0, 0.0) ); + // if ( nClique >= 0 ) { + for (auto& iRef1 : cls.mRef1) { + projectVariableConstr(iRef1.first, iRef1.second); + } + + DBGOUT_MIPD("Clique " << nClique << ": main ref var " << cls.varRef1->id()->str() + << ", domain dec: " << sDomain); + + MZN_MIPD__assert_for_feas(sDomain.size(), "Clique " << nClique << ": main ref var " + << cls.varRef1->id()->str() + << ", domain dec: " << sDomain); + + MZN_MIPD__assert_hard(sDomain.checkFiniteBounds()); + MZN_MIPD__assert_hard(sDomain.checkDisjunctStrict()); + + makeRangeDomains(); + + // Then, use equality_encoding if available + if (cls.fRef1HasEqEncode) { + syncWithEqEncoding(); + syncOtherEqEncodings(); + } else { // ! cls.fRef1HasEqEncode + if (sDomain.size() >= 2) { // need to simplify stuff otherwise + considerDenseEncoding(); + createDomainFlags(); + } + } + implement__POSTs(); + + // Statistics + if (sDomain.size() < MIPD__stats[N_POSTs__NSubintvMin]) + MIPD__stats[N_POSTs__NSubintvMin] = static_cast(sDomain.size()); + MIPD__stats[N_POSTs__NSubintvSum] += sDomain.size(); + if (sDomain.size() > MIPD__stats[N_POSTs__NSubintvMax]) + MIPD__stats[N_POSTs__NSubintvMax] = static_cast(sDomain.size()); + for (auto& intv : sDomain) { + const auto nSubSize = intv.right - intv.left; + if (nSubSize < MIPD__stats[N_POSTs__SubSizeMin]) + MIPD__stats[N_POSTs__SubSizeMin] = nSubSize; + MIPD__stats[N_POSTs__SubSizeSum] += nSubSize; + if (nSubSize > MIPD__stats[N_POSTs__SubSizeMax]) + MIPD__stats[N_POSTs__SubSizeMax] = nSubSize; + } + if (cls.fRef1HasEqEncode) ++MIPD__stats[N_POSTs__cliquesWithEqEncode]; + } + + /// Project the domain-related constraints of a variable into the clique + /// Deltas should be scaled but to a minimum of the target's discr + /// COmparison sense changes on negated vars + void projectVariableConstr(VarDecl* vd, std::pair eq1) { + DBGOUT_MIPD__(" MIPD: projecting variable "); + DBGOUT_MIPD_SELF(debugprint(vd)); + // Always check if domain becomes empty? TODO + const double A = eq1.first; // vd = A*arg + B. conversion + const double B = eq1.second; + // process domain info + double lb = B, ub = A + B; // projected bounds for bool + if (vd->ti()->domain()) { + if (vd->type().isint() || vd->type().isfloat()) { // INT VAR OR FLOAT VAR + SetOfIntvReal sD1; + convertIntSet(vd->ti()->domain(), sD1, cls.varRef1, A, B); + sDomain.intersect(sD1); + DBGOUT_MIPD(" Clique domain after proj of the init. domain " + << sD1 << " of " << (vd->type().isint() ? "varint" : "varfloat") << A << " * " + << vd->id()->str() << " + " << B << ": " << sDomain); + auto bnds = sD1.getBounds(); + lb = bnds.left; + ub = bnds.right; + } else { + MZN_MIPD__assert_for_feas(0, "Variable " << vd->id()->str() << " of type " + << vd->type().toString(mipd.__env->envi()) + << " has a domain."); + } + // /// Deleting var domain: + // vd->ti()->domain( NULL ); + } else { + if (NULL == vd->ti()->domain() && !vd->type().isbool()) { + lb = IntvReal::infMinus(); + ub = IntvReal::infPlus(); + } + } + auto bnds = sDomain.getBounds(); // can change TODO + // process calls. Can use the constr type info. + auto& aCalls = mipd.vVarDescr[vd->payload()].aCalls; + for (Item* pItem : aCalls) { + ConstraintI* pCI = pItem->dyn_cast(); + MZN_MIPD__assert_hard(pCI); + Call* pCall = pCI->e()->dyn_cast(); + MZN_MIPD__assert_hard(pCall); + DBGOUT_MIPD__("PROPAG CALL "); + DBGOUT_MIPD_SELF(debugprint(pCall)); + // check the bounds for bool in reifs? TODO + auto ipct = mipd.mCallTypes.find(pCall->decl()); + MZN_MIPD__assert_hard(mipd.mCallTypes.end() != ipct); + const DCT& dct = *ipct->second; + int nCmpType_ADAPTED = dct.nCmpType; + if (A < 0.0) { // negative factor + if (std::abs(nCmpType_ADAPTED) >= 4) // inequality + nCmpType_ADAPTED = -nCmpType_ADAPTED; + } + switch (dct.nConstrType) { + case CT_SetIn: { + SetOfIntvReal SS; + convertIntSet(pCall->arg(1), SS, cls.varRef1, A, B); + if (RIT_Static == dct.nReifType) { + sDomain.intersect(SS); + ++MIPD__stats[N_POSTs__setIn]; + } else { + sDomain.cutDeltas(SS, std::max(1.0, std::fabs(A))); // deltas to scale + ++MIPD__stats[N_POSTs__setInReif]; + } + } break; + case CT_Comparison: + if (RIT_Reif == dct.nReifType) { + const double rhs = (mipd.aux_float_lt_zero_iff_1__POST == pCall->decl()) + ? B /* + A*0.0, relating to 0 */ + // The 2nd argument is constant: + : A * mipd.expr2Const(pCall->arg(1)) + B; + const double rhsUp = rndUpIfInt(cls.varRef1, rhs); + const double rhsDown = rndDownIfInt(cls.varRef1, rhs); + const double rhsRnd = rndIfInt(cls.varRef1, rhs); + /// Strictly, for delta we should finish domain reductions first... TODO? + const double delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 3); + switch (nCmpType_ADAPTED) { + case CMPT_LE: + sDomain.cutDeltas(IntvReal::infMinus(), rhsDown, delta); + break; + case CMPT_GE: + sDomain.cutDeltas(rhsUp, IntvReal::infPlus(), delta); + break; + case CMPT_LT_0: + sDomain.cutDeltas(IntvReal::infMinus(), rhsDown - delta, delta); + break; + case CMPT_GT_0: + sDomain.cutDeltas(rhsUp + delta, IntvReal::infPlus(), delta); + break; + case CMPT_EQ: + if (!(cls.varRef1->type().isint() && // skip if int target var + std::fabs(rhs - rhsRnd) > INT_EPS)) // && fract value + sDomain.cutDeltas(rhsRnd, rhsRnd, delta); + break; + default: + MZN_MIPD__assert_hard_msg(0, " No other reified cmp type "); + } + ++MIPD__stats[(vd->ti()->type().isint()) ? N_POSTs__intCmpReif + : N_POSTs__floatCmpReif]; + } else if (RIT_Static == dct.nReifType) { + // _ne, later maybe static ineq TODO + MZN_MIPD__assert_hard(CMPT_NE == dct.nCmpType); + const double rhs = A * mipd.expr2Const(pCall->arg(1)) + B; + const double rhsRnd = rndIfInt(cls.varRef1, rhs); + bool fSkipNE = (cls.varRef1->type().isint() && std::fabs(rhs - rhsRnd) > INT_EPS); + if (!fSkipNE) { + const double delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 2); + sDomain.cutOut({rhsRnd - delta, rhsRnd + delta}); + } + ++MIPD__stats[(vd->ti()->type().isint()) ? N_POSTs__intNE : N_POSTs__floatNE]; + } else { // aux_ relate to 0.0 + // But we don't modify domain splitting for them currently + ++MIPD__stats[(vd->ti()->type().isint()) ? N_POSTs__intAux : N_POSTs__floatAux]; + MZN_MIPD__assert_hard(RIT_Halfreif == dct.nReifType); + // const double rhs = B; // + A*0 + // const double delta = vd->type().isint() ? 1.0 : 1e-5; // + // TODO : eps + } + break; + case CT_Encode: + // See if any further constraints here? TODO + ++MIPD__stats[N_POSTs__eq_encode]; + break; + default: + MZN_MIPD__assert_hard_msg(0, "Unknown constraint type"); + } + } + DBGOUT_MIPD(" Clique domain after proj of " << A << " * " << vd->id()->str() << " + " << B + << ": " << sDomain); + } + + static double rndIfInt(VarDecl* vdTarget, double v) { + return vdTarget->type().isint() ? std::round(v) : v; + } + static double rndIfBothInt(VarDecl* vdTarget, double v) { + if (!vdTarget->type().isint()) return v; + const double vRnd = std::round(v); + return (fabs(v - vRnd) < INT_EPS) ? vRnd : v; + } + static double rndUpIfInt(VarDecl* vdTarget, double v) { + return vdTarget->type().isint() ? std::ceil(v - INT_EPS) : v; + } + static double rndDownIfInt(VarDecl* vdTarget, double v) { + return vdTarget->type().isint() ? std::floor(v + INT_EPS) : v; + } + + void makeRangeDomains() { + auto bnds = sDomain.getBounds(); + for (auto& iRef1 : cls.mRef1) { + VarDecl* vd = iRef1.first; + // projecting the bounds back: + double lb0 = (bnds.left - iRef1.second.second) / iRef1.second.first; + double ub0 = (bnds.right - iRef1.second.second) / iRef1.second.first; + if (lb0 > ub0) { + MZN_MIPD__assert_hard(iRef1.second.first < 0.0); + std::swap(lb0, ub0); + } + if (vd->type().isint()) { + lb0 = rndUpIfInt(vd, lb0); + ub0 = rndDownIfInt(vd, ub0); + } + setVarDomain(vd, lb0, ub0); + } + } + + /// tightens element bounds in the existing eq_encoding of varRef1 + /// necessary because if one exists, int_ne is not translated into it + /// Can also back-check from there? TODO + /// And further checks TODO + void syncWithEqEncoding() { + std::vector pp; + auto bnds = sDomain.getBounds(); + const long long iMin = mipd.expr2ExprArray( + mipd.vVarDescr[cls.varRef1->payload()].pEqEncoding->e()->dyn_cast()->arg(1), pp); + MZN_MIPD__assert_hard(pp.size() >= bnds.right - bnds.left + 1); + MZN_MIPD__assert_hard(iMin <= bnds.left); + long long vEE = iMin; + DBGOUT_MIPD__( + " SYNC EQ_ENCODE( " + << (*cls.varRef1) << ", bitflags: " + << *(mipd.vVarDescr[cls.varRef1->payload()].pEqEncoding->e()->dyn_cast()->arg(1)) + << " ): SETTING 0 FLAGS FOR VALUES: "); + for (auto& intv : sDomain) { + for (; vEE < intv.left; ++vEE) { + if (vEE >= static_cast(iMin + pp.size())) return; + if (pp[vEE - iMin]->isa()) + if (pp[vEE - iMin]->dyn_cast()->decl()->type().isvar()) { + DBGOUT_MIPD__(vEE << ", "); + setVarDomain(pp[vEE - iMin]->dyn_cast()->decl(), 0.0, 0.0); + } + } + vEE = static_cast(intv.right + 1); + } + for (; vEE < static_cast(iMin + pp.size()); ++vEE) { + if (pp[vEE - iMin]->isa()) + if (pp[vEE - iMin]->dyn_cast()->decl()->type().isvar()) { + DBGOUT_MIPD__(vEE << ", "); + setVarDomain(pp[vEE - iMin]->dyn_cast()->decl(), 0.0, 0.0); + } + } + DBGOUT_MIPD(""); + } + + /// sync varRef1's eq_encoding with those of other variables + void syncOtherEqEncodings() { + // TODO This could be in the var projection? No, need the final domain + } + + /// Depending on params, + /// create an equality encoding for an integer variable + /// TODO What if a float's domain is discrete? + void considerDenseEncoding() { + if (cls.varRef1->id()->type().isint()) { + if (sDomain.max_interval() <= mipd.nMaxIntv2Bits || + sDomain.card_int() <= mipd.dMaxNValueDensity * sDomain.size()) { + sDomain.split2Bits(); + ++MIPD__stats[N_POSTs__clEEEnforced]; + } + } + } + + /// if ! eq_encoding, creates a flag for each subinterval in the domain + /// && constrains sum(flags)==1 + void createDomainFlags() { + std::vector vVars(sDomain.size()); // flags for each subinterval + std::vector vIntvLB(sDomain.size() + 1), vIntvUB__(sDomain.size() + 1); + int i = 0; + double dMaxIntv = -1.0; + for (auto& intv : sDomain) { + intv.varFlag = addIntVar(0.0, 1.0); + vVars[i] = intv.varFlag->id(); + vIntvLB[i] = intv.left; + vIntvUB__[i] = -intv.right; + dMaxIntv = std::max(dMaxIntv, intv.right - intv.left); + ++i; + } + // Sum of flags == 1 + std::vector ones(sDomain.size(), 1.0); + addLinConstr(ones, vVars, CMPT_EQ, 1.0); + // Domain decomp + vVars.push_back(cls.varRef1->id()); + vIntvLB[i] = -1.0; // var1 >= sum(LBi*flagi) + /// STRICT equality encoding if small intervals + if (dMaxIntv > 1e-6) { // EPS = param? TODO + vIntvUB__[i] = 1.0; // var1 <= sum(UBi*flagi) + addLinConstr(vIntvLB, vVars, CMPT_LE, 0.0); + addLinConstr(vIntvUB__, vVars, CMPT_LE, 0.0); + } else { + ++MIPD__stats[N_POSTs__clEEFound]; + addLinConstr(vIntvLB, vVars, CMPT_EQ, 0.0); + } + } + + /// deletes them as well + void implement__POSTs() { + auto bnds = sDomain.getBounds(); + for (auto& iRef1 : cls.mRef1) { + // DBGOUT_MIPD__( " MIPD: implementing constraints of variable " ); + // DBGOUT_MIPD_SELF( debugprint(vd) ); + VarDecl* vd = iRef1.first; + auto eq1 = iRef1.second; + const double A = eq1.first; // vd = A*arg + B. conversion + const double B = eq1.second; + // process calls. Can use the constr type info. + auto& aCalls = mipd.vVarDescr[vd->payload()].aCalls; + for (Item* pItem : aCalls) { + ConstraintI* pCI = pItem->dyn_cast(); + MZN_MIPD__assert_hard(pCI); + Call* pCall = pCI->e()->dyn_cast(); + MZN_MIPD__assert_hard(pCall); + DBGOUT_MIPD__("IMPL CALL "); + DBGOUT_MIPD_SELF(debugprint(pCall)); + // check the bounds for bool in reifs? TODO + auto ipct = mipd.mCallTypes.find(pCall->decl()); + MZN_MIPD__assert_hard(mipd.mCallTypes.end() != ipct); + const DCT& dct = *ipct->second; + int nCmpType_ADAPTED = dct.nCmpType; + if (A < 0.0) { // negative factor + if (std::abs(nCmpType_ADAPTED) >= 4) // inequality + nCmpType_ADAPTED = -nCmpType_ADAPTED; + } + switch (dct.nConstrType) { + case CT_SetIn: + if (RIT_Reif == dct.nReifType) { + SetOfIntvReal SS; + convertIntSet(pCall->arg(1), SS, cls.varRef1, A, B); + relateReifFlag(pCall->arg(2), SS); + } + break; + case CT_Comparison: + if (RIT_Reif == dct.nReifType) { + const double rhs = (mipd.aux_float_lt_zero_iff_1__POST == pCall->decl()) + ? B /* + A*0.0, relating to 0 */ + // The 2nd argument is constant: + : A * mipd.expr2Const(pCall->arg(1)) + B; + const double rhsUp = rndUpIfInt(cls.varRef1, rhs); + const double rhsDown = rndDownIfInt(cls.varRef1, rhs); + const double rhsRnd = rndIfBothInt( + cls.varRef1, rhs); // if the ref var is int, need to round almost-int values + const double delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 3); + switch (nCmpType_ADAPTED) { + case CMPT_LE: + relateReifFlag(pCall->arg(2), {{IntvReal::infMinus(), rhsDown}}); + break; + case CMPT_GE: + relateReifFlag(pCall->arg(2), {{rhsUp, IntvReal::infPlus()}}); + break; + case CMPT_LT_0: + relateReifFlag(pCall->arg(1), {{IntvReal::infMinus(), rhsDown - delta}}); + break; + case CMPT_GT_0: + relateReifFlag(pCall->arg(1), {{rhsUp + delta, IntvReal::infPlus()}}); + break; + case CMPT_EQ: + relateReifFlag(pCall->arg(2), {{rhsRnd, rhsRnd}}); + break; // ... but if the value is sign. fractional for an int var, the flag is + // set=0 + default: + break; + } + } else if (RIT_Static == dct.nReifType) { + // !hing here for NE + MZN_MIPD__assert_hard(CMPT_NE == nCmpType_ADAPTED); + } else { // aux_ relate to 0.0 + // But we don't modify domain splitting for them currently + MZN_MIPD__assert_hard(RIT_Halfreif == dct.nReifType); + double rhs = B; // + A*0 + const double rhsUp = rndUpIfInt(cls.varRef1, rhs); + const double rhsDown = rndDownIfInt(cls.varRef1, rhs); + const double rhsRnd = rndIfInt(cls.varRef1, rhs); + double delta = 0.0; + if (mipd.aux_float_lt_zero_if_1__POST == pCall->decl()) // only float && lt + delta = computeDelta(cls.varRef1, vd, bnds, A, pCall, 3); + if (nCmpType_ADAPTED < 0) delta = -delta; + if (cls.varRef1->type().isint() && CMPT_EQ_0 != nCmpType_ADAPTED) { + if (nCmpType_ADAPTED < 0) + rhs = rhsDown; + else + rhs = rhsUp; + } else { + rhs += delta; + } + // Now we need rhs ! to be in the inner of the domain + bool fUseDD = true; + if (!cls.fRef1HasEqEncode) { + switch (nCmpType_ADAPTED) { + case CMPT_EQ_0: { + auto itLB = sDomain.lower_bound(rhsRnd); + fUseDD = (itLB->left == rhsRnd && itLB->right == rhsRnd); // exactly + } break; + case CMPT_LT_0: + case CMPT_LE_0: { + auto itUB = sDomain.upper_bound(rhsUp); + bool fInner = false; + if (sDomain.begin() != itUB) { + --itUB; + if (itUB->right > rhs) fInner = true; + } + fUseDD = !fInner; + } break; + case CMPT_GT_0: + case CMPT_GE_0: { + auto itLB = sDomain.lower_bound(rhsDown); + bool fInner = false; + if (sDomain.begin() != itLB) { + --itLB; + if (itLB->right >= rhs) fInner = true; + } + fUseDD = !fInner; + } break; + default: + MZN_MIPD__assert_hard_msg(0, "Unknown halfreif cmp type"); + } + } + if (fUseDD) { // use sDomain + if (CMPT_EQ_0 == nCmpType_ADAPTED) { + relateReifFlag(pCall->arg(1), {{rhsRnd, rhsRnd}}, RIT_Halfreif); + } else if (nCmpType_ADAPTED < 0) { + relateReifFlag(pCall->arg(1), {{IntvReal::infMinus(), rhsDown}}, RIT_Halfreif); + } else { + relateReifFlag(pCall->arg(1), {{rhsUp, IntvReal::infPlus()}}, RIT_Halfreif); + } + } else { // use big-M + DBGOUT_MIPD(" AUX BY BIG-Ms: "); + const bool fLE = (CMPT_EQ_0 == nCmpType_ADAPTED || 0 > nCmpType_ADAPTED); + const bool fGE = (CMPT_EQ_0 == nCmpType_ADAPTED || 0 < nCmpType_ADAPTED); + // Take integer || float indicator version, depending on the constrained var: + const int nIdxInd = // (VT_Int==dct.nVarType) ? + // No: vd->ti()->type().isint() ? 1 : 2; + cls.varRef1->ti()->type().isint() + ? 1 + : 2; // need the type of the variable to be constr + MZN_MIPD__assert_hard(static_cast(nIdxInd) < pCall->n_args()); + Expression* pInd = pCall->arg(nIdxInd); + if (fLE && rhs < bnds.right) { + if (rhs >= bnds.left) { + std::vector coefs = {1.0, bnds.right - rhs}; + // Use the float version of indicator: + std::vector vars = {cls.varRef1->id(), pInd}; + addLinConstr(coefs, vars, CMPT_LE, bnds.right); + } else + setVarDomain(mipd.expr2VarDecl(pInd), 0.0, 0.0); + } + if (fGE && rhs > bnds.left) { + if (rhs <= bnds.right) { + std::vector coefs = {-1.0, rhs - bnds.left}; + std::vector vars = {cls.varRef1->id(), pInd}; + addLinConstr(coefs, vars, CMPT_LE, -bnds.left); + } else + setVarDomain(mipd.expr2VarDecl(pInd), 0.0, 0.0); + } + } + } + break; + case CT_Encode: + // See if any further constraints here? TODO + break; + default: + MZN_MIPD__assert_hard_msg(0, "Unknown constraint type"); + } + pItem->remove(); // removing the call + } + // removing the eq_encoding call + if (mipd.vVarDescr[vd->payload()].pEqEncoding) + mipd.vVarDescr[vd->payload()].pEqEncoding->remove(); + } + } + + /// sets varFlag = || <= sum( intv.varFlag : SS ) + void relateReifFlag(Expression* expFlag, const SetOfIntvReal& SS, EnumReifType nRT = RIT_Reif) { + MZN_MIPD__assert_hard(RIT_Reif == nRT || RIT_Halfreif == nRT); + // MZN_MIPD__assert_hard( sDomain.size()>=2 ); + VarDecl* varFlag = mipd.expr2VarDecl(expFlag); + std::vector vIntvFlags; + if (cls.fRef1HasEqEncode) { // use eq_encoding + MZN_MIPD__assert_hard(varFlag->type().isint()); + std::vector pp; + auto bnds = sDomain.getBounds(); + const long long iMin = mipd.expr2ExprArray( + mipd.vVarDescr[cls.varRef1->payload()].pEqEncoding->e()->dyn_cast()->arg(1), pp); + MZN_MIPD__assert_hard(pp.size() >= bnds.right - bnds.left + 1); + MZN_MIPD__assert_hard(iMin <= bnds.left); + for (auto& intv : SS) { + for (long long vv = (long long)std::max(double(iMin), ceil(intv.left)); + vv <= (long long)std::min(double(iMin) + pp.size() - 1, floor(intv.right)); ++vv) { + vIntvFlags.push_back(pp[vv - iMin]); + } + } + } else { + MZN_MIPD__assert_hard(varFlag->type().isint()); + for (auto& intv : SS) { + auto it1 = sDomain.lower_bound(intv.left); + auto it2 = sDomain.upper_bound(intv.right); + auto it11 = it1; + // Check that we are looking ! into a subinterval: + if (sDomain.begin() != it11) { + --it11; + MZN_MIPD__assert_hard(it11->right < intv.left); + } + auto it12 = it2; + if (sDomain.begin() != it12) { + --it12; + MZN_MIPD__assert_hard_msg(it12->right <= intv.right, + " relateReifFlag for " << intv << " in " << sDomain); + } + for (it12 = it1; it12 != it2; ++it12) { + if (it12->varFlag) + vIntvFlags.push_back(it12->varFlag->id()); + else { + MZN_MIPD__assert_hard(1 == sDomain.size()); + vIntvFlags.push_back(IntLit::a(1)); // just a constant then + } + } + } + } + if (vIntvFlags.size()) { + // Could find out if reif is true -- TODO && see above for 1 subinterval + std::vector onesm(vIntvFlags.size(), -1.0); + onesm.push_back(1.0); + vIntvFlags.push_back(varFlag->id()); + EnumCmpType nCmpType = (RIT_Reif == nRT) ? CMPT_EQ : CMPT_LE; + addLinConstr(onesm, vIntvFlags, nCmpType, 0.0); + } else { // the reif is false + setVarDomain(varFlag, 0.0, 0.0); + } + } + + void setVarDomain(VarDecl* vd, double lb, double ub) { + // need to check if the new range is in the previous bounds... TODO + if (vd->type().isfloat()) { + // if ( 0.0==lb && 0.0==ub ) { + BinOp* newDom = + new BinOp(Location().introduce(), FloatLit::a(lb), BOT_DOTDOT, FloatLit::a(ub)); + vd->ti()->domain(newDom); + DBGOUT_MIPD(" NULL OUT: " << vd->id()->str()); + // } + } else if (vd->type().isint() || vd->type().isbool()) { + SetLit* newDom = new SetLit( + Location().introduce(), + IntSetVal::a(static_cast(lb), static_cast(ub))); + // TypeInst* nti = copy(mipd.getEnv()->envi(),varFlag->ti())->cast(); + // nti->domain(newDom); + vd->ti()->domain(newDom); + } else + MZN_MIPD__assert_hard_msg(0, "Unknown var type "); + } + + VarDecl* addIntVar(double LB, double UB) { + // GCLock lock; + // Cache them? Only location can be different TODO + SetLit* newDom = + new SetLit(Location().introduce(), + IntSetVal::a(static_cast(LB), static_cast(UB))); + TypeInst* ti = new TypeInst(Location().introduce(), Type::varint(), newDom); + VarDecl* newVar = new VarDecl(Location().introduce(), ti, mipd.getEnv()->envi().genId()); + newVar->flat(newVar); + mipd.getEnv()->envi().flat_addItem(new VarDeclI(Location().introduce(), newVar)); + return newVar; + } + + void addLinConstr(std::vector& coefs, std::vector& vars, + EnumCmpType nCmpType, double rhs) { + std::vector args(3); + MZN_MIPD__assert_hard(vars.size() >= 2); + for (auto v : vars) { + MZN_MIPD__assert_hard(&v); + // throw std::string("addLinConstr: &var=NULL"); + MZN_MIPD__assert_hard_msg(v->isa() || v->isa() || v->isa(), + " expression at " << (&v) << " eid = " << v->eid() + << " while E_INTLIT=" << Expression::E_INTLIT); + // throw std::string("addLinConstr: only id's as variables allowed"); + } + MZN_MIPD__assert_hard(coefs.size() == vars.size()); + MZN_MIPD__assert_hard(CMPT_EQ == nCmpType || CMPT_LE == nCmpType); + DBGOUT_MIPD_SELF( // LinEq leq; leq.coefs=coefs; leq.vd=vars; leq.rhs=rhs; + DBGOUT_MIPD__(" ADDING " << (CMPT_EQ == nCmpType ? "LIN_EQ" : "LIN_LE") << ": [ "); + for (auto c + : coefs) DBGOUT_MIPD__(c << ','); + DBGOUT_MIPD__(" ] * [ "); for (auto v + : vars) { + MZN_MIPD__assert_hard(!v->isa()); + if (v->isa()) DBGOUT_MIPD__(v->dyn_cast()->str() << ','); + // else if ( v->isa() ) + // MZN_MIPD__assert_hard ("addLinConstr: only id's as variables allowed"); + else + DBGOUT_MIPD__(mipd.expr2Const(v) << ','); + } DBGOUT_MIPD(" ] " << (CMPT_EQ == nCmpType ? "== " : "<= ") << rhs);); + std::vector nc_c; + std::vector nx; + bool fFloat = false; + for (auto v : vars) { + if (!v->type().isint()) { + fFloat = true; + break; + } + } + auto sName = constants().ids.float_.lin_eq; // "int_lin_eq"; + FunctionI* fDecl = mipd.float_lin_eq; + if (fFloat) { // MZN_MIPD__assert_hard all vars of same type TODO + for (int i = 0; i < vars.size(); ++i) + if (fabs(coefs[i]) > 1e-8) /// Only add terms with non-0 coefs. TODO Eps=param + { + nc_c.push_back(FloatLit::a(coefs[i])); + if (vars[i]->type().isint()) { + std::vector i2f_args(1); + i2f_args[0] = vars[i]; + Call* i2f = new Call(Location().introduce(), constants().ids.int2float, i2f_args); + i2f->type(Type::varfloat()); + i2f->decl(mipd.getEnv()->model()->matchFn(mipd.getEnv()->envi(), i2f, false)); + EE ret = flat_exp(mipd.getEnv()->envi(), Ctx(), i2f, NULL, constants().var_true); + nx.push_back(ret.r()); + } else { + nx.push_back(vars[i]); // ->id(); once passing a general expression + } + } + args[2] = FloatLit::a(rhs); + args[2]->type(Type::parfloat(0)); + args[0] = new ArrayLit(Location().introduce(), nc_c); + args[0]->type(Type::parfloat(1)); + args[1] = new ArrayLit(Location().introduce(), nx); + args[1]->type(Type::varfloat(1)); + if (CMPT_LE == nCmpType) { + sName = constants().ids.float_.lin_le; // "float_lin_le"; + fDecl = mipd.float_lin_le; + } + } else { + for (int i = 0; i < vars.size(); ++i) + if (fabs(coefs[i]) > 1e-8) /// Only add terms with non-0 coefs. TODO Eps=param + { + nc_c.push_back(IntLit::a(static_cast(coefs[i]))); + nx.push_back(vars[i]); //->id(); + } + args[2] = IntLit::a(static_cast(rhs)); + args[2]->type(Type::parint(0)); + args[0] = new ArrayLit(Location().introduce(), nc_c); + args[0]->type(Type::parint(1)); + args[1] = new ArrayLit(Location().introduce(), nx); + args[1]->type(Type::varint(1)); + if (CMPT_LE == nCmpType) { + sName = constants().ids.int_.lin_le; // "int_lin_le"; + fDecl = mipd.int_lin_le; + } else { + sName = constants().ids.int_.lin_eq; // "int_lin_eq"; + fDecl = mipd.int_lin_eq; + } + } + if (mipd.getEnv()->envi().cse_map_end() != mipd.getEnv()->envi().cse_map_find(args[0])) { + DBGOUT_MIPD__(" Found expr "); + DBGOUT_MIPD_SELF(debugprint(args[0])); + } + auto nc = new Call(Location().introduce(), ASTString(sName), args); + nc->type(Type::varbool()); + nc->decl(fDecl); + mipd.getEnv()->envi().flat_addItem(new ConstraintI(Location().introduce(), nc)); + } + + /// domain / reif set of one variable into that for a!her + void convertIntSet(Expression* e, SetOfIntvReal& s, VarDecl* varTarget, double A, double B) { + MZN_MIPD__assert_hard(A != 0.0); + if (e->type().isintset()) { + IntSetVal* S = eval_intset(mipd.getEnv()->envi(), e); + IntSetRanges domr(S); + for (; domr(); ++domr) { // * A + B + IntVal mmin = domr.min(); + IntVal mmax = domr.max(); + if (A < 0.0) std::swap(mmin, mmax); + s.insert(IntvReal( // * A + B + mmin.isFinite() ? rndUpIfInt(varTarget, (mmin.toInt() * A + B)) + : IntvReal::infMinus(), + mmax.isFinite() ? rndDownIfInt(varTarget, (mmax.toInt() * A + B)) + : IntvReal::infPlus())); + } + } else { + assert(e->type().isfloatset()); + FloatSetVal* S = eval_floatset(mipd.getEnv()->envi(), e); + FloatSetRanges domr(S); + for (; domr(); ++domr) { // * A + B + FloatVal mmin = domr.min(); + FloatVal mmax = domr.max(); + if (A < 0.0) std::swap(mmin, mmax); + s.insert(IntvReal( // * A + B + mmin.isFinite() ? rndUpIfInt(varTarget, (mmin.toDouble() * A + B)) + : IntvReal::infMinus(), + mmax.isFinite() ? rndDownIfInt(varTarget, (mmax.toDouble() * A + B)) + : IntvReal::infPlus())); + } + } + } + + /// compute the delta for float strict ineq + double computeDelta(VarDecl* var, VarDecl* varOrig, IntvReal bnds, double A, Call* pCall, + int nArg) { + double delta = varOrig->type().isfloat() + ? mipd.expr2Const(pCall->arg(nArg)) + // * ( bnds.right-bnds.left ) ABANDONED 12.4.18 due to #207 + : std::fabs(A); // delta should be scaled as well + if (var->type().isint()) // the projected-onto variable + delta = std::max(1.0, delta); + return delta; + } + }; // class DomainDecomp + + /// Vars without explicit clique still need a decomposition. + /// Have !iced all __POSTs, set_in's && eq_encode's to it BEFORE + /// In each clique, relate all vars to one chosen + /// Find all "smallest rel. factor" variables, integer && with eq_encode if avail + /// Re-relate all vars to it + /// Refer all __POSTs && dom() to it + /// build domain decomposition + /// Implement all domain constraints, incl. possible corresp, of eq_encode's + /// + /// REMARKS. + /// ! impose effects of integrality scaling (e.g., int v = int k/3) + /// BUT when using k's eq_encode? + /// And when subdividing into intervals + bool decomposeDomains() { + // for (int iClq=0; iClqremove(); + if (vVar.pEqEncoding) vVar.pEqEncoding->remove(); + } + return fRetTrue; + } + + VarDecl* expr2VarDecl(Expression* arg) { + // The requirement to have actual variable objects + // might be a limitation if more optimizations are done before... + // Might need to flexibilize this TODO + // MZN_MIPD__assert_hard_msg( ! arg->dyn_cast(), + // "Expression " << *arg << " is an IntLit!" ); + // MZN_MIPD__assert_hard( ! arg->dyn_cast() ); + // MZN_MIPD__assert_hard( ! arg->dyn_cast() ); + Id* id = arg->dyn_cast(); + // MZN_MIPD__assert_hard(id); + if (0 == id) return 0; // the call using this should be ignored? + VarDecl* vd = id->decl(); + MZN_MIPD__assert_hard(vd); + return vd; + } + + /// Fills the vector of vardecls && returns the least index of the array + template + long long expr2DeclArray(Expression* arg, Array& aVD) { + ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); + checkOrResize(aVD, al->size()); + for (unsigned int i = 0; i < al->size(); i++) aVD[i] = expr2VarDecl((*al)[i]); + return al->min(0); + } + + /// Fills the vector of expressions && returns the least index of the array + template + long long expr2ExprArray(Expression* arg, Array& aVD) { + ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); + checkOrResize(aVD, al->size()); + for (unsigned int i = 0; i < al->size(); i++) aVD[i] = ((*al)[i]); + return al->min(0); + } + + double expr2Const(Expression* arg) { + if (IntLit* il = arg->dyn_cast()) { + return (static_cast(il->v().toInt())); + } else if (FloatLit* fl = arg->dyn_cast()) { + return (fl->v().toDouble()); + } else if (BoolLit* bl = arg->dyn_cast()) { + return (bl->v()); + } else { + MZN_MIPD__assert_hard_msg(0, + "unexpected expression instead of an int/float/bool literal: eid=" + << arg->eid() << " while E_INTLIT=" << Expression::E_INTLIT); + } + return 0.0; + } + + template + void checkOrResize(Container& cnt, size_t sz) { + cnt.resize(sz); + } + + template + void checkOrResize(std::array& cnt, size_t sz) { + MZN_MIPD__assert_hard(cnt.size() == sz); + } + + template + void expr2Array(Expression* arg, Array& vals) { + ArrayLit* al = eval_array_lit(getEnv()->envi(), arg); + // if ( typeid(typename Array::pointer) == typeid(typename Array::iterator) ) // fixed + // array + // MZN_MIPD__assert_hard( vals.size() == al->v().size() ); + // else + // vals.resize( al->v().size() ); + checkOrResize(vals, al->size()); + for (unsigned int i = 0; i < al->size(); i++) { + vals[i] = expr2Const((*al)[i]); + } + } + + void printStats(std::ostream& os) { + // if ( aCliques.empty() ) + // return; + if (vVarDescr.empty()) return; + int nc = 0; + for (auto& cl : aCliques) + if (cl.size()) ++nc; + for (auto& var : vVarDescr) + if (0 > var.nClique) + ++nc; // 1-var cliques + // os << "N cliques " << aCliques.size() << " total, " + // << nc << " final" << std::endl; + MZN_MIPD__assert_hard(nc); + MIPD__stats[N_POSTs__eqNmapsize] = static_cast(mNViews.size()); + double nSubintvAve = MIPD__stats[N_POSTs__NSubintvSum] / nc; + MZN_MIPD__assert_hard(MIPD__stats[N_POSTs__NSubintvSum]); + double dSubSizeAve = MIPD__stats[N_POSTs__SubSizeSum] / MIPD__stats[N_POSTs__NSubintvSum]; + os << MIPD__stats[N_POSTs__all] + << " POSTs" +#ifdef __MZN__MIPDOMAINS__PRINTMORESTATS + " [ "; + for (int i = N_POSTs__intCmpReif; i <= N_POSTs__floatAux; ++i) os << MIPD__stats[i] << ','; + os << " ], LINEQ [ "; + for (int i = N_POSTs__eq2intlineq; i <= N_POSTs__eqNmapsize; ++i) os << MIPD__stats[i] << ','; + os << " ]" +#endif + ", " + << MIPD__stats[N_POSTs__varsDirect] << " / " << MIPD__stats[N_POSTs__varsInvolved] + << " vars, " << nc << " cliques, " << MIPD__stats[N_POSTs__NSubintvMin] << " / " + << nSubintvAve << " / " << MIPD__stats[N_POSTs__NSubintvMax] << " NSubIntv m/a/m, " + << MIPD__stats[N_POSTs__SubSizeMin] << " / " << dSubSizeAve << " / " + << MIPD__stats[N_POSTs__SubSizeMax] << " SubIntvSize m/a/m, " + << MIPD__stats[N_POSTs__cliquesWithEqEncode] << "+" << MIPD__stats[N_POSTs__clEEEnforced] + << "(" << MIPD__stats[N_POSTs__clEEFound] << ")" + << " clq eq_encoded "; + // << std::flush + if (TCliqueSorter::LinEqGraph::dCoefMax > 1.0) + os << TCliqueSorter::LinEqGraph::dCoefMin << "--" << TCliqueSorter::LinEqGraph::dCoefMax + << " abs coefs"; + os << " ... "; + } + +}; // class MIPD + +template +template +void SetOfIntervals::intersect(const SetOfIntervals& s2) { + if (s2.empty()) { + this->clear(); + return; + } + this->cutOut(Interval(Interval::infMinus(), (N)s2.begin()->left)); + for (auto is2 = s2.begin(); is2 != s2.end(); ++is2) { + auto is2next = is2; + ++is2next; + this->cutOut( + Interval(is2->right, s2.end() == is2next ? Interval::infPlus() : (N)is2next->left)); + } +} +template +template +void SetOfIntervals::cutDeltas(const SetOfIntervals& s2, N1 delta) { + if (this->empty()) return; + // What if distance < delta? TODO + for (auto is2 : s2) { + if (is2.left > Interval::infMinus()) this->cutOut(Interval(is2.left - delta, is2.left)); + if (is2.right < Interval::infPlus()) + this->cutOut(Interval(is2.right, is2.right + delta)); + } +} +template +void SetOfIntervals::cutOut(const Interval& intv) { + DBGOUT_MIPD__("Cutting " << intv << " from " << (*this)); + if (this->empty()) return; + iterator it1 = (Interval::infMinus() == intv.left) + ? this->lower_bound(Interval(intv.left, intv.right)) + : this->upper_bound(Interval(intv.left, intv.right)); + auto it2Del1 = it1; // from which to delete + if (this->begin() != it1) { + --it1; + const N it1l = it1->left; + MZN_MIPD__assert_hard(it1l <= intv.left); + if (it1->right > intv.left) { // split it + it2Del1 = split(it1, intv.left).second; + // it1->right = intv.left; READ-ONLY + // this->erase(it1); + // it1 = this->end(); + // auto iR = this->insert( Interval( it1l, intv.left ) ); + // MZN_MIPD__assert_hard( iR.second ); + } + } + DBGOUT_MIPD__("; after split 1: " << (*this)); + // Processing the right end: + auto it2 = this->lower_bound(Interval(intv.right, intv.right + 1)); + auto it2Del2 = it2; + if (this->begin() != it2) { + --it2; + MZN_MIPD__assert_hard(it2->left < intv.right); + const N it2r = it2->right; + if ((Interval::infPlus() == intv.right) ? (it2r > intv.right) + : (it2r >= intv.right)) { // >=: split it + // it2Del2 = split( it2, intv.right ).second; + const bool fEEE = (it2Del1 == it2); + this->erase(it2); + it2 = this->end(); + it2Del2 = this->insert(Interval(intv.right, it2r)); + if (fEEE) it2Del1 = it2Del2; + } + } + DBGOUT_MIPD__("; after split 2: " << (*this)); + DBGOUT_MIPD__("; cutting out: " << SetOfIntervals(it2Del1, it2Del2)); +#ifdef MZN_DBG_CHECK_ITER_CUTOUT + { + auto it = this->begin(); + int nO = 0; + do { + if (it == it2Del1) { + MZN_MIPD__assert_hard(!nO); + ++nO; + } + if (it == it2Del2) { + MZN_MIPD__assert_hard(1 == nO); + ++nO; + } + if (this->end() == it) break; + ++it; + } while (1); + MZN_MIPD__assert_hard(2 == nO); + } +#endif + this->erase(it2Del1, it2Del2); + DBGOUT_MIPD(" ... gives " << (*this)); +} +template +typename SetOfIntervals::SplitResult SetOfIntervals::split(iterator& it, N pos) { + MZN_MIPD__assert_hard(pos >= it->left); + MZN_MIPD__assert_hard(pos <= it->right); + Interval intvOld = *it; + this->erase(it); + iterator it_01 = this->insert(Interval(intvOld.left, pos)); + iterator it_02 = this->insert(Interval(pos, intvOld.right)); + it = this->end(); + return std::make_pair(it_01, it_02); +} +template +Interval SetOfIntervals::getBounds() const { + if (this->empty()) return Interval(Interval::infPlus(), Interval::infMinus()); + iterator it2 = this->end(); + --it2; + return Interval(this->begin()->left, it2->right); +} +template +bool SetOfIntervals::checkFiniteBounds() { + if (this->empty()) return false; + auto bnds = getBounds(); + return bnds.left > Interval::infMinus() && bnds.right < Interval::infPlus(); +} +template +bool SetOfIntervals::checkDisjunctStrict() { + for (auto it = this->begin(); it != this->end(); ++it) { + if (it->left > it->right) return false; + if (this->begin() != it) { + auto it_1 = it; + --it_1; + if (it_1->right >= it->left) return false; + } + } + return true; +} +/// Assumes integer interval bounds +template +int SetOfIntervals::card_int() const { + int nn = 0; + for (auto it = this->begin(); it != this->end(); ++it) { + ++nn; + nn += int(round(it->right - it->left)); + } + return nn; +} +template +N SetOfIntervals::max_interval() const { + N ll = -1; + for (auto it = this->begin(); it != this->end(); ++it) { + ll = std::max(ll, it->right - it->left); + } + return ll; +} +/// Assumes integer interval bounds +template +void SetOfIntervals::split2Bits() { + Base bsNew; + for (auto it = this->begin(); it != this->end(); ++it) { + for (int v = static_cast(round(it->left)); v <= round(it->right); ++v) + bsNew.insert(Intv(v, v)); + } + *(Base*)this = std::move(bsNew); +} + +bool MIPD::fVerbose = false; + +void MIPdomains(Env& env, bool fVerbose, int nmi, double dmd) { + MIPD mipd(&env, fVerbose, nmi, dmd); + if (!mipd.MIPdomains()) { + GCLock lock; + env.envi().fail(); + } +} + +double MIPD::TCliqueSorter::LinEqGraph::dCoefMin = +1e100; +double MIPD::TCliqueSorter::LinEqGraph::dCoefMax = -1e100; + +} // namespace MiniZinc diff --git a/lib/algorithms/min_cut.cpp b/lib/algorithms/min_cut.cpp new file mode 100644 index 0000000..3d47871 --- /dev/null +++ b/lib/algorithms/min_cut.cpp @@ -0,0 +1,72 @@ +#include +#include + +#include + +#ifdef COMPILE_BOOST_MINCUT +#include +#include +#include +#include +#include +#include +#endif + +void Algorithms::MinCut::solve() { +#ifndef COMPILE_BOOST_MINCUT + throw std::runtime_error( + "MIP/circuit: Subtour Elimination Constraints: MinCut::solve not compiled. " + "Compile with COMPILE_BOOST_MINCUT (needs boost), or use -D nSECcuts=0 for flattening."); +#else + typedef std::pair edge_t; + + // A graphic of the min-cut is available at + // + using namespace std; + + typedef boost::adjacency_list > + undirected_graph; + typedef boost::property_map::type weight_map_type; + typedef boost::property_traits::value_type weight_type; + + // define the 16 edges of the graph. {3, 4} means an undirected edge between vertices 3 and 4. + // edge_t edges[] = {{3, 4}, {3, 6}, {3, 5}, {0, 4}, {0, 1}, {0, 6}, {0, 7}, + // {0, 5}, {0, 2}, {4, 1}, {1, 6}, {1, 5}, {6, 7}, {7, 5}, {5, 2}, {3, 4}}; + std::vector edges_array; // = edges; + // std::vector< weight_type > ws_array; + + edges_array.reserve(edges.size()); + for (const auto& edg : edges) + edges_array.push_back(std::pair(edg.first, edg.second)); + + // for each of the 16 edges, define the associated edge weight. ws[i] is the weight for the edge + // that is described by edges[i]. + // weight_type ws[] = + // {0.2, 3.1, 1.3, 3.7, 1.5, 2.6, 6.44, 1.26, 8.77, 1.29, 1.95, 80.74, 2.23, 1.94, 1.23, 4.957}; + + // construct the graph object. 8 is the number of vertices, which are numbered from 0 + // through 7, and 16 is the number of edges. + undirected_graph g(edges_array.data(), edges_array.data() + edges_array.size(), weights.data(), + nNodes, edges_array.size()); + + // define a property map, `parities`, that will store a boolean value for each vertex. + // Vertices that have the same parity after `stoer_wagner_min_cut` runs are on the same side of + // the min-cut. + BOOST_AUTO(parities00, + boost::make_one_bit_color_map(num_vertices(g), get(boost::vertex_index, g))); + + // run the Stoer-Wagner algorithm to obtain the min-cut weight. `parities` is also filled in. + wMinCut = + boost::stoer_wagner_min_cut(g, get(boost::edge_weight, g), boost::parity_map(parities00)); + + assert(nNodes == num_vertices(g)); + + // parities = parities00; + parities.clear(); + parities.reserve(nNodes); + for (size_t i = 0; i < num_vertices(g); ++i) { + parities.push_back(get(parities00, i)); + } +#endif +} diff --git a/lib/ast.cpp b/lib/ast.cpp new file mode 100644 index 0000000..657310b --- /dev/null +++ b/lib/ast.cpp @@ -0,0 +1,1821 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +Location::LocVec* Location::LocVec::a(const ASTString& filename, unsigned int first_line, + unsigned int first_column, unsigned int last_line, + unsigned int last_column) { + static const unsigned int pointerBits = sizeof(IntLit*) * 8; + if (pointerBits <= 32) { + if (first_line < (1 << 8) && last_line - first_line < (1 << 7) && first_column < (1 << 6) && + last_column < (1 << 7)) { + long long int combined = first_line; + combined |= (last_line - first_line) << 8; + combined |= (first_column) << (8 + 7); + combined |= (last_column) << (8 + 7 + 6); + LocVec* v = static_cast(alloc(2)); + new (v) LocVec(filename, combined); + return v; + } + } else if (pointerBits >= 64) { + if (first_line < (1 << 20) && last_line - first_line < (1 << 20) && first_column < (1 << 10) && + last_column < (1 << 10)) { + long long int combined = first_line; + combined |= (static_cast(last_line - first_line)) << 20; + combined |= (static_cast(first_column)) << (20 + 20); + combined |= (static_cast(last_column)) << (20 + 20 + 10); + LocVec* v = static_cast(alloc(2)); + new (v) LocVec(filename, combined); + return v; + } + } + + LocVec* v = static_cast(alloc(5)); + new (v) LocVec(filename, first_line, first_column, last_line, last_column); + return v; +} + +Location::LocVec::LocVec(const ASTString& filename, IntVal combined) : ASTVec(2) { + *(_data + 0) = filename.aststr(); + *(_data + 1) = IntLit::a(combined); +} + +Location::LocVec::LocVec(const ASTString& filename, unsigned int fl, unsigned int first_column, + unsigned int last_line, unsigned int last_column) + : ASTVec(5) { + *(_data + 0) = filename.aststr(); + *(_data + 1) = IntLit::a(fl); + *(_data + 2) = IntLit::a(last_line); + *(_data + 3) = IntLit::a(first_column); + *(_data + 4) = IntLit::a(last_column); +} + +Location Location::nonalloc; + +Type Type::unboxedint = Type::parint(); +Type Type::unboxedfloat = Type::parfloat(); + +Annotation Annotation::empty; + +std::string Location::toString(void) const { + std::ostringstream oss; + oss << filename() << ":" << first_line() << "." << first_column(); + return oss.str(); +} + +void Location::mark(void) const { + if (lv()) lv()->mark(); +} + +Location Location::introduce() const { + Location l = *this; + if (l._loc_info.lv) { + l._loc_info.t |= 1; + } + return l; +} + +void Expression::addAnnotation(Expression* ann) { + if (!isUnboxedVal()) _ann.add(ann); +} +void Expression::addAnnotations(std::vector ann) { + if (!isUnboxedVal()) + for (unsigned int i = 0; i < ann.size(); i++) + if (ann[i]) _ann.add(ann[i]); +} + +#define pushstack(e) \ + do { \ + if (e != NULL) { \ + stack.push_back(e); \ + } \ + } while (0) +#define pushall(v) \ + do { \ + v.mark(); \ + for (unsigned int i = 0; i < v.size(); i++) \ + if (v[i] != NULL) { \ + stack.push_back(v[i]); \ + } \ + } while (0) +#define pushann(a) \ + do { \ + for (ExpressionSetIter it = a.begin(); it != a.end(); ++it) { \ + pushstack(*it); \ + } \ + } while (0) +void Expression::mark(Expression* e) { + if (e == NULL || e->isUnboxedVal()) return; + std::vector stack; + stack.reserve(1000); + stack.push_back(e); + while (!stack.empty()) { + const Expression* cur = stack.back(); + stack.pop_back(); + if (!cur->isUnboxedVal() && cur->_gc_mark == 0) { + cur->_gc_mark = 1; + cur->loc().mark(); + pushann(cur->ann()); + switch (cur->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_ANON: + break; + case Expression::E_SETLIT: + if (cur->cast()->isv()) { + cur->cast()->isv()->mark(); + } else if (cur->cast()->fsv()) { + cur->cast()->fsv()->mark(); + } else { + pushall(cur->cast()->v()); + } + break; + case Expression::E_STRINGLIT: + cur->cast()->v().mark(); + break; + case Expression::E_ID: + if (cur->cast()->idn() == -1) cur->cast()->v().mark(); + pushstack(cur->cast()->decl()); + break; + case Expression::E_ARRAYLIT: + if (cur->_flag_2) { + pushstack(cur->cast()->_u._al); + } else { + pushall(ASTExprVec(cur->cast()->_u._v)); + } + cur->cast()->_dims.mark(); + break; + case Expression::E_ARRAYACCESS: + pushstack(cur->cast()->v()); + pushall(cur->cast()->idx()); + break; + case Expression::E_COMP: + pushstack(cur->cast()->_e); + pushall(cur->cast()->_g); + cur->cast()->_g_idx.mark(); + break; + case Expression::E_ITE: + pushstack(cur->cast()->e_else()); + pushall(cur->cast()->_e_if_then); + break; + case Expression::E_BINOP: + pushstack(cur->cast()->lhs()); + pushstack(cur->cast()->rhs()); + break; + case Expression::E_UNOP: + pushstack(cur->cast()->e()); + break; + case Expression::E_CALL: + cur->cast()->id().mark(); + for (unsigned int i = cur->cast()->n_args(); i--;) + pushstack(cur->cast()->arg(i)); + if (!cur->cast()->_u._oneArg->isUnboxedVal() && + !cur->cast()->_u._oneArg->isTagged()) + cur->cast()->_u._args->mark(); + if (FunctionI* fi = cur->cast()->decl()) { + fi->mark(); + fi->id().mark(); + pushstack(fi->ti()); + pushann(fi->ann()); + pushstack(fi->e()); + pushall(fi->params()); + } + break; + case Expression::E_VARDECL: + pushstack(cur->cast()->ti()); + pushstack(cur->cast()->e()); + pushstack(cur->cast()->id()); + break; + case Expression::E_LET: + pushall(cur->cast()->let()); + pushall(cur->cast()->_let_orig); + pushstack(cur->cast()->in()); + break; + case Expression::E_TI: + pushstack(cur->cast()->domain()); + pushall(cur->cast()->ranges()); + break; + case Expression::E_TIID: + cur->cast()->v().mark(); + break; + } + } + } +} +#undef pushstack +#undef pushall + +void IntLit::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(_v)); +} + +void FloatLit::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(_v)); +} + +void SetLit::rehash(void) { + init_hash(); + if (isv()) { + std::hash h; + for (IntSetRanges r0(isv()); r0(); ++r0) { + cmb_hash(h(r0.min())); + cmb_hash(h(r0.max())); + } + } else if (fsv()) { + std::hash h; + for (FloatSetRanges r0(fsv()); r0(); ++r0) { + cmb_hash(h(r0.min())); + cmb_hash(h(r0.max())); + } + } else { + for (unsigned int i = v().size(); i--;) cmb_hash(Expression::hash(_v[i])); + } +} + +void BoolLit::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(_v)); +} + +void StringLit::rehash(void) { + init_hash(); + cmb_hash(_v.hash()); +} + +void Id::rehash(void) { + init_hash(); + std::hash h; + if (idn() == -1) + cmb_hash(v().hash()); + else + cmb_hash(h(idn())); +} + +ASTString Id::str() const { + if (idn() == -1) return v(); + std::ostringstream oss; + oss << "X_INTRODUCED_" << idn() << "_"; + return oss.str(); +} + +void TIId::rehash(void) { + init_hash(); + cmb_hash(_v.hash()); +} + +void AnonVar::rehash(void) { init_hash(); } + +int ArrayLit::dims(void) const { + return _flag_2 ? ((_dims.size() - 2 * _u._al->dims()) / 2) + : (_dims.size() == 0 ? 1 : _dims.size() / 2); +} +int ArrayLit::min(int i) const { + if (_dims.size() == 0) { + assert(i == 0); + return 1; + } + return _dims[2 * i]; +} +int ArrayLit::max(int i) const { + if (_dims.size() == 0) { + assert(i == 0); + return _u._v->size(); + } + return _dims[2 * i + 1]; +} +int ArrayLit::length(void) const { + if (dims() == 0) return 0; + int l = max(0) - min(0) + 1; + for (int i = 1; i < dims(); i++) l *= (max(i) - min(i) + 1); + return l; +} +void ArrayLit::make1d(void) { + if (_dims.size() != 0) { + GCLock lock; + if (_flag_2) { + std::vector d(2 + _u._al->dims() * 2); + int dimOffset = dims() * 2; + d[0] = 1; + d[1] = length(); + for (unsigned int i = 2; i < d.size(); i++) { + d[i] = _dims[dimOffset + i]; + } + _dims = ASTIntVec(d); + } else { + std::vector d(2); + d[0] = 1; + d[1] = length(); + _dims = ASTIntVec(d); + } + } +} + +int ArrayLit::origIdx(int i) const { + assert(_flag_2); + int curIdx = i; + int multiplyer = 1; + int oIdx = 0; + int sliceOffset = dims() * 2; + for (int curDim = _u._al->dims() - 1; curDim >= 0; curDim--) { + oIdx += + multiplyer * + ((curIdx % (_dims[sliceOffset + curDim * 2 + 1] - _dims[sliceOffset + curDim * 2] + 1)) + + (_dims[sliceOffset + curDim * 2] - _u._al->min(curDim))); + curIdx = curIdx / (_dims[sliceOffset + curDim * 2 + 1] - _dims[sliceOffset + curDim * 2] + 1); + multiplyer *= (_u._al->max(curDim) - _u._al->min(curDim) + 1); + } + return oIdx; +} + +Expression* ArrayLit::slice_get(int i) const { + if (!_flag_2) { + assert(_u._v->flag()); + int off = length() - _u._v->size(); + return i <= off ? (*_u._v)[0] : (*_u._v)[i - off]; + } else { + assert(_flag_2); + return (*_u._al)[origIdx(i)]; + } +} + +void ArrayLit::slice_set(int i, Expression* e) { + if (!_flag_2) { + assert(_u._v->flag()); + int off = length() - _u._v->size(); + if (i <= off) { + (*_u._v)[0] = e; + } else { + (*_u._v)[i - off] = e; + } + } else { + assert(_flag_2); + _u._al->set(origIdx(i), e); + } +} + +ArrayLit::ArrayLit(const Location& loc, ArrayLit* v, const std::vector >& dims, + const std::vector >& slice) + : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = true; + _u._al = v; + assert(slice.size() == v->dims()); + std::vector d(dims.size() * 2 + 2 * slice.size()); + for (unsigned int i = static_cast(dims.size()); i--;) { + d[i * 2] = dims[i].first; + d[i * 2 + 1] = dims[i].second; + } + int sliceOffset = static_cast(2 * dims.size()); + for (unsigned int i = static_cast(slice.size()); i--;) { + d[sliceOffset + i * 2] = slice[i].first; + d[sliceOffset + i * 2 + 1] = slice[i].second; + } + _dims = ASTIntVec(d); +} + +void ArrayLit::compress(const std::vector& v, const std::vector& dims) { + if (v.size() >= 4 && Expression::equal(v[0], v[1]) && Expression::equal(v[1], v[2]) && + Expression::equal(v[2], v[3])) { + std::vector compress(v.size()); + compress[0] = v[0]; + int k = 4; + while (k < v.size() && Expression::equal(v[k], v[0])) { + k++; + } + int i = 1; + for (; k < v.size(); k++) { + compress[i++] = v[k]; + } + compress.resize(i); + _u._v = ASTExprVec(compress).vec(); + _u._v->flag(true); + _dims = ASTIntVec(dims); + } else { + _u._v = ASTExprVec(v).vec(); + if (dims.size() != 2 || dims[0] != 1) { + // only allocate dims vector if it is not a 1d array indexed from 1 + _dims = ASTIntVec(dims); + } + } +} + +ArrayLit::ArrayLit(const Location& loc, const std::vector& v, + const std::vector >& dims) + : Expression(loc, E_ARRAYLIT, Type()) { + _flag_1 = false; + _flag_2 = false; + std::vector d(dims.size() * 2); + for (unsigned int i = static_cast(dims.size()); i--;) { + d[i * 2] = dims[i].first; + d[i * 2 + 1] = dims[i].second; + } + compress(v, d); + rehash(); +} + +void ArrayLit::rehash(void) { + init_hash(); + std::hash h; + for (unsigned int i = 0; i < _dims.size(); i++) { + cmb_hash(h(_dims[i])); + } + if (_flag_2) { + cmb_hash(Expression::hash(_u._al)); + } else { + for (unsigned int i = _u._v->size(); i--;) { + cmb_hash(h(i)); + cmb_hash(Expression::hash((*_u._v)[i])); + } + } +} + +void ArrayAccess::rehash(void) { + init_hash(); + cmb_hash(Expression::hash(_v)); + std::hash h; + cmb_hash(h(_idx.size())); + for (unsigned int i = _idx.size(); i--;) cmb_hash(Expression::hash(_idx[i])); +} + +Generator::Generator(const std::vector& v, Expression* in, Expression* where) { + std::vector vd; + Location loc = in == NULL ? where->loc() : in->loc(); + for (unsigned int i = 0; i < v.size(); i++) { + VarDecl* nvd = new VarDecl(loc, new TypeInst(loc, Type::parint()), v[i]); + nvd->toplevel(false); + vd.push_back(nvd); + } + _v = vd; + _in = in; + _where = where; +} +Generator::Generator(const std::vector& v, Expression* in, Expression* where) { + std::vector vd; + for (unsigned int i = 0; i < v.size(); i++) { + VarDecl* nvd = new VarDecl(v[i]->loc(), new TypeInst(v[i]->loc(), Type::parint()), v[i]->v()); + nvd->toplevel(false); + vd.push_back(nvd); + } + _v = vd; + _in = in; + _where = where; +} +Generator::Generator(const std::vector& v, Expression* in, Expression* where) { + std::vector vd; + Location loc = in == NULL ? where->loc() : in->loc(); + for (unsigned int i = 0; i < v.size(); i++) { + VarDecl* nvd = new VarDecl(loc, new TypeInst(loc, Type::parint()), ASTString(v[i])); + nvd->toplevel(false); + vd.push_back(nvd); + } + _v = vd; + _in = in; + _where = where; +} +Generator::Generator(const std::vector& v, Expression* in, Expression* where) { + _v = v; + _in = in; + _where = where; +} +Generator::Generator(int pos, Expression* where) { + std::vector vd; + std::ostringstream oss; + oss << "__dummy" << pos; + VarDecl* nvd = + new VarDecl(Location().introduce(), new TypeInst(Location().introduce(), Type::parint()), + ASTString(oss.str())); + nvd->toplevel(false); + vd.push_back(nvd); + _v = vd; + _in = new ArrayLit(Location().introduce(), std::vector({IntLit::a(0)})); + _where = where; +} + +bool Comprehension::set(void) const { return _flag_1; } +void Comprehension::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(set())); + cmb_hash(Expression::hash(_e)); + cmb_hash(h(_g_idx.size())); + for (unsigned int i = _g_idx.size(); i--;) { + cmb_hash(h(_g_idx[i])); + } + cmb_hash(h(_g.size())); + for (unsigned int i = _g.size(); i--;) { + cmb_hash(Expression::hash(_g[i])); + } +} + +int Comprehension::n_generators(void) const { return _g_idx.size() - 1; } +Expression* Comprehension::in(int i) { return _g[_g_idx[i]]; } +const Expression* Comprehension::in(int i) const { return _g[_g_idx[i]]; } +const Expression* Comprehension::where(int i) const { return _g[_g_idx[i] + 1]; } +Expression* Comprehension::where(int i) { return _g[_g_idx[i] + 1]; } + +int Comprehension::n_decls(int i) const { return _g_idx[i + 1] - _g_idx[i] - 2; } +VarDecl* Comprehension::decl(int gen, int i) { return _g[_g_idx[gen] + 2 + i]->cast(); } +const VarDecl* Comprehension::decl(int gen, int i) const { + return _g[_g_idx[gen] + 2 + i]->cast(); +} + +void ITE::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(_e_if_then.size())); + for (unsigned int i = _e_if_then.size(); i--;) { + cmb_hash(Expression::hash(_e_if_then[i])); + } + cmb_hash(Expression::hash(e_else())); +} + +BinOpType BinOp::op(void) const { return static_cast(_sec_id); } +void BinOp::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(static_cast(op()))); + cmb_hash(Expression::hash(_e0)); + cmb_hash(Expression::hash(_e1)); +} + +namespace { + +class OpToString { +protected: + Model* rootSetModel; + +public: + Id* sBOT_PLUS; + Id* sBOT_MINUS; + Id* sBOT_MULT; + Id* sBOT_DIV; + Id* sBOT_IDIV; + Id* sBOT_MOD; + Id* sBOT_POW; + Id* sBOT_LE; + Id* sBOT_LQ; + Id* sBOT_GR; + Id* sBOT_GQ; + Id* sBOT_EQ; + Id* sBOT_NQ; + Id* sBOT_IN; + Id* sBOT_SUBSET; + Id* sBOT_SUPERSET; + Id* sBOT_UNION; + Id* sBOT_DIFF; + Id* sBOT_SYMDIFF; + Id* sBOT_INTERSECT; + Id* sBOT_PLUSPLUS; + Id* sBOT_EQUIV; + Id* sBOT_IMPL; + Id* sBOT_RIMPL; + Id* sBOT_OR; + Id* sBOT_AND; + Id* sBOT_XOR; + Id* sBOT_DOTDOT; + Id* sBOT_NOT; + + OpToString(void) { + GCLock lock; + rootSetModel = new Model(); + std::vector rootSet; + sBOT_PLUS = new Id(Location(), "'+'", NULL); + rootSet.push_back(sBOT_PLUS); + sBOT_MINUS = new Id(Location(), "'-'", NULL); + rootSet.push_back(sBOT_MINUS); + sBOT_MULT = new Id(Location(), "'*'", NULL); + rootSet.push_back(sBOT_MULT); + sBOT_DIV = new Id(Location(), "'/'", NULL); + rootSet.push_back(sBOT_DIV); + sBOT_IDIV = new Id(Location(), "'div'", NULL); + rootSet.push_back(sBOT_IDIV); + sBOT_MOD = new Id(Location(), "'mod'", NULL); + rootSet.push_back(sBOT_MOD); + sBOT_POW = new Id(Location(), "'^'", NULL); + rootSet.push_back(sBOT_POW); + sBOT_LE = new Id(Location(), "'<'", NULL); + rootSet.push_back(sBOT_LE); + sBOT_LQ = new Id(Location(), "'<='", NULL); + rootSet.push_back(sBOT_LQ); + sBOT_GR = new Id(Location(), "'>'", NULL); + rootSet.push_back(sBOT_GR); + sBOT_GQ = new Id(Location(), "'>='", NULL); + rootSet.push_back(sBOT_GQ); + sBOT_EQ = new Id(Location(), "'='", NULL); + rootSet.push_back(sBOT_EQ); + sBOT_NQ = new Id(Location(), "'!='", NULL); + rootSet.push_back(sBOT_NQ); + sBOT_IN = new Id(Location(), "'in'", NULL); + rootSet.push_back(sBOT_IN); + sBOT_SUBSET = new Id(Location(), "'subset'", NULL); + rootSet.push_back(sBOT_SUBSET); + sBOT_SUPERSET = new Id(Location(), "'superset'", NULL); + rootSet.push_back(sBOT_SUPERSET); + sBOT_UNION = new Id(Location(), "'union'", NULL); + rootSet.push_back(sBOT_UNION); + sBOT_DIFF = new Id(Location(), "'diff'", NULL); + rootSet.push_back(sBOT_DIFF); + sBOT_SYMDIFF = new Id(Location(), "'symdiff'", NULL); + rootSet.push_back(sBOT_SYMDIFF); + sBOT_INTERSECT = new Id(Location(), "'intersect'", NULL); + rootSet.push_back(sBOT_INTERSECT); + sBOT_PLUSPLUS = new Id(Location(), "'++'", NULL); + rootSet.push_back(sBOT_PLUSPLUS); + sBOT_EQUIV = new Id(Location(), "'<->'", NULL); + rootSet.push_back(sBOT_EQUIV); + sBOT_IMPL = new Id(Location(), "'->'", NULL); + rootSet.push_back(sBOT_IMPL); + sBOT_RIMPL = new Id(Location(), "'<-'", NULL); + rootSet.push_back(sBOT_RIMPL); + sBOT_OR = new Id(Location(), "'\\/'", NULL); + rootSet.push_back(sBOT_OR); + sBOT_AND = new Id(Location(), "'/\\'", NULL); + rootSet.push_back(sBOT_AND); + sBOT_XOR = new Id(Location(), "'xor'", NULL); + rootSet.push_back(sBOT_XOR); + sBOT_DOTDOT = new Id(Location(), "'..'", NULL); + rootSet.push_back(sBOT_DOTDOT); + sBOT_NOT = new Id(Location(), "'not'", NULL); + rootSet.push_back(sBOT_NOT); + rootSetModel->addItem(new ConstraintI(Location(), new ArrayLit(Location(), rootSet))); + } + + static OpToString& o(void) { + static OpToString _o; + return _o; + } +}; +} // namespace + +ASTString BinOp::opToString(void) const { + switch (op()) { + case BOT_PLUS: + return OpToString::o().sBOT_PLUS->v(); + case BOT_MINUS: + return OpToString::o().sBOT_MINUS->v(); + case BOT_MULT: + return OpToString::o().sBOT_MULT->v(); + case BOT_DIV: + return OpToString::o().sBOT_DIV->v(); + case BOT_IDIV: + return OpToString::o().sBOT_IDIV->v(); + case BOT_MOD: + return OpToString::o().sBOT_MOD->v(); + case BOT_POW: + return OpToString::o().sBOT_POW->v(); + case BOT_LE: + return OpToString::o().sBOT_LE->v(); + case BOT_LQ: + return OpToString::o().sBOT_LQ->v(); + case BOT_GR: + return OpToString::o().sBOT_GR->v(); + case BOT_GQ: + return OpToString::o().sBOT_GQ->v(); + case BOT_EQ: + return OpToString::o().sBOT_EQ->v(); + case BOT_NQ: + return OpToString::o().sBOT_NQ->v(); + case BOT_IN: + return OpToString::o().sBOT_IN->v(); + case BOT_SUBSET: + return OpToString::o().sBOT_SUBSET->v(); + case BOT_SUPERSET: + return OpToString::o().sBOT_SUPERSET->v(); + case BOT_UNION: + return OpToString::o().sBOT_UNION->v(); + case BOT_DIFF: + return OpToString::o().sBOT_DIFF->v(); + case BOT_SYMDIFF: + return OpToString::o().sBOT_SYMDIFF->v(); + case BOT_INTERSECT: + return OpToString::o().sBOT_INTERSECT->v(); + case BOT_PLUSPLUS: + return OpToString::o().sBOT_PLUSPLUS->v(); + case BOT_EQUIV: + return OpToString::o().sBOT_EQUIV->v(); + case BOT_IMPL: + return OpToString::o().sBOT_IMPL->v(); + case BOT_RIMPL: + return OpToString::o().sBOT_RIMPL->v(); + case BOT_OR: + return OpToString::o().sBOT_OR->v(); + case BOT_AND: + return OpToString::o().sBOT_AND->v(); + case BOT_XOR: + return OpToString::o().sBOT_XOR->v(); + case BOT_DOTDOT: + return OpToString::o().sBOT_DOTDOT->v(); + default: + assert(false); + return ASTString(""); + } +} + +UnOpType UnOp::op(void) const { return static_cast(_sec_id); } +void UnOp::rehash(void) { + init_hash(); + std::hash h; + cmb_hash(h(static_cast(_sec_id))); + cmb_hash(Expression::hash(_e0)); +} + +ASTString UnOp::opToString(void) const { + switch (op()) { + case UOT_PLUS: + return OpToString::o().sBOT_PLUS->v(); + case UOT_MINUS: + return OpToString::o().sBOT_MINUS->v(); + case UOT_NOT: + return OpToString::o().sBOT_NOT->v(); + default: + assert(false); + return ASTString(""); + } +} + +void Call::rehash(void) { + init_hash(); + cmb_hash(id().hash()); + std::hash hf; + cmb_hash(hf(decl())); + std::hash hu; + cmb_hash(hu(n_args())); + for (unsigned int i = 0; i < n_args(); i++) cmb_hash(Expression::hash(arg(i))); +} + +void VarDecl::trail(void) { + GC::trail(&_e, e()); + if (_ti->ranges().size() > 0) { + GC::trail(reinterpret_cast(&_ti), _ti); + } +} + +void VarDecl::rehash(void) { + init_hash(); + cmb_hash(Expression::hash(_ti)); + cmb_hash(_id->hash()); + cmb_hash(Expression::hash(_e)); +} + +void Let::rehash(void) { + init_hash(); + cmb_hash(Expression::hash(_in)); + std::hash h; + cmb_hash(h(_let.size())); + for (unsigned int i = _let.size(); i--;) cmb_hash(Expression::hash(_let[i])); +} + +Let::Let(const Location& loc, const std::vector& let, Expression* in) + : Expression(loc, E_LET, Type()) { + _let = ASTExprVec(let); + std::vector vde(let.size()); + for (unsigned int i = 0; i < let.size(); i++) { + if (VarDecl* vd = Expression::dyn_cast(let[i])) { + vde[i] = vd->e(); + } else { + vde[i] = NULL; + } + } + _let_orig = ASTExprVec(vde); + _in = in; + rehash(); +} + +void Let::pushbindings(void) { + GC::mark(); + for (unsigned int i = _let.size(); i--;) { + if (VarDecl* vd = _let[i]->dyn_cast()) { + vd->trail(); + vd->e(_let_orig[i]); + } + } +} +void Let::popbindings(void) { + for (unsigned int i = _let.size(); i--;) { + if (VarDecl* vd = _let[i]->dyn_cast()) { + GC::untrail(); + break; + } + } +} + +void TypeInst::rehash(void) { + init_hash(); + std::hash h; + unsigned int rsize = _ranges.size(); + cmb_hash(h(rsize)); + for (unsigned int i = rsize; i--;) cmb_hash(Expression::hash(_ranges[i])); + cmb_hash(Expression::hash(domain())); +} + +void TypeInst::setRanges(const std::vector& ranges) { + _ranges = ASTExprVec(ranges); + if (ranges.size() == 1 && ranges[0] && ranges[0]->isa() && + ranges[0]->cast()->domain() && ranges[0]->cast()->domain()->isa() && + !ranges[0]->cast()->domain()->cast()->v().beginsWith("$")) + _type.dim(-1); + else + _type.dim(static_cast(ranges.size())); + rehash(); +} + +bool TypeInst::hasTiVariable(void) const { + if (domain() && domain()->isa()) return true; + for (unsigned int i = _ranges.size(); i--;) + if (_ranges[i]->isa()) return true; + return false; +} + +namespace { +Type getType(Expression* e) { return e->type(); } +Type getType(const Type& t) { return t; } +const Location& getLoc(Expression* e, FunctionI*) { return e->loc(); } +const Location& getLoc(const Type&, FunctionI* fi) { return fi->loc(); } + +bool isaTIId(Expression* e) { + if (TIId* t = Expression::dyn_cast(e)) { + return !t->v().beginsWith("$"); + } + return false; +} +bool isaEnumTIId(Expression* e) { + if (TIId* t = Expression::dyn_cast(e)) { + return t->v().beginsWith("$"); + } + return false; +} + +template +Type return_type(EnvI& env, FunctionI* fi, const std::vector& ta, bool strictEnum) { + if (fi->id() == constants().var_redef->id()) return Type::varbool(); + Type ret = fi->ti()->type(); + ASTString dh; + if (fi->ti()->domain() && fi->ti()->domain()->isa()) + dh = fi->ti()->domain()->cast()->v(); + ASTString rh; + if (fi->ti()->ranges().size() == 1 && isaTIId(fi->ti()->ranges()[0]->domain())) + rh = fi->ti()->ranges()[0]->domain()->cast()->v(); + + ASTStringMap::t tmap; + for (unsigned int i = 0; i < ta.size(); i++) { + TypeInst* tii = fi->params()[i]->ti(); + if (tii->domain() && tii->domain()->isa()) { + ASTString tiid = tii->domain()->cast()->v(); + Type tiit = getType(ta[i]); + if (tiit.enumId() != 0 && tiit.dim() > 0) { + const std::vector& enumIds = env.getArrayEnum(tiit.enumId()); + tiit.enumId(enumIds[enumIds.size() - 1]); + } + tiit.dim(0); + if (tii->type().st() == Type::ST_SET) { + tiit.st(Type::ST_PLAIN); + } + if (isaEnumTIId(tii->domain())) { + tiit.st(Type::ST_SET); + } + ASTStringMap::t::iterator it = tmap.find(tiid); + if (it == tmap.end()) { + tmap.insert(std::pair(tiid, tiit)); + } else { + if (it->second.dim() > 0) { + throw TypeError( + env, getLoc(ta[i], fi), + "type-inst variable $" + tiid.str() + " used in both array and non-array position"); + } else { + Type tiit_par = tiit; + tiit_par.ti(Type::TI_PAR); + tiit_par.ot(Type::OT_PRESENT); + Type its_par = it->second; + its_par.ti(Type::TI_PAR); + its_par.ot(Type::OT_PRESENT); + if (tiit_par.bt() == Type::BT_TOP || tiit_par.bt() == Type::BT_BOT) { + tiit_par.bt(its_par.bt()); + } + if (its_par.bt() == Type::BT_TOP || its_par.bt() == Type::BT_BOT) { + its_par.bt(tiit_par.bt()); + } + if (env.isSubtype(tiit_par, its_par, strictEnum)) { + if (it->second.bt() == Type::BT_TOP) it->second.bt(tiit.bt()); + } else if (env.isSubtype(its_par, tiit_par, strictEnum)) { + it->second = tiit_par; + } else { + throw TypeError(env, getLoc(ta[i], fi), + "type-inst variable $" + tiid.str() + + " instantiated with different types (" + tiit.toString(env) + + " vs " + it->second.toString(env) + ")"); + } + } + } + } + if (tii->ranges().size() == 1 && isaTIId(tii->ranges()[0]->domain())) { + ASTString tiid = tii->ranges()[0]->domain()->cast()->v(); + Type orig_tiit = getType(ta[i]); + if (orig_tiit.dim() == 0) { + throw TypeError(env, getLoc(ta[i], fi), + "type-inst variable $" + tiid.str() + " must be an array index"); + } + Type tiit = Type::top(orig_tiit.dim()); + if (orig_tiit.enumId() != 0) { + std::vector enumIds(tiit.dim() + 1); + const std::vector& orig_enumIds = env.getArrayEnum(orig_tiit.enumId()); + for (unsigned int i = 0; i < enumIds.size() - 1; i++) { + enumIds[i] = orig_enumIds[i]; + } + enumIds[enumIds.size() - 1] = 0; + tiit.enumId(env.registerArrayEnum(enumIds)); + } + ASTStringMap::t::iterator it = tmap.find(tiid); + if (it == tmap.end()) { + tmap.insert(std::pair(tiid, tiit)); + } else { + if (it->second.dim() == 0) { + throw TypeError( + env, getLoc(ta[i], fi), + "type-inst variable $" + tiid.str() + " used in both array and non-array position"); + } else if (it->second != tiit) { + throw TypeError(env, getLoc(ta[i], fi), + "type-inst variable $" + tiid.str() + + " instantiated with different types (" + tiit.toString(env) + " vs " + + it->second.toString(env) + ")"); + } + } + } else if (tii->ranges().size() > 0) { + for (unsigned int j = 0; j < tii->ranges().size(); j++) { + if (isaEnumTIId(tii->ranges()[j]->domain())) { + ASTString enumTIId = tii->ranges()[j]->domain()->cast()->v(); + Type tiit = getType(ta[i]); + Type enumIdT; + if (tiit.enumId() != 0) { + unsigned int enumId = env.getArrayEnum(tiit.enumId())[j]; + enumIdT = Type::parsetenum(enumId); + } else { + enumIdT = Type::parsetint(); + } + ASTStringMap::t::iterator it = tmap.find(enumTIId); + // TODO: this may clash if the same enum TIId is used for different types + // but the same enum + if (it == tmap.end()) { + tmap.insert(std::pair(enumTIId, enumIdT)); + } else { + if (it->second.enumId() != enumIdT.enumId()) { + throw TypeError( + env, getLoc(ta[i], fi), + "type-inst variable $" + enumTIId.str() + " used for different enum types"); + } + } + } + } + } + } + if (dh.size() != 0) { + ASTStringMap::t::iterator it = tmap.find(dh); + if (it == tmap.end()) + throw TypeError(env, fi->loc(), "type-inst variable $" + dh.str() + " used but not defined"); + if (dh.beginsWith("$")) { + // this is an enum + ret.bt(Type::BT_INT); + } else { + ret.bt(it->second.bt()); + if (ret.st() == Type::ST_PLAIN) ret.st(it->second.st()); + } + if (fi->ti()->ranges().size() > 0 && it->second.enumId() != 0) { + std::vector enumIds(fi->ti()->ranges().size() + 1); + for (unsigned int i = 0; i < fi->ti()->ranges().size(); i++) { + enumIds[i] = 0; + } + enumIds[enumIds.size() - 1] = it->second.enumId(); + ret.enumId(env.registerArrayEnum(enumIds)); + } else { + ret.enumId(it->second.enumId()); + } + } + if (rh.size() != 0) { + ASTStringMap::t::iterator it = tmap.find(rh); + if (it == tmap.end()) + throw TypeError(env, fi->loc(), "type-inst variable $" + rh.str() + " used but not defined"); + ret.dim(it->second.dim()); + if (it->second.enumId() != 0) { + std::vector enumIds(it->second.dim() + 1); + const std::vector& orig_enumIds = env.getArrayEnum(it->second.enumId()); + for (unsigned int i = 0; i < enumIds.size() - 1; i++) { + enumIds[i] = orig_enumIds[i]; + } + enumIds[enumIds.size() - 1] = + ret.enumId() == 0 ? 0 : env.getArrayEnum(ret.enumId())[enumIds.size() - 1]; + ret.enumId(env.registerArrayEnum(enumIds)); + } + + } else if (fi->ti()->ranges().size() > 0) { + std::vector enumIds(fi->ti()->ranges().size() + 1); + bool hadRealEnum = false; + if (ret.enumId() == 0) { + enumIds[enumIds.size() - 1] = 0; + } else { + enumIds[enumIds.size() - 1] = env.getArrayEnum(ret.enumId())[enumIds.size() - 1]; + hadRealEnum = true; + } + + for (unsigned int i = 0; i < fi->ti()->ranges().size(); i++) { + if (isaEnumTIId(fi->ti()->ranges()[i]->domain())) { + ASTString enumTIId = fi->ti()->ranges()[i]->domain()->cast()->v(); + ASTStringMap::t::iterator it = tmap.find(enumTIId); + if (it == tmap.end()) + throw TypeError(env, fi->loc(), + "type-inst variable $" + enumTIId.str() + " used but not defined"); + enumIds[i] = it->second.enumId(); + hadRealEnum |= (enumIds[i] != 0); + } else { + enumIds[i] = 0; + } + } + if (hadRealEnum) ret.enumId(env.registerArrayEnum(enumIds)); + } + return ret; +} +} // namespace + +Type FunctionI::rtype(EnvI& env, const std::vector& ta, bool strictEnums) { + return return_type(env, this, ta, strictEnums); +} + +Type FunctionI::rtype(EnvI& env, const std::vector& ta, bool strictEnums) { + return return_type(env, this, ta, strictEnums); +} + +Type FunctionI::argtype(EnvI& env, const std::vector& ta, int n) { + TypeInst* tii = params()[n]->ti(); + if (tii->domain() && tii->domain()->isa()) { + Type ty = ta[n]->type(); + ty.st(tii->type().st()); + ty.dim(tii->type().dim()); + ASTString tv = tii->domain()->cast()->v(); + for (unsigned int i = 0; i < params().size(); i++) { + if (params()[i]->ti()->domain() && params()[i]->ti()->domain()->isa() && + params()[i]->ti()->domain()->cast()->v() == tv) { + Type toCheck = ta[i]->type(); + toCheck.st(tii->type().st()); + toCheck.dim(tii->type().dim()); + if (toCheck != ty) { + if (env.isSubtype(ty, toCheck, true)) { + ty = toCheck; + } else { + Type ty_par = ty; + ty_par.ti(Type::TI_PAR); + Type toCheck_par = toCheck; + toCheck_par.ti(Type::TI_PAR); + if (env.isSubtype(ty_par, toCheck_par, true)) { + ty.bt(toCheck.bt()); + } + } + } + } + } + return ty; + } else { + return tii->type(); + } +} + +bool Expression::equal_internal(const Expression* e0, const Expression* e1) { + switch (e0->eid()) { + case Expression::E_INTLIT: + return e0->cast()->v() == e1->cast()->v(); + case Expression::E_FLOATLIT: + return e0->cast()->v() == e1->cast()->v(); + case Expression::E_SETLIT: { + const SetLit* s0 = e0->cast(); + const SetLit* s1 = e1->cast(); + if (s0->isv()) { + if (s1->isv()) { + IntSetRanges r0(s0->isv()); + IntSetRanges r1(s1->isv()); + return Ranges::equal(r0, r1); + } else { + return false; + } + } else if (s0->fsv()) { + if (s1->fsv()) { + FloatSetRanges r0(s0->fsv()); + FloatSetRanges r1(s1->fsv()); + return Ranges::equal(r0, r1); + } else { + return false; + } + } else { + if (s1->isv() || s1->fsv()) return false; + if (s0->v().size() != s1->v().size()) return false; + for (unsigned int i = 0; i < s0->v().size(); i++) + if (!Expression::equal(s0->v()[i], s1->v()[i])) return false; + return true; + } + } + case Expression::E_BOOLLIT: + return e0->cast()->v() == e1->cast()->v(); + case Expression::E_STRINGLIT: + return e0->cast()->v() == e1->cast()->v(); + case Expression::E_ID: { + const Id* id0 = e0->cast(); + const Id* id1 = e1->cast(); + if (id0->decl() == NULL || id1->decl() == NULL) { + return id0->v() == id1->v() && id0->idn() == id1->idn(); + } + return id0->decl() == id1->decl() || + (id0->decl()->flat() != NULL && id0->decl()->flat() == id1->decl()->flat()); + } + case Expression::E_ANON: + return false; + case Expression::E_ARRAYLIT: { + const ArrayLit* a0 = e0->cast(); + const ArrayLit* a1 = e1->cast(); + if (a0->size() != a1->size()) return false; + if (a0->_dims.size() != a1->_dims.size()) return false; + for (unsigned int i = 0; i < a0->_dims.size(); i++) { + if (a0->_dims[i] != a1->_dims[i]) { + return false; + } + } + for (unsigned int i = 0; i < a0->size(); i++) { + if (!Expression::equal((*a0)[i], (*a1)[i])) { + return false; + } + } + return true; + } + case Expression::E_ARRAYACCESS: { + const ArrayAccess* a0 = e0->cast(); + const ArrayAccess* a1 = e1->cast(); + if (!Expression::equal(a0->v(), a1->v())) return false; + if (a0->idx().size() != a1->idx().size()) return false; + for (unsigned int i = 0; i < a0->idx().size(); i++) + if (!Expression::equal(a0->idx()[i], a1->idx()[i])) return false; + return true; + } + case Expression::E_COMP: { + const Comprehension* c0 = e0->cast(); + const Comprehension* c1 = e1->cast(); + if (c0->set() != c1->set()) return false; + if (!Expression::equal(c0->_e, c1->_e)) return false; + if (c0->_g.size() != c1->_g.size()) return false; + for (unsigned int i = 0; i < c0->_g.size(); i++) { + if (!Expression::equal(c0->_g[i], c1->_g[i])) return false; + } + for (unsigned int i = 0; i < c0->_g_idx.size(); i++) { + if (c0->_g_idx[i] != c1->_g_idx[i]) return false; + } + return true; + } + case Expression::E_ITE: { + const ITE* i0 = e0->cast(); + const ITE* i1 = e1->cast(); + if (i0->_e_if_then.size() != i1->_e_if_then.size()) return false; + for (unsigned int i = i0->_e_if_then.size(); i--;) { + if (!Expression::equal(i0->_e_if_then[i], i1->_e_if_then[i])) return false; + } + if (!Expression::equal(i0->e_else(), i1->e_else())) return false; + return true; + } + case Expression::E_BINOP: { + const BinOp* b0 = e0->cast(); + const BinOp* b1 = e1->cast(); + if (b0->op() != b1->op()) return false; + if (!Expression::equal(b0->lhs(), b1->lhs())) return false; + if (!Expression::equal(b0->rhs(), b1->rhs())) return false; + return true; + } + case Expression::E_UNOP: { + const UnOp* b0 = e0->cast(); + const UnOp* b1 = e1->cast(); + if (b0->op() != b1->op()) return false; + if (!Expression::equal(b0->e(), b1->e())) return false; + return true; + } + case Expression::E_CALL: { + const Call* c0 = e0->cast(); + const Call* c1 = e1->cast(); + if (c0->id() != c1->id()) return false; + if (c0->decl() != c1->decl()) return false; + if (c0->n_args() != c1->n_args()) return false; + for (unsigned int i = 0; i < c0->n_args(); i++) + if (!Expression::equal(c0->arg(i), c1->arg(i))) return false; + return true; + } + case Expression::E_VARDECL: { + const VarDecl* v0 = e0->cast(); + const VarDecl* v1 = e1->cast(); + if (!Expression::equal(v0->ti(), v1->ti())) return false; + if (!Expression::equal(v0->id(), v1->id())) return false; + if (!Expression::equal(v0->e(), v1->e())) return false; + return true; + } + case Expression::E_LET: { + const Let* l0 = e0->cast(); + const Let* l1 = e1->cast(); + if (!Expression::equal(l0->in(), l1->in())) return false; + if (l0->let().size() != l1->let().size()) return false; + for (unsigned int i = l0->let().size(); i--;) + if (!Expression::equal(l0->let()[i], l1->let()[i])) return false; + return true; + } + case Expression::E_TI: { + const TypeInst* t0 = e0->cast(); + const TypeInst* t1 = e1->cast(); + if (t0->ranges().size() != t1->ranges().size()) return false; + for (unsigned int i = t0->ranges().size(); i--;) + if (!Expression::equal(t0->ranges()[i], t1->ranges()[i])) return false; + if (!Expression::equal(t0->domain(), t1->domain())) return false; + return true; + } + case Expression::E_TIID: + return false; + default: + assert(false); + return false; + } +} + +Constants::Constants(void) { + GCLock lock; + TypeInst* ti = new TypeInst(Location(), Type::parbool()); + lit_true = new BoolLit(Location(), true); + var_true = new VarDecl(Location(), ti, "_bool_true", lit_true); + lit_false = new BoolLit(Location(), false); + var_false = new VarDecl(Location(), ti, "_bool_false", lit_false); + var_ignore = new VarDecl(Location(), ti, "_bool_ignore"); + absent = new Id(Location(), "_absent", NULL); + Type absent_t; + absent_t.bt(Type::BT_BOT); + absent_t.dim(0); + absent_t.st(Type::ST_PLAIN); + absent_t.ot(Type::OT_OPTIONAL); + absent->type(absent_t); + + IntSetVal* isv_infty = IntSetVal::a(-IntVal::infinity(), IntVal::infinity()); + infinity = new SetLit(Location(), isv_infty); + + ids.forall = ASTString("forall"); + ids.forall_reif = ASTString("forall_reif"); + ids.exists = ASTString("exists"); + ids.clause = ASTString("clause"); + ids.bool2int = ASTString("bool2int"); + ids.int2float = ASTString("int2float"); + ids.bool2float = ASTString("bool2float"); + ids.assert = ASTString("assert"); + ids.trace = ASTString("trace"); + + ids.sum = ASTString("sum"); + ids.lin_exp = ASTString("lin_exp"); + ids.element = ASTString("element"); + + ids.show = ASTString("show"); + ids.output = ASTString("output"); + ids.fix = ASTString("fix"); + + ids.int_.lin_eq = ASTString("int_lin_eq"); + ids.int_.lin_le = ASTString("int_lin_le"); + ids.int_.lin_ne = ASTString("int_lin_ne"); + ids.int_.plus = ASTString("int_plus"); + ids.int_.minus = ASTString("int_minus"); + ids.int_.times = ASTString("int_times"); + ids.int_.div = ASTString("int_div"); + ids.int_.mod = ASTString("int_mod"); + ids.int_.lt = ASTString("int_lt"); + ids.int_.le = ASTString("int_le"); + ids.int_.gt = ASTString("int_gt"); + ids.int_.ge = ASTString("int_ge"); + ids.int_.eq = ASTString("int_eq"); + ids.int_.ne = ASTString("int_ne"); + + ids.int_reif.lin_eq = ASTString("int_lin_eq_reif"); + ids.int_reif.lin_le = ASTString("int_lin_le_reif"); + ids.int_reif.lin_ne = ASTString("int_lin_ne_reif"); + ids.int_reif.plus = ASTString("int_plus_reif"); + ids.int_reif.minus = ASTString("int_minus_reif"); + ids.int_reif.times = ASTString("int_times_reif"); + ids.int_reif.div = ASTString("int_div_reif"); + ids.int_reif.mod = ASTString("int_mod_reif"); + ids.int_reif.lt = ASTString("int_lt_reif"); + ids.int_reif.le = ASTString("int_le_reif"); + ids.int_reif.gt = ASTString("int_gt_reif"); + ids.int_reif.ge = ASTString("int_ge_reif"); + ids.int_reif.eq = ASTString("int_eq_reif"); + ids.int_reif.ne = ASTString("int_ne_reif"); + + ids.float_.lin_eq = ASTString("float_lin_eq"); + ids.float_.lin_le = ASTString("float_lin_le"); + ids.float_.lin_lt = ASTString("float_lin_lt"); + ids.float_.lin_ne = ASTString("float_lin_ne"); + ids.float_.plus = ASTString("float_plus"); + ids.float_.minus = ASTString("float_minus"); + ids.float_.times = ASTString("float_times"); + ids.float_.div = ASTString("float_div"); + ids.float_.mod = ASTString("float_mod"); + ids.float_.lt = ASTString("float_lt"); + ids.float_.le = ASTString("float_le"); + ids.float_.gt = ASTString("float_gt"); + ids.float_.ge = ASTString("float_ge"); + ids.float_.eq = ASTString("float_eq"); + ids.float_.ne = ASTString("float_ne"); + ids.float_.in = ASTString("float_in"); + ids.float_.dom = ASTString("float_dom"); + + ids.float_reif.lin_eq = ASTString("float_lin_eq_reif"); + ids.float_reif.lin_le = ASTString("float_lin_le_reif"); + ids.float_reif.lin_lt = ASTString("float_lin_lt_reif"); + ids.float_reif.lin_ne = ASTString("float_lin_ne_reif"); + ids.float_reif.plus = ASTString("float_plus_reif"); + ids.float_reif.minus = ASTString("float_minus_reif"); + ids.float_reif.times = ASTString("float_times_reif"); + ids.float_reif.div = ASTString("float_div_reif"); + ids.float_reif.mod = ASTString("float_mod_reif"); + ids.float_reif.lt = ASTString("float_lt_reif"); + ids.float_reif.le = ASTString("float_le_reif"); + ids.float_reif.gt = ASTString("float_gt_reif"); + ids.float_reif.ge = ASTString("float_ge_reif"); + ids.float_reif.eq = ASTString("float_eq_reif"); + ids.float_reif.ne = ASTString("float_ne_reif"); + ids.float_reif.in = ASTString("float_in_reif"); + + ids.bool_eq = ASTString("bool_eq"); + ids.bool_eq_reif = ASTString("bool_eq_reif"); + ids.bool_clause = ASTString("bool_clause"); + ids.bool_clause_reif = ASTString("bool_clause_reif"); + ids.bool_xor = ASTString("bool_xor"); + ids.array_bool_or = ASTString("array_bool_or"); + ids.array_bool_and = ASTString("array_bool_and"); + ids.set_eq = ASTString("set_eq"); + ids.set_in = ASTString("set_in"); + ids.set_card = ASTString("set_card"); + ids.pow = ASTString("pow"); + + ids.introduced_var = ASTString("__INTRODUCED"); + ids.anonEnumFromStrings = ASTString("anon_enum"); + + ctx.root = new Id(Location(), ASTString("ctx_root"), NULL); + ctx.root->type(Type::ann()); + ctx.pos = new Id(Location(), ASTString("ctx_pos"), NULL); + ctx.pos->type(Type::ann()); + ctx.neg = new Id(Location(), ASTString("ctx_neg"), NULL); + ctx.neg->type(Type::ann()); + ctx.mix = new Id(Location(), ASTString("ctx_mix"), NULL); + ctx.mix->type(Type::ann()); + + ann.output_var = new Id(Location(), ASTString("output_var"), NULL); + ann.output_var->type(Type::ann()); + ann.output_only = new Id(Location(), ASTString("output_only"), NULL); + ann.output_only->type(Type::ann()); + ann.output_array = ASTString("output_array"); + ann.add_to_output = new Id(Location(), ASTString("add_to_output"), NULL); + ann.add_to_output->type(Type::ann()); + ann.mzn_check_var = new Id(Location(), ASTString("mzn_check_var"), NULL); + ann.mzn_check_var->type(Type::ann()); + ann.mzn_check_enum_var = ASTString("mzn_check_enum_var"); + ann.is_defined_var = new Id(Location(), ASTString("is_defined_var"), NULL); + ann.is_defined_var->type(Type::ann()); + ann.defines_var = ASTString("defines_var"); + ann.is_reverse_map = new Id(Location(), ASTString("is_reverse_map"), NULL); + ann.is_reverse_map->type(Type::ann()); + ann.promise_total = new Id(Location(), ASTString("promise_total"), NULL); + ann.promise_total->type(Type::ann()); + ann._export = new Id(Location(), ASTString("export"), NULL); + ann._export->type(Type::ann()); + ann.maybe_partial = new Id(Location(), ASTString("maybe_partial"), NULL); + ann.maybe_partial->type(Type::ann()); + ann.doc_comment = ASTString("doc_comment"); + ann.mzn_path = ASTString("mzn_path"); + ann.is_introduced = ASTString("is_introduced"); + ann.user_cut = new Id(Location(), ASTString("user_cut"), NULL); + ann.user_cut->type(Type::ann()); + ann.lazy_constraint = new Id(Location(), ASTString("lazy_constraint"), NULL); + ann.lazy_constraint->type(Type::ann()); +#ifndef NDEBUG + ann.mzn_break_here = new Id(Location(), ASTString("mzn_break_here"), NULL); + ann.mzn_break_here->type(Type::ann()); +#endif + ann.rhs_from_assignment = new Id(Location(), ASTString("mzn_rhs_from_assignment"), NULL); + ann.rhs_from_assignment->type(Type::ann()); + ann.domain_change_constraint = new Id(Location(), ASTString("domain_change_constraint"), NULL); + ann.domain_change_constraint->type(Type::ann()); + ann.global_register = ASTString("global_register"); + + var_redef = new FunctionI(Location(), "__internal_var_redef", + new TypeInst(Location(), Type::varbool()), std::vector()); + + cli.cmdlineData_short_str = ASTString("-D"); + cli.cmdlineData_str = ASTString("--cmdline-data"); + cli.datafile_str = ASTString("--data"); + cli.datafile_short_str = ASTString("-d"); + cli.globalsDir_str = ASTString("--globals-dir"); + cli.globalsDir_alt_str = ASTString("--mzn-globals-dir"); + cli.globalsDir_short_str = ASTString("-G"); + cli.help_str = ASTString("--help"); + cli.help_short_str = ASTString("-h"); + cli.ignoreStdlib_str = ASTString("--ignore-stdlib"); + cli.include_str = ASTString("-I"); + cli.inputFromStdin_str = ASTString("--input-from-stdin"); + cli.instanceCheckOnly_str = ASTString("--instance-check-only"); + cli.newfzn_str = ASTString("--newfzn"); + cli.no_optimize_str = ASTString("--no-optimize"); + cli.no_optimize_alt_str = ASTString("--no-optimise"); + cli.no_outputOzn_str = ASTString("--no-output-ozn"); + cli.no_outputOzn_short_str = ASTString("-O-"); + cli.no_typecheck_str = ASTString("--no-typecheck"); + cli.outputBase_str = ASTString("--output-base"); + cli.outputFznToStdout_str = ASTString("--output-to-stdout"); + cli.outputFznToStdout_alt_str = ASTString("--output-fzn-to-stdout"); + cli.outputOznToFile_str = ASTString("--output-ozn-to-file"); + cli.outputOznToStdout_str = ASTString("--output-ozn-to-stdout"); + cli.outputFznToFile_alt_str = ASTString("--output-fzn-to-file"); + cli.outputFznToFile_short_str = ASTString("-o"); + cli.outputFznToFile_str = ASTString("--output-to-file"); + cli.rangeDomainsOnly_str = ASTString("--only-range-domains"); + cli.statistics_str = ASTString("--statistics"); + cli.statistics_short_str = ASTString("-s"); + cli.stdlib_str = ASTString("--stdlib-dir"); + cli.verbose_str = ASTString("--verbose"); + cli.verbose_short_str = ASTString("-v"); + cli.version_str = ASTString("--version"); + cli.werror_str = ASTString("-Werror"); + + cli.solver.all_sols_str = ASTString("-a"); + cli.solver.fzn_solver_str = ASTString("--solver"); + + opts.cmdlineData = ASTString("cmdlineData"); + opts.datafile = ASTString("datafile"); + opts.datafiles = ASTString("datafiles"); + opts.fznToFile = ASTString("fznToFile"); + opts.fznToStdout = ASTString("fznToStdout"); + opts.globalsDir = ASTString("globalsDir"); + opts.ignoreStdlib = ASTString("ignoreStdlib"); + opts.includeDir = ASTString("includeDir"); + opts.includePaths = ASTString("includePaths"); + opts.inputFromStdin = ASTString("inputStdin"); + opts.instanceCheckOnly = ASTString("instanceCheckOnly"); + opts.model = ASTString("model"); + opts.newfzn = ASTString("newfzn"); + opts.noOznOutput = ASTString("noOznOutput"); + opts.optimize = ASTString("optimize"); + opts.outputBase = ASTString("outputBase"); + opts.oznToFile = ASTString("oznToFile"); + opts.oznToStdout = ASTString("oznToStdout"); + opts.rangeDomainsOnly = ASTString("rangeDomainsOnly"); + opts.statistics = ASTString("statistics"); + opts.stdlib = ASTString("stdlib"); + opts.typecheck = ASTString("typecheck"); + opts.verbose = ASTString("verbose"); + opts.werror = ASTString("werror"); + + opts.solver.allSols = ASTString("allSols"); + opts.solver.numSols = ASTString("numSols"); + opts.solver.threads = ASTString("threads"); + opts.solver.fzn_solver = ASTString("fznsolver"); + opts.solver.fzn_flags = ASTString("fzn_flags"); + opts.solver.fzn_flag = ASTString("fzn_flag"); + opts.solver.fzn_time_limit_ms = ASTString("fzn_time_limit_ms"); + opts.solver.fzn_sigint = ASTString("fzn_sigint"); + + cli_cat.general = ASTString("General Options"); + cli_cat.io = ASTString("Input/Output Options"); + cli_cat.solver = ASTString("Solver Options"); + cli_cat.translation = ASTString("Translation Options"); + + std::vector v; + v.push_back(ti); + v.push_back(lit_true); + v.push_back(var_true); + v.push_back(lit_false); + v.push_back(var_false); + v.push_back(var_ignore); + v.push_back(absent); + v.push_back(infinity); + v.push_back(new StringLit(Location(), ids.forall)); + v.push_back(new StringLit(Location(), ids.exists)); + v.push_back(new StringLit(Location(), ids.clause)); + v.push_back(new StringLit(Location(), ids.bool2int)); + v.push_back(new StringLit(Location(), ids.int2float)); + v.push_back(new StringLit(Location(), ids.bool2float)); + v.push_back(new StringLit(Location(), ids.sum)); + v.push_back(new StringLit(Location(), ids.lin_exp)); + v.push_back(new StringLit(Location(), ids.element)); + v.push_back(new StringLit(Location(), ids.show)); + v.push_back(new StringLit(Location(), ids.output)); + v.push_back(new StringLit(Location(), ids.fix)); + + v.push_back(new StringLit(Location(), ids.int_.lin_eq)); + v.push_back(new StringLit(Location(), ids.int_.lin_le)); + v.push_back(new StringLit(Location(), ids.int_.lin_ne)); + v.push_back(new StringLit(Location(), ids.int_.plus)); + v.push_back(new StringLit(Location(), ids.int_.minus)); + v.push_back(new StringLit(Location(), ids.int_.times)); + v.push_back(new StringLit(Location(), ids.int_.div)); + v.push_back(new StringLit(Location(), ids.int_.mod)); + v.push_back(new StringLit(Location(), ids.int_.lt)); + v.push_back(new StringLit(Location(), ids.int_.le)); + v.push_back(new StringLit(Location(), ids.int_.gt)); + v.push_back(new StringLit(Location(), ids.int_.ge)); + v.push_back(new StringLit(Location(), ids.int_.eq)); + v.push_back(new StringLit(Location(), ids.int_.ne)); + + v.push_back(new StringLit(Location(), ids.int_reif.lin_eq)); + v.push_back(new StringLit(Location(), ids.int_reif.lin_le)); + v.push_back(new StringLit(Location(), ids.int_reif.lin_ne)); + v.push_back(new StringLit(Location(), ids.int_reif.plus)); + v.push_back(new StringLit(Location(), ids.int_reif.minus)); + v.push_back(new StringLit(Location(), ids.int_reif.times)); + v.push_back(new StringLit(Location(), ids.int_reif.div)); + v.push_back(new StringLit(Location(), ids.int_reif.mod)); + v.push_back(new StringLit(Location(), ids.int_reif.lt)); + v.push_back(new StringLit(Location(), ids.int_reif.le)); + v.push_back(new StringLit(Location(), ids.int_reif.gt)); + v.push_back(new StringLit(Location(), ids.int_reif.ge)); + v.push_back(new StringLit(Location(), ids.int_reif.eq)); + v.push_back(new StringLit(Location(), ids.int_reif.ne)); + + v.push_back(new StringLit(Location(), ids.float_.lin_eq)); + v.push_back(new StringLit(Location(), ids.float_.lin_le)); + v.push_back(new StringLit(Location(), ids.float_.lin_lt)); + v.push_back(new StringLit(Location(), ids.float_.lin_ne)); + v.push_back(new StringLit(Location(), ids.float_.plus)); + v.push_back(new StringLit(Location(), ids.float_.minus)); + v.push_back(new StringLit(Location(), ids.float_.times)); + v.push_back(new StringLit(Location(), ids.float_.div)); + v.push_back(new StringLit(Location(), ids.float_.mod)); + v.push_back(new StringLit(Location(), ids.float_.lt)); + v.push_back(new StringLit(Location(), ids.float_.le)); + v.push_back(new StringLit(Location(), ids.float_.gt)); + v.push_back(new StringLit(Location(), ids.float_.ge)); + v.push_back(new StringLit(Location(), ids.float_.eq)); + v.push_back(new StringLit(Location(), ids.float_.ne)); + v.push_back(new StringLit(Location(), ids.float_.in)); + v.push_back(new StringLit(Location(), ids.float_.dom)); + + v.push_back(new StringLit(Location(), ids.float_reif.lin_eq)); + v.push_back(new StringLit(Location(), ids.float_reif.lin_le)); + v.push_back(new StringLit(Location(), ids.float_reif.lin_lt)); + v.push_back(new StringLit(Location(), ids.float_reif.lin_ne)); + v.push_back(new StringLit(Location(), ids.float_reif.plus)); + v.push_back(new StringLit(Location(), ids.float_reif.minus)); + v.push_back(new StringLit(Location(), ids.float_reif.times)); + v.push_back(new StringLit(Location(), ids.float_reif.div)); + v.push_back(new StringLit(Location(), ids.float_reif.mod)); + v.push_back(new StringLit(Location(), ids.float_reif.lt)); + v.push_back(new StringLit(Location(), ids.float_reif.le)); + v.push_back(new StringLit(Location(), ids.float_reif.gt)); + v.push_back(new StringLit(Location(), ids.float_reif.ge)); + v.push_back(new StringLit(Location(), ids.float_reif.eq)); + v.push_back(new StringLit(Location(), ids.float_reif.ne)); + v.push_back(new StringLit(Location(), ids.float_reif.in)); + + v.push_back(new StringLit(Location(), ids.bool_eq)); + v.push_back(new StringLit(Location(), ids.bool_eq_reif)); + v.push_back(new StringLit(Location(), ids.bool_clause)); + v.push_back(new StringLit(Location(), ids.bool_clause_reif)); + v.push_back(new StringLit(Location(), ids.bool_xor)); + v.push_back(new StringLit(Location(), ids.array_bool_or)); + v.push_back(new StringLit(Location(), ids.array_bool_and)); + v.push_back(new StringLit(Location(), ids.set_eq)); + v.push_back(new StringLit(Location(), ids.set_in)); + v.push_back(new StringLit(Location(), ids.set_card)); + v.push_back(new StringLit(Location(), ids.pow)); + + v.push_back(new StringLit(Location(), ids.assert)); + v.push_back(new StringLit(Location(), ids.trace)); + v.push_back(new StringLit(Location(), ids.introduced_var)); + v.push_back(new StringLit(Location(), ids.anonEnumFromStrings)); + v.push_back(ctx.root); + v.push_back(ctx.pos); + v.push_back(ctx.neg); + v.push_back(ctx.mix); + v.push_back(ann.output_var); + v.push_back(ann.output_only); + v.push_back(ann.add_to_output); + v.push_back(ann.mzn_check_var); + v.push_back(new StringLit(Location(), ann.mzn_check_enum_var)); + v.push_back(new StringLit(Location(), ann.output_array)); + v.push_back(ann.is_defined_var); + v.push_back(new StringLit(Location(), ann.defines_var)); + v.push_back(ann.is_reverse_map); + v.push_back(ann.promise_total); + v.push_back(ann._export); + v.push_back(ann.maybe_partial); + v.push_back(new StringLit(Location(), ann.doc_comment)); + v.push_back(new StringLit(Location(), ann.mzn_path)); + v.push_back(new StringLit(Location(), ann.is_introduced)); + v.push_back(ann.user_cut); + v.push_back(ann.lazy_constraint); +#ifndef NDEBUG + v.push_back(ann.mzn_break_here); +#endif + v.push_back(ann.rhs_from_assignment); + v.push_back(ann.domain_change_constraint); + v.push_back(new StringLit(Location(), ann.global_register)); + + v.push_back(new StringLit(Location(), cli.cmdlineData_short_str)); + v.push_back(new StringLit(Location(), cli.cmdlineData_str)); + v.push_back(new StringLit(Location(), cli.datafile_short_str)); + v.push_back(new StringLit(Location(), cli.datafile_str)); + v.push_back(new StringLit(Location(), cli.globalsDir_alt_str)); + v.push_back(new StringLit(Location(), cli.globalsDir_short_str)); + v.push_back(new StringLit(Location(), cli.globalsDir_str)); + v.push_back(new StringLit(Location(), cli.help_short_str)); + v.push_back(new StringLit(Location(), cli.help_str)); + v.push_back(new StringLit(Location(), cli.ignoreStdlib_str)); + v.push_back(new StringLit(Location(), cli.include_str)); + v.push_back(new StringLit(Location(), cli.inputFromStdin_str)); + v.push_back(new StringLit(Location(), cli.instanceCheckOnly_str)); + v.push_back(new StringLit(Location(), cli.newfzn_str)); + v.push_back(new StringLit(Location(), cli.no_optimize_alt_str)); + v.push_back(new StringLit(Location(), cli.no_optimize_str)); + v.push_back(new StringLit(Location(), cli.no_outputOzn_short_str)); + v.push_back(new StringLit(Location(), cli.no_outputOzn_str)); + v.push_back(new StringLit(Location(), cli.no_typecheck_str)); + v.push_back(new StringLit(Location(), cli.outputBase_str)); + v.push_back(new StringLit(Location(), cli.outputFznToStdout_alt_str)); + v.push_back(new StringLit(Location(), cli.outputFznToStdout_str)); + v.push_back(new StringLit(Location(), cli.outputOznToFile_str)); + v.push_back(new StringLit(Location(), cli.outputOznToStdout_str)); + v.push_back(new StringLit(Location(), cli.outputFznToFile_alt_str)); + v.push_back(new StringLit(Location(), cli.outputFznToFile_short_str)); + v.push_back(new StringLit(Location(), cli.outputFznToFile_str)); + v.push_back(new StringLit(Location(), cli.rangeDomainsOnly_str)); + v.push_back(new StringLit(Location(), cli.statistics_short_str)); + v.push_back(new StringLit(Location(), cli.statistics_str)); + v.push_back(new StringLit(Location(), cli.stdlib_str)); + v.push_back(new StringLit(Location(), cli.verbose_short_str)); + v.push_back(new StringLit(Location(), cli.verbose_str)); + v.push_back(new StringLit(Location(), cli.version_str)); + v.push_back(new StringLit(Location(), cli.werror_str)); + + v.push_back(new StringLit(Location(), cli.solver.all_sols_str)); + v.push_back(new StringLit(Location(), cli.solver.fzn_solver_str)); + + v.push_back(new StringLit(Location(), opts.cmdlineData)); + v.push_back(new StringLit(Location(), opts.datafile)); + v.push_back(new StringLit(Location(), opts.datafiles)); + v.push_back(new StringLit(Location(), opts.fznToFile)); + v.push_back(new StringLit(Location(), opts.fznToStdout)); + v.push_back(new StringLit(Location(), opts.globalsDir)); + v.push_back(new StringLit(Location(), opts.ignoreStdlib)); + v.push_back(new StringLit(Location(), opts.includePaths)); + v.push_back(new StringLit(Location(), opts.includeDir)); + v.push_back(new StringLit(Location(), opts.inputFromStdin)); + v.push_back(new StringLit(Location(), opts.instanceCheckOnly)); + v.push_back(new StringLit(Location(), opts.model)); + v.push_back(new StringLit(Location(), opts.newfzn)); + v.push_back(new StringLit(Location(), opts.noOznOutput)); + v.push_back(new StringLit(Location(), opts.optimize)); + v.push_back(new StringLit(Location(), opts.outputBase)); + v.push_back(new StringLit(Location(), opts.oznToFile)); + v.push_back(new StringLit(Location(), opts.oznToStdout)); + v.push_back(new StringLit(Location(), opts.rangeDomainsOnly)); + v.push_back(new StringLit(Location(), opts.statistics)); + v.push_back(new StringLit(Location(), opts.stdlib)); + v.push_back(new StringLit(Location(), opts.typecheck)); + v.push_back(new StringLit(Location(), opts.verbose)); + v.push_back(new StringLit(Location(), opts.werror)); + + v.push_back(new StringLit(Location(), opts.solver.allSols)); + v.push_back(new StringLit(Location(), opts.solver.numSols)); + v.push_back(new StringLit(Location(), opts.solver.threads)); + v.push_back(new StringLit(Location(), opts.solver.fzn_solver)); + v.push_back(new StringLit(Location(), opts.solver.fzn_flags)); + v.push_back(new StringLit(Location(), opts.solver.fzn_flag)); + v.push_back(new StringLit(Location(), opts.solver.fzn_time_limit_ms)); + v.push_back(new StringLit(Location(), opts.solver.fzn_sigint)); + + v.push_back(new StringLit(Location(), cli_cat.general)); + v.push_back(new StringLit(Location(), cli_cat.io)); + v.push_back(new StringLit(Location(), cli_cat.solver)); + v.push_back(new StringLit(Location(), cli_cat.translation)); + + m = new Model(); + m->addItem(new ConstraintI(Location(), new ArrayLit(Location(), v))); + m->addItem(var_redef); +} + +const int Constants::max_array_size; + +Constants& constants(void) { + static Constants _c; + return _c; +} + +Annotation::~Annotation(void) { delete _s; } + +bool Annotation::contains(Expression* e) const { return _s && _s->contains(e); } + +bool Annotation::isEmpty(void) const { return _s == NULL || _s->isEmpty(); } + +ExpressionSetIter Annotation::begin(void) const { + return _s == NULL ? ExpressionSetIter(true) : _s->begin(); +} + +ExpressionSetIter Annotation::end(void) const { + return _s == NULL ? ExpressionSetIter(true) : _s->end(); +} + +void Annotation::add(Expression* e) { + if (_s == NULL) _s = new ExpressionSet; + if (e) _s->insert(e); +} + +void Annotation::add(std::vector e) { + if (_s == NULL) _s = new ExpressionSet; + for (unsigned int i = static_cast(e.size()); i--;) + if (e[i]) _s->insert(e[i]); +} + +void Annotation::remove(Expression* e) { + if (_s && e) { + _s->remove(e); + } +} + +void Annotation::removeCall(const ASTString& id) { + if (_s == NULL) return; + std::vector toRemove; + for (ExpressionSetIter it = _s->begin(); it != _s->end(); ++it) { + if (Call* c = (*it)->dyn_cast()) { + if (c->id() == id) toRemove.push_back(*it); + } + } + for (unsigned int i = static_cast(toRemove.size()); i--;) _s->remove(toRemove[i]); +} + +Call* Annotation::getCall(const ASTString& id) const { + if (_s == NULL) return NULL; + for (ExpressionSetIter it = _s->begin(); it != _s->end(); ++it) { + if (Call* c = (*it)->dyn_cast()) { + if (c->id() == id) return c; + } + } + return NULL; +} + +bool Annotation::containsCall(const MiniZinc::ASTString& id) const { + if (_s == NULL) return false; + for (ExpressionSetIter it = _s->begin(); it != _s->end(); ++it) { + if (Call* c = (*it)->dyn_cast()) { + if (c->id() == id) return true; + } + } + return false; +} + +void Annotation::clear(void) { + if (_s) { + _s->clear(); + } +} + +void Annotation::merge(const Annotation& ann) { + if (ann._s == NULL) return; + if (_s == NULL) { + _s = new ExpressionSet; + } + for (ExpressionSetIter it = ann.begin(); it != ann.end(); ++it) { + _s->insert(*it); + } +} + +Expression* getAnnotation(const Annotation& ann, std::string str) { + for (ExpressionSetIter i = ann.begin(); i != ann.end(); ++i) { + Expression* e = *i; + if ((e->isa() && e->cast()->str().str() == str) || + (e->isa() && e->cast()->id().str() == str)) + return e; + } + return NULL; +} +Expression* getAnnotation(const Annotation& ann, const ASTString& str) { + for (ExpressionSetIter i = ann.begin(); i != ann.end(); ++i) { + Expression* e = *i; + if ((e->isa() && e->cast()->str() == str) || + (e->isa() && e->cast()->id() == str)) + return e; + } + return NULL; +} +} // namespace MiniZinc diff --git a/lib/astexception.cpp b/lib/astexception.cpp new file mode 100644 index 0000000..78af00e --- /dev/null +++ b/lib/astexception.cpp @@ -0,0 +1,28 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +LocationException::LocationException(EnvI& env, const Location& loc, const std::string& msg) + : Exception(msg), _loc(loc) { + env.createErrorStack(); +} + +ResultUndefinedError::ResultUndefinedError(EnvI& env, const Location& loc, const std::string& msg) + : LocationException(env, loc, msg) { + if (env.in_maybe_partial == 0) + env.addWarning("undefined result becomes false in Boolean context\n (" + msg + ")"); +} + +} // namespace MiniZinc diff --git a/lib/aststring.cpp b/lib/aststring.cpp new file mode 100644 index 0000000..b5f7b15 --- /dev/null +++ b/lib/aststring.cpp @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include + +#ifndef HAS_MEMCPY_S +namespace { +void memcpy_s(char* dest, size_t, const char* src, size_t count) { memcpy(dest, src, count); } +} // namespace +#endif + +namespace MiniZinc { + +ASTStringO::ASTStringO(const std::string& s) : ASTChunk(s.size() + sizeof(size_t) + 1) { + memcpy_s(_data + sizeof(size_t), s.size() + 1, s.c_str(), s.size()); + *(_data + sizeof(size_t) + s.size()) = 0; + std::hash h; + reinterpret_cast(_data)[0] = h(s); +} + +ASTStringO* ASTStringO::a(const std::string& s) { + ASTStringO* as = static_cast(alloc(1 + sizeof(size_t) + s.size())); + new (as) ASTStringO(s); + return as; +} + +} // namespace MiniZinc diff --git a/lib/astvec.cpp b/lib/astvec.cpp new file mode 100644 index 0000000..7ab1e58 --- /dev/null +++ b/lib/astvec.cpp @@ -0,0 +1,26 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +ASTIntVecO::ASTIntVecO(const std::vector& v) : ASTChunk(sizeof(int) * v.size()) { + for (unsigned int i = static_cast(v.size()); i--;) (*this)[i] = v[i]; +} + +ASTIntVecO* ASTIntVecO::a(const std::vector& v) { + ASTIntVecO* ao = static_cast(alloc(sizeof(int) * v.size())); + new (ao) ASTIntVecO(v); + return ao; +} + +} // namespace MiniZinc \ No newline at end of file diff --git a/lib/builtins.cpp b/lib/builtins.cpp new file mode 100644 index 0000000..92c66f3 --- /dev/null +++ b/lib/builtins.cpp @@ -0,0 +1,2902 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace MiniZinc { + +void rb(EnvI& env, Model* m, const ASTString& id, const std::vector& t, + FunctionI::builtin_e b, bool fromGlobals = false) { + FunctionI* fi = m->matchFn(env, id, t, false); + if (fi) { + fi->_builtins.e = b; + } else if (!fromGlobals) { + throw InternalError("no definition found for builtin " + id.str()); + } +} +void rb(EnvI& env, Model* m, const ASTString& id, const std::vector& t, + FunctionI::builtin_f b, bool fromGlobals = false) { + FunctionI* fi = m->matchFn(env, id, t, false); + if (fi) { + fi->_builtins.f = b; + } else if (!fromGlobals) { + throw InternalError("no definition found for builtin " + id.str()); + } +} +void rb(EnvI& env, Model* m, const ASTString& id, const std::vector& t, + FunctionI::builtin_i b, bool fromGlobals = false) { + FunctionI* fi = m->matchFn(env, id, t, false); + if (fi) { + fi->_builtins.i = b; + } else if (!fromGlobals) { + throw InternalError("no definition found for builtin " + id.str()); + } +} +void rb(EnvI& env, Model* m, const ASTString& id, const std::vector& t, + FunctionI::builtin_b b, bool fromGlobals = false) { + FunctionI* fi = m->matchFn(env, id, t, false); + if (fi) { + fi->_builtins.b = b; + } else if (!fromGlobals) { + throw InternalError("no definition found for builtin " + id.str()); + } +} +void rb(EnvI& env, Model* m, const ASTString& id, const std::vector& t, + FunctionI::builtin_s b, bool fromGlobals = false) { + FunctionI* fi = m->matchFn(env, id, t, false); + if (fi) { + fi->_builtins.s = b; + } else if (!fromGlobals) { + throw InternalError("no definition found for builtin " + id.str()); + } +} +void rb(EnvI& env, Model* m, const ASTString& id, const std::vector& t, + FunctionI::builtin_str b, bool fromGlobals = false) { + FunctionI* fi = m->matchFn(env, id, t, false); + if (fi) { + fi->_builtins.str = b; + } else if (!fromGlobals) { + throw InternalError("no definition found for builtin " + id.str()); + } +} + +IntVal b_int_min(EnvI& env, Call* call) { + switch (call->n_args()) { + case 1: + if (call->arg(0)->type().is_set()) { + throw EvalError(env, call->arg(0)->loc(), "sets not supported"); + } else { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) + throw ResultUndefinedError(env, al->loc(), "minimum of empty array is undefined"); + IntVal m = eval_int(env, (*al)[0]); + for (unsigned int i = 1; i < al->size(); i++) m = std::min(m, eval_int(env, (*al)[i])); + return m; + } + case 2: { + return std::min(eval_int(env, call->arg(0)), eval_int(env, call->arg(1))); + } + default: + throw EvalError(env, Location(), "dynamic type error"); + } +} + +IntVal b_int_max(EnvI& env, Call* call) { + switch (call->n_args()) { + case 1: + if (call->arg(0)->type().is_set()) { + throw EvalError(env, call->arg(0)->loc(), "sets not supported"); + } else { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) + throw ResultUndefinedError(env, al->loc(), "maximum of empty array is undefined"); + IntVal m = eval_int(env, (*al)[0]); + for (unsigned int i = 1; i < al->size(); i++) m = std::max(m, eval_int(env, (*al)[i])); + return m; + } + case 2: { + return std::max(eval_int(env, call->arg(0)), eval_int(env, call->arg(1))); + } + default: + throw EvalError(env, Location(), "dynamic type error"); + } +} + +IntVal b_arg_min_int(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) + throw ResultUndefinedError(env, al->loc(), "argmin of empty array is undefined"); + IntVal m = eval_int(env, (*al)[0]); + int m_idx = 0; + for (unsigned int i = 1; i < al->size(); i++) { + IntVal mi = eval_int(env, (*al)[i]); + if (mi < m) { + m = mi; + m_idx = i; + } + } + return m_idx + 1; +} +IntVal b_arg_max_int(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) + throw ResultUndefinedError(env, al->loc(), "argmax of empty array is undefined"); + IntVal m = eval_int(env, (*al)[0]); + int m_idx = 0; + for (unsigned int i = 1; i < al->size(); i++) { + IntVal mi = eval_int(env, (*al)[i]); + if (mi > m) { + m = mi; + m_idx = i; + } + } + return m_idx + 1; +} +IntVal b_arg_min_float(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) + throw ResultUndefinedError(env, al->loc(), "argmin of empty array is undefined"); + FloatVal m = eval_float(env, (*al)[0]); + int m_idx = 0; + for (unsigned int i = 1; i < al->size(); i++) { + FloatVal mi = eval_float(env, (*al)[i]); + if (mi < m) { + m = mi; + m_idx = i; + } + } + return m_idx + 1; +} +IntVal b_arg_max_float(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) + throw ResultUndefinedError(env, al->loc(), "argmax of empty array is undefined"); + FloatVal m = eval_float(env, (*al)[0]); + int m_idx = 0; + for (unsigned int i = 1; i < al->size(); i++) { + FloatVal mi = eval_float(env, (*al)[i]); + if (mi > m) { + m = mi; + m_idx = i; + } + } + return m_idx + 1; +} + +IntVal b_abs_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + return std::abs(eval_int(env, call->arg(0))); +} + +FloatVal b_abs_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + return std::abs(eval_float(env, call->arg(0))); +} + +bool b_has_bounds_int(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "dynamic type error"); + IntBounds ib = compute_int_bounds(env, call->arg(0)); + return ib.valid && ib.l.isFinite() && ib.u.isFinite(); +} +bool b_has_bounds_float(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "dynamic type error"); + FloatBounds fb = compute_float_bounds(env, call->arg(0)); + return fb.valid; +} + +IntVal lb_varoptint(EnvI& env, Expression* e) { + IntBounds b = compute_int_bounds(env, e); + if (b.valid) + return b.l; + else + return -IntVal::infinity(); +} +IntVal b_lb_varoptint(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "dynamic type error"); + return lb_varoptint(env, call->arg(0)); +} + +bool b_occurs(EnvI& env, Call* call) { + GCLock lock; + return eval_par(env, call->arg(0)) != constants().absent; +} + +IntVal b_deopt_int(EnvI& env, Call* call) { + GCLock lock; + Expression* e = eval_par(env, call->arg(0)); + if (e == constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_int(env, e); +} + +bool b_deopt_bool(EnvI& env, Call* call) { + GCLock lock; + Expression* e = eval_par(env, call->arg(0)); + if (e == constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_bool(env, e); +} + +FloatVal b_deopt_float(EnvI& env, Call* call) { + GCLock lock; + Expression* e = eval_par(env, call->arg(0)); + if (e == constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_float(env, e); +} + +IntSetVal* b_deopt_intset(EnvI& env, Call* call) { + GCLock lock; + Expression* e = eval_par(env, call->arg(0)); + if (e == constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_intset(env, e); +} + +std::string b_deopt_string(EnvI& env, Call* call) { + GCLock lock; + Expression* e = eval_par(env, call->arg(0)); + if (e == constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return eval_string(env, e); +} + +Expression* b_deopt_expr(EnvI& env, Call* call) { + GCLock lock; + Expression* e = eval_par(env, call->arg(0)); + if (e == constants().absent) + throw EvalError(env, e->loc(), "cannot evaluate deopt on absent value"); + return e; +}; + +IntVal b_array_lb_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* e = follow_id_to_decl(call->arg(0)); + + bool foundMin = false; + IntVal array_lb = -IntVal::infinity(); + + if (VarDecl* vd = e->dyn_cast()) { + if (vd->ti()->domain()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, vd->ti()->domain()); + if (isv->size() != 0) { + array_lb = isv->min(); + foundMin = true; + } + } + e = vd->e(); + } + + if (e != NULL) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, e); + if (al->size() == 0) throw EvalError(env, Location(), "lower bound of empty array undefined"); + IntVal min = IntVal::infinity(); + for (unsigned int i = 0; i < al->size(); i++) { + IntBounds ib = compute_int_bounds(env, (*al)[i]); + if (!ib.valid) goto b_array_lb_int_done; + min = std::min(min, ib.l); + } + if (foundMin) + array_lb = std::max(array_lb, min); + else + array_lb = min; + foundMin = true; + } +b_array_lb_int_done: + if (foundMin) { + return array_lb; + } else { + return -IntVal::infinity(); + } +} + +IntVal ub_varoptint(EnvI& env, Expression* e) { + IntBounds b = compute_int_bounds(env, e); + if (b.valid) + return b.u; + else + return IntVal::infinity(); +} +IntVal b_ub_varoptint(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "dynamic type error"); + return ub_varoptint(env, call->arg(0)); +} + +IntVal b_array_ub_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* e = follow_id_to_decl(call->arg(0)); + + bool foundMax = false; + IntVal array_ub = IntVal::infinity(); + + if (VarDecl* vd = e->dyn_cast()) { + if (vd->ti()->domain()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, vd->ti()->domain()); + if (isv->size() != 0) { + array_ub = isv->max(); + foundMax = true; + } + } + e = vd->e(); + } + + if (e != NULL) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, e); + if (al->size() == 0) throw EvalError(env, Location(), "upper bound of empty array undefined"); + IntVal max = -IntVal::infinity(); + for (unsigned int i = 0; i < al->size(); i++) { + IntBounds ib = compute_int_bounds(env, (*al)[i]); + if (!ib.valid) goto b_array_ub_int_done; + max = std::max(max, ib.u); + } + if (foundMax) + array_ub = std::min(array_ub, max); + else + array_ub = max; + foundMax = true; + } +b_array_ub_int_done: + if (foundMax) { + return array_ub; + } else { + return IntVal::infinity(); + } +} + +IntVal b_sum_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) return 0; + IntVal m = 0; + for (unsigned int i = 0; i < al->size(); i++) m += eval_int(env, (*al)[i]); + return m; +} + +IntVal b_product_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) return 1; + IntVal m = 1; + for (unsigned int i = 0; i < al->size(); i++) m *= eval_int(env, (*al)[i]); + return m; +} + +FloatVal b_product_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) return 1; + FloatVal m = 1.0; + for (unsigned int i = 0; i < al->size(); i++) m *= eval_float(env, (*al)[i]); + return m; +} + +FloatVal lb_varoptfloat(EnvI& env, Expression* e) { + FloatBounds b = compute_float_bounds(env, e); + if (b.valid) + return b.l; + else + throw EvalError(env, e->loc(), "cannot determine bounds"); +} +FloatVal ub_varoptfloat(EnvI& env, Expression* e) { + FloatBounds b = compute_float_bounds(env, e); + if (b.valid) + return b.u; + else + throw EvalError(env, e->loc(), "cannot determine bounds"); +} + +FloatVal b_lb_varoptfloat(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "dynamic type error"); + return lb_varoptfloat(env, call->arg(0)); +} +FloatVal b_ub_varoptfloat(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "dynamic type error"); + return ub_varoptfloat(env, call->arg(0)); +} + +FloatVal b_array_lb_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* e = follow_id_to_decl(call->arg(0)); + + bool foundMin = false; + FloatVal array_lb = 0.0; + + if (VarDecl* vd = e->dyn_cast()) { + if (vd->ti()->domain()) { + FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); + array_lb = fsv->min(); + foundMin = true; + } + e = vd->e(); + } + + if (e != NULL) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, e); + if (al->size() == 0) throw EvalError(env, Location(), "lower bound of empty array undefined"); + bool min_valid = false; + FloatVal min = 0.0; + for (unsigned int i = 0; i < al->size(); i++) { + FloatBounds fb = compute_float_bounds(env, (*al)[i]); + if (!fb.valid) goto b_array_lb_float_done; + if (min_valid) { + min = std::min(min, fb.l); + } else { + min_valid = true; + min = fb.l; + } + } + assert(min_valid); + if (foundMin) + array_lb = std::max(array_lb, min); + else + array_lb = min; + foundMin = true; + } +b_array_lb_float_done: + if (foundMin) { + return array_lb; + } else { + throw EvalError(env, e->loc(), "cannot determine lower bound"); + } +} + +FloatVal b_array_ub_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* e = follow_id_to_decl(call->arg(0)); + + bool foundMax = false; + FloatVal array_ub = 0.0; + + if (VarDecl* vd = e->dyn_cast()) { + if (vd->ti()->domain()) { + FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); + array_ub = fsv->max(); + foundMax = true; + } + e = vd->e(); + } + + if (e != NULL) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, e); + if (al->size() == 0) throw EvalError(env, Location(), "upper bound of empty array undefined"); + bool max_valid = false; + FloatVal max = 0.0; + for (unsigned int i = 0; i < al->size(); i++) { + FloatBounds fb = compute_float_bounds(env, (*al)[i]); + if (!fb.valid) goto b_array_ub_float_done; + if (max_valid) { + max = std::max(max, fb.u); + } else { + max_valid = true; + max = fb.u; + } + } + assert(max_valid); + if (foundMax) + array_ub = std::min(array_ub, max); + else + array_ub = max; + foundMax = true; + } +b_array_ub_float_done: + if (foundMax) { + return array_ub; + } else { + throw EvalError(env, e->loc(), "cannot determine upper bound"); + } +} + +FloatVal b_sum_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) return 0; + FloatVal m = 0; + for (unsigned int i = 0; i < al->size(); i++) m += eval_float(env, (*al)[i]); + return m; +} + +FloatVal b_float_min(EnvI& env, Call* call) { + switch (call->n_args()) { + case 1: + if (call->arg(0)->type().is_set()) { + throw EvalError(env, call->arg(0)->loc(), "sets not supported"); + } else { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) throw EvalError(env, al->loc(), "min on empty array undefined"); + FloatVal m = eval_float(env, (*al)[0]); + for (unsigned int i = 1; i < al->size(); i++) m = std::min(m, eval_float(env, (*al)[i])); + return m; + } + case 2: { + return std::min(eval_float(env, call->arg(0)), eval_float(env, call->arg(1))); + } + default: + throw EvalError(env, Location(), "dynamic type error"); + } +} + +FloatVal b_float_max(EnvI& env, Call* call) { + switch (call->n_args()) { + case 1: + if (call->arg(0)->type().is_set()) { + throw EvalError(env, call->arg(0)->loc(), "sets not supported"); + } else { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) throw EvalError(env, al->loc(), "max on empty array undefined"); + FloatVal m = eval_float(env, (*al)[0]); + for (unsigned int i = 1; i < al->size(); i++) m = std::max(m, eval_float(env, (*al)[i])); + return m; + } + case 2: { + return std::max(eval_float(env, call->arg(0)), eval_float(env, call->arg(1))); + } + default: + throw EvalError(env, Location(), "dynamic type error"); + } +} + +IntSetVal* b_index_set(EnvI& env, Expression* e, int i) { + if (e->eid() != Expression::E_ID) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, e); + if (al->dims() < i) throw EvalError(env, e->loc(), "index_set: wrong dimension"); + return IntSetVal::a(al->min(i - 1), al->max(i - 1)); + } + Id* id = e->cast(); + if (id->decl() == NULL) throw EvalError(env, id->loc(), "undefined identifier"); + if ((id->decl()->ti()->ranges().size() == 1 && id->decl()->ti()->ranges()[0]->domain() != NULL && + id->decl()->ti()->ranges()[0]->domain()->isa()) || + (static_cast(id->decl()->ti()->ranges().size()) >= i && + (id->decl()->ti()->ranges()[i - 1]->domain() == NULL || + id->decl()->ti()->ranges()[i - 1]->domain()->isa()))) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, id); + if (al->dims() < i) throw EvalError(env, id->loc(), "index_set: wrong dimension"); + return IntSetVal::a(al->min(i - 1), al->max(i - 1)); + } + if (static_cast(id->decl()->ti()->ranges().size()) < i) + throw EvalError(env, id->loc(), "index_set: wrong dimension"); + return eval_intset(env, id->decl()->ti()->ranges()[i - 1]->domain()); +} +bool b_index_sets_agree(EnvI& env, Call* call) { + if (call->n_args() != 2) + throw EvalError(env, Location(), "index_sets_agree needs exactly two arguments"); + GCLock lock; + ArrayLit* al0 = eval_array_lit(env, call->arg(0)); + ArrayLit* al1 = eval_array_lit(env, call->arg(1)); + if (al0->type().dim() != al1->type().dim()) return false; + for (int i = 1; i <= al0->type().dim(); i++) { + IntSetVal* index0 = b_index_set(env, al0, i); + IntSetVal* index1 = b_index_set(env, al1, i); + if (!index0->equal(index1)) return false; + } + return true; +} +IntSetVal* b_index_set1(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "index_set needs exactly one argument"); + return b_index_set(env, call->arg(0), 1); +} +IntSetVal* b_index_set2(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "index_set needs exactly one argument"); + return b_index_set(env, call->arg(0), 2); +} +IntSetVal* b_index_set3(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "index_set needs exactly one argument"); + return b_index_set(env, call->arg(0), 3); +} +IntSetVal* b_index_set4(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "index_set needs exactly one argument"); + return b_index_set(env, call->arg(0), 4); +} +IntSetVal* b_index_set5(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "index_set needs exactly one argument"); + return b_index_set(env, call->arg(0), 5); +} +IntSetVal* b_index_set6(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "index_set needs exactly one argument"); + return b_index_set(env, call->arg(0), 6); +} + +IntVal b_min_parsetint(EnvI& env, Call* call) { + assert(call->n_args() == 1); + IntSetVal* isv = eval_intset(env, call->arg(0)); + return isv->min(); +} +IntVal b_max_parsetint(EnvI& env, Call* call) { + assert(call->n_args() == 1); + IntSetVal* isv = eval_intset(env, call->arg(0)); + return isv->max(); +} +IntSetVal* b_lb_set(EnvI& env, Call* e) { + Expression* ee = eval_par(env, e->arg(0)); + if (ee->type().ispar()) { + return eval_intset(env, ee); + } + return IntSetVal::a(); +} +IntSetVal* b_ub_set(EnvI& env, Expression* e) { + IntSetVal* isv = compute_intset_bounds(env, e); + if (isv) return isv; + throw EvalError(env, e->loc(), "cannot determine bounds of set expression"); +} +IntSetVal* b_ub_set(EnvI& env, Call* call) { + assert(call->n_args() == 1); + return b_ub_set(env, call->arg(0)); +} +bool b_has_ub_set(EnvI& env, Call* call) { + Expression* e = call->arg(0); + for (;;) { + switch (e->eid()) { + case Expression::E_SETLIT: + return true; + case Expression::E_ID: { + Id* id = e->cast(); + if (id->decl() == NULL) throw EvalError(env, id->loc(), "undefined identifier"); + if (id->decl()->e() == NULL) + return id->decl()->ti()->domain() != NULL; + else + e = id->decl()->e(); + } break; + default: + throw EvalError(env, e->loc(), "invalid argument to has_ub_set"); + } + } +} + +IntSetVal* b_array_ub_set(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) throw EvalError(env, Location(), "upper bound of empty array undefined"); + IntSetVal* ub = b_ub_set(env, (*al)[0]); + for (unsigned int i = 1; i < al->size(); i++) { + IntSetRanges isr(ub); + IntSetRanges r(b_ub_set(env, (*al)[i])); + Ranges::Union u(isr, r); + ub = IntSetVal::ai(u); + } + return ub; +} + +IntSetVal* b_dom_varint(EnvI& env, Expression* e) { + Id* lastid = NULL; + Expression* cur = e; + for (;;) { + if (cur == NULL) { + if (lastid == NULL || lastid->decl()->ti()->domain() == NULL) { + IntBounds b = compute_int_bounds(env, e); + if (b.valid) + return IntSetVal::a(b.l, b.u); + else + return IntSetVal::a(-IntVal::infinity(), IntVal::infinity()); + } else { + return eval_intset(env, lastid->decl()->ti()->domain()); + } + } + switch (cur->eid()) { + case Expression::E_INTLIT: { + IntVal v = cur->cast()->v(); + return IntSetVal::a(v, v); + } + case Expression::E_ID: { + lastid = cur->cast(); + if (lastid->decl() == NULL) throw EvalError(env, lastid->loc(), "undefined identifier"); + cur = lastid->decl()->e(); + } break; + case Expression::E_ARRAYACCESS: { + bool success; + cur = eval_arrayaccess(env, cur->cast(), success); + if (!success) { + cur = NULL; + } + } break; + default: + cur = NULL; + break; + } + } +} +IntSetVal* b_dom_varint(EnvI& env, Call* call) { + assert(call->n_args() == 1); + return b_dom_varint(env, call->arg(0)); +} + +IntSetVal* b_dom_bounds_array(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* arg_e = call->arg(0); + Expression* e = follow_id_to_decl(arg_e); + + bool foundBounds = false; + IntVal array_lb = -IntVal::infinity(); + IntVal array_ub = IntVal::infinity(); + + if (VarDecl* vd = e->dyn_cast()) { + if (vd->ti()->domain()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, vd->ti()->domain()); + if (isv->size() != 0) { + array_lb = isv->min(); + array_ub = isv->max(); + foundBounds = true; + } + } + e = vd->e(); + if (e == NULL) e = vd->flat()->e(); + } + + if (foundBounds) { + return IntSetVal::a(array_lb, array_ub); + } + + if (e != NULL) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, e); + if (al->size() == 0) throw EvalError(env, Location(), "lower bound of empty array undefined"); + IntVal min = IntVal::infinity(); + IntVal max = -IntVal::infinity(); + for (unsigned int i = 0; i < al->size(); i++) { + IntBounds ib = compute_int_bounds(env, (*al)[i]); + if (!ib.valid) goto b_array_lb_int_done; + min = std::min(min, ib.l); + max = std::max(max, ib.u); + } + array_lb = std::max(array_lb, min); + array_ub = std::min(array_ub, max); + foundBounds = true; + } +b_array_lb_int_done: + if (foundBounds) { + return IntSetVal::a(array_lb, array_ub); + } else { + throw EvalError(env, e->loc(), "cannot determine lower bound"); + } +} + +IntSetVal* b_dom_array(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* ae = call->arg(0); + ArrayLit* al = NULL; + while (al == NULL) { + switch (ae->eid()) { + case Expression::E_ARRAYLIT: + al = ae->cast(); + break; + case Expression::E_ID: { + Id* id = ae->cast(); + if (id->decl() == NULL) throw EvalError(env, id->loc(), "undefined identifier"); + if (id->decl()->e() == NULL) { + if (id->decl()->flat() == NULL) { + throw EvalError(env, id->loc(), "array without initialiser"); + } else { + if (id->decl()->flat()->e() == NULL) { + throw EvalError(env, id->loc(), "array without initialiser"); + } + ae = id->decl()->flat()->e(); + } + } else { + ae = id->decl()->e(); + } + } break; + default: + throw EvalError(env, ae->loc(), "invalid argument to dom"); + } + } + if (al->size() == 0) return IntSetVal::a(); + IntSetVal* isv = b_dom_varint(env, (*al)[0]); + for (unsigned int i = 1; i < al->size(); i++) { + IntSetRanges isr(isv); + IntSetRanges r(b_dom_varint(env, (*al)[i])); + Ranges::Union u(isr, r); + isv = IntSetVal::ai(u); + } + return isv; +} +IntSetVal* b_compute_div_bounds(EnvI& env, Call* call) { + assert(call->n_args() == 2); + IntBounds bx = compute_int_bounds(env, call->arg(0)); + if (!bx.valid) throw EvalError(env, call->arg(0)->loc(), "cannot determine bounds"); + /// TODO: better bounds if only some input bounds are infinite + if (!bx.l.isFinite() || !bx.u.isFinite()) return constants().infinity->isv(); + IntBounds by = compute_int_bounds(env, call->arg(1)); + if (!by.valid) throw EvalError(env, call->arg(1)->loc(), "cannot determine bounds"); + if (!by.l.isFinite() || !by.u.isFinite()) return constants().infinity->isv(); + Ranges::Const byr(by.l, by.u); + Ranges::Const by0(0, 0); + Ranges::Diff, Ranges::Const> byr0(byr, by0); + + IntVal min = IntVal::maxint(); + IntVal max = IntVal::minint(); + if (byr0()) { + min = std::min(min, bx.l / byr0.min()); + min = std::min(min, bx.l / byr0.max()); + min = std::min(min, bx.u / byr0.min()); + min = std::min(min, bx.u / byr0.max()); + max = std::max(max, bx.l / byr0.min()); + max = std::max(max, bx.l / byr0.max()); + max = std::max(max, bx.u / byr0.min()); + max = std::max(max, bx.u / byr0.max()); + ++byr0; + if (byr0()) { + min = std::min(min, bx.l / byr0.min()); + min = std::min(min, bx.l / byr0.max()); + min = std::min(min, bx.u / byr0.min()); + min = std::min(min, bx.u / byr0.max()); + max = std::max(max, bx.l / byr0.min()); + max = std::max(max, bx.l / byr0.max()); + max = std::max(max, bx.u / byr0.min()); + max = std::max(max, bx.u / byr0.max()); + } + } + return IntSetVal::a(min, max); +} + +ArrayLit* b_arrayXd(EnvI& env, Call* call, int d) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(d)); + std::vector> dims(d); + unsigned int dim1d = 1; + for (int i = 0; i < d; i++) { + IntSetVal* di = eval_intset(env, call->arg(i)); + if (di->size() == 0) { + dims[i] = std::pair(1, 0); + dim1d = 0; + } else if (di->size() != 1) { + throw EvalError(env, call->arg(i)->loc(), "arrayXd only defined for ranges"); + } else { + dims[i] = std::pair(static_cast(di->min(0).toInt()), + static_cast(di->max(0).toInt())); + dim1d *= dims[i].second - dims[i].first + 1; + } + } + if (dim1d != al->size()) throw EvalError(env, al->loc(), "mismatch in array dimensions"); + ArrayLit* ret = new ArrayLit(al->loc(), *al, dims); + Type t = al->type(); + t.dim(d); + ret->type(t); + ret->flat(al->flat()); + return ret; +} +Expression* b_array1d_list(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->dims() == 1 && al->min(0) == 1) { + return call->arg(0)->isa() ? call->arg(0) : al; + } + ArrayLit* ret = new ArrayLit(al->loc(), *al); + Type t = al->type(); + t.dim(1); + ret->type(t); + ret->flat(al->flat()); + return ret; +} +Expression* b_array1d(EnvI& env, Call* call) { return b_arrayXd(env, call, 1); } +Expression* b_array2d(EnvI& env, Call* call) { return b_arrayXd(env, call, 2); } +Expression* b_array3d(EnvI& env, Call* call) { return b_arrayXd(env, call, 3); } +Expression* b_array4d(EnvI& env, Call* call) { return b_arrayXd(env, call, 4); } +Expression* b_array5d(EnvI& env, Call* call) { return b_arrayXd(env, call, 5); } +Expression* b_array6d(EnvI& env, Call* call) { return b_arrayXd(env, call, 6); } + +Expression* b_arrayXd(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al0 = eval_array_lit(env, call->arg(0)); + ArrayLit* al1 = eval_array_lit(env, call->arg(1)); + if (al0->dims() == al1->dims()) { + bool sameDims = true; + for (unsigned int i = al0->dims(); i--;) { + if (al0->min(i) != al1->min(i) || al0->max(i) != al1->max(i)) { + sameDims = false; + break; + } + } + if (sameDims) return call->arg(1)->isa() ? call->arg(1) : al1; + } + std::vector> dims(al0->dims()); + for (unsigned int i = al0->dims(); i--;) { + dims[i] = std::make_pair(al0->min(i), al0->max(i)); + } + ArrayLit* ret = new ArrayLit(al1->loc(), *al1, dims); + Type t = al1->type(); + t.dim(static_cast(dims.size())); + ret->type(t); + ret->flat(al1->flat()); + return ret; +} + +IntVal b_length(EnvI& env, Call* call) { + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + return al->size(); +} + +IntVal b_bool2int(EnvI& env, Call* call) { return eval_bool(env, call->arg(0)) ? 1 : 0; } + +bool b_forall_par(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "forall needs exactly one argument"); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + for (unsigned int i = al->size(); i--;) + if (!eval_bool(env, (*al)[i])) return false; + return true; +} +bool b_exists_par(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "exists needs exactly one argument"); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + for (unsigned int i = al->size(); i--;) + if (eval_bool(env, (*al)[i])) return true; + return false; +} +bool b_clause_par(EnvI& env, Call* call) { + if (call->n_args() != 2) throw EvalError(env, Location(), "clause needs exactly two arguments"); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + for (unsigned int i = al->size(); i--;) + if (eval_bool(env, (*al)[i])) return true; + al = eval_array_lit(env, call->arg(1)); + for (unsigned int i = al->size(); i--;) + if (!eval_bool(env, (*al)[i])) return true; + return false; +} +bool b_xorall_par(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "xorall needs exactly one argument"); + GCLock lock; + int count = 0; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + for (unsigned int i = al->size(); i--;) count += eval_bool(env, (*al)[i]); + return count % 2 == 1; +} +bool b_iffall_par(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "xorall needs exactly one argument"); + GCLock lock; + int count = 0; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + for (unsigned int i = al->size(); i--;) count += eval_bool(env, (*al)[i]); + return count % 2 == 0; +} + +IntVal b_card(EnvI& env, Call* call) { + if (call->n_args() != 1) throw EvalError(env, Location(), "card needs exactly one argument"); + IntSetVal* isv = eval_intset(env, call->arg(0)); + IntSetRanges isr(isv); + return Ranges::cardinality(isr); +} + +Expression* exp_is_fixed(EnvI& env, Expression* e) { + GCLock lock; + Expression* cur = eval_par(env, e); + for (;;) { + if (cur == NULL) return NULL; + if (cur->type().ispar()) return cur; + switch (cur->eid()) { + case Expression::E_ID: + cur = cur->cast()->decl(); + break; + case Expression::E_VARDECL: + if (cur->type().st() != Type::ST_SET) { + Expression* dom = cur->cast()->ti()->domain(); + if (dom && (dom->isa() || dom->isa() || dom->isa())) + return dom; + } + cur = cur->cast()->e(); + break; + default: + return NULL; + } + } +} + +bool b_is_fixed(EnvI& env, Call* call) { + assert(call->n_args() == 1); + return exp_is_fixed(env, call->arg(0)) != NULL; +} + +bool b_is_fixed_array(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) return true; + for (unsigned int i = 0; i < al->size(); i++) { + if (exp_is_fixed(env, (*al)[i]) == NULL) return false; + } + return true; +} + +Expression* b_fix(EnvI& env, Call* call) { + assert(call->n_args() == 1); + Expression* ret = exp_is_fixed(env, call->arg(0)); + if (ret == NULL) throw EvalError(env, call->arg(0)->loc(), "expression is not fixed"); + return ret; +} + +IntVal b_fix_int(EnvI& env, Call* call) { return eval_int(env, b_fix(env, call)); } +bool b_fix_bool(EnvI& env, Call* call) { return eval_bool(env, b_fix(env, call)); } +FloatVal b_fix_float(EnvI& env, Call* call) { return eval_float(env, b_fix(env, call)); } +IntSetVal* b_fix_set(EnvI& env, Call* call) { return eval_intset(env, b_fix(env, call)); } + +Expression* b_fix_array(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + std::vector fixed(al->size()); + for (unsigned int i = 0; i < fixed.size(); i++) { + fixed[i] = exp_is_fixed(env, (*al)[i]); + if (fixed[i] == NULL) throw EvalError(env, (*al)[i]->loc(), "expression is not fixed"); + } + ArrayLit* ret = new ArrayLit(Location(), fixed); + Type tt = al->type(); + tt.ti(Type::TI_PAR); + ret->type(tt); + return ret; +} + +FloatVal b_int2float(EnvI& env, Call* call) { return eval_int(env, call->arg(0)); } +IntVal b_ceil(EnvI& env, Call* call) { + return static_cast(std::ceil(eval_float(env, call->arg(0)))); +} +IntVal b_floor(EnvI& env, Call* call) { + return static_cast(std::floor(eval_float(env, call->arg(0)))); +} +IntVal b_round(EnvI& env, Call* call) { + FloatVal v(eval_float(env, call->arg(0))); + return v < 0 ? static_cast(v - 0.5) : static_cast(v + 0.5); + // return static_cast(eval_float(env,call->arg(0))+0.5); +} +FloatVal b_log10(EnvI& env, Call* call) { + return std::log10(eval_float(env, call->arg(0)).toDouble()); +} +FloatVal b_log2(EnvI& env, Call* call) { + return std::log(eval_float(env, call->arg(0)).toDouble()) / std::log(2.0); +} +FloatVal b_ln(EnvI& env, Call* call) { return std::log(eval_float(env, call->arg(0)).toDouble()); } +FloatVal b_log(EnvI& env, Call* call) { + return std::log(eval_float(env, call->arg(1)).toDouble()) / + std::log(eval_float(env, call->arg(0)).toDouble()); +} +FloatVal b_exp(EnvI& env, Call* call) { return std::exp(eval_float(env, call->arg(0)).toDouble()); } +FloatVal b_pow(EnvI& env, Call* call) { + return std::pow(eval_float(env, call->arg(0)).toDouble(), + eval_float(env, call->arg(1)).toDouble()); +} +IntVal b_pow_int(EnvI& env, Call* call) { + IntVal p = eval_int(env, call->arg(0)); + IntVal r = 1; + long long int e = eval_int(env, call->arg(1)).toInt(); + if (e < 0) throw EvalError(env, call->arg(1)->loc(), "Cannot raise integer to a negative power"); + for (long long int i = e; i--;) r = r * p; + return r; +} +FloatVal b_sqrt(EnvI& env, Call* call) { + return std::sqrt(eval_float(env, call->arg(0)).toDouble()); +} + +bool b_assert_bool(EnvI& env, Call* call) { + assert(call->n_args() == 2); + GCLock lock; + if (eval_bool(env, call->arg(0))) return true; + StringLit* err = eval_par(env, call->arg(1))->cast(); + throw EvalError(env, call->arg(0)->loc(), "Assertion failed: " + err->v().str()); +} + +Expression* b_assert(EnvI& env, Call* call) { + assert(call->n_args() == 3); + GCLock lock; + if (eval_bool(env, call->arg(0))) return call->arg(2); + StringLit* err = eval_par(env, call->arg(1))->cast(); + throw EvalError(env, call->arg(0)->loc(), "Assertion failed: " + err->v().str()); +} + +bool b_abort(EnvI& env, Call* call) { + GCLock lock; + StringLit* err = eval_par(env, call->arg(0))->cast(); + throw EvalError(env, call->arg(0)->loc(), "Abort: " + err->v().str()); +} + +Expression* b_trace(EnvI& env, Call* call) { + GCLock lock; + StringLit* msg = eval_par(env, call->arg(0))->cast(); + env.errstream << msg->v(); + return call->n_args() == 1 ? constants().lit_true : call->arg(1); +} + +Expression* b_trace_stdout(EnvI& env, Call* call) { + GCLock lock; + StringLit* msg = eval_par(env, call->arg(0))->cast(); + env.outstream << msg->v(); + return call->n_args() == 1 ? constants().lit_true : call->arg(1); +} + +bool b_in_redundant_constraint(EnvI& env, Call*) { return env.in_redundant_constraint > 0; } + +Expression* b_set2array(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + IntSetVal* isv = eval_intset(env, call->arg(0)); + std::vector elems; + IntSetRanges isr(isv); + for (Ranges::ToValues isr_v(isr); isr_v(); ++isr_v) + elems.push_back(IntLit::a(isr_v.val())); + ArrayLit* al = new ArrayLit(call->arg(0)->loc(), elems); + al->type(Type::parint(1)); + return al; +} + +IntVal b_string_length(EnvI& env, Call* call) { + GCLock lock; + std::string s = eval_string(env, call->arg(0)); + return s.size(); +} + +std::string show(EnvI& env, Expression* exp) { + std::ostringstream oss; + GCLock lock; + Printer p(oss, 0, false); + Expression* e = eval_par(env, exp); + if (e->type().isvar()) { + p.print(e); + } else { + e = eval_par(env, e); + if (ArrayLit* al = e->dyn_cast()) { + oss << "["; + for (unsigned int i = 0; i < al->size(); i++) { + p.print((*al)[i]); + if (i < al->size() - 1) oss << ", "; + } + oss << "]"; + } else { + p.print(e); + } + } + return oss.str(); +} +std::string b_show(EnvI& env, Call* call) { return show(env, call->arg(0)); } +std::string b_showDznId(EnvI& env, Call* call) { + GCLock lock; + std::string s = eval_string(env, call->arg(0)); + size_t nonIdChar = + s.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"); + size_t nonIdBegin = s.find_first_of("0123456789_"); + if (nonIdChar != std::string::npos || nonIdBegin == 0) { + s = "'" + s + "'"; + } + return s; +} + +std::string b_show_json_basic(EnvI& env, Expression* e) { + std::ostringstream oss; + Printer p(oss, 0, false); + if (SetLit* sl = e->dyn_cast()) { + oss << "{ \"set\" : ["; + if (IntSetVal* isv = sl->isv()) { + bool first = true; + for (IntSetRanges isr(isv); isr(); ++isr) { + if (first) { + first = false; + } else { + oss << ","; + } + if (isr.min() == isr.max()) { + oss << isr.min(); + } else { + oss << "[" << isr.min() << "," << isr.max() << "]"; + } + } + } else if (FloatSetVal* fsv = sl->fsv()) { + bool first = true; + for (FloatSetRanges fsr(fsv); fsr(); ++fsr) { + if (first) { + first = false; + } else { + oss << ","; + } + if (fsr.min() == fsr.max()) { + ppFloatVal(oss, fsr.min()); + } else { + oss << "["; + ppFloatVal(oss, fsr.min()); + oss << ","; + ppFloatVal(oss, fsr.max()); + oss << "]"; + } + } + } else { + for (unsigned int i = 0; i < sl->v().size(); i++) { + p.print(sl->v()[i]); + if (i < sl->v().size() - 1) oss << ","; + } + } + oss << "]}"; + } else if (e == constants().absent) { + oss << "null"; + } else { + p.print(e); + } + return oss.str(); +} + +std::string b_show_json(EnvI& env, Call* call) { + Expression* exp = call->arg(0); + GCLock lock; + Expression* e = eval_par(env, exp); + if (e->type().isvar()) { + std::ostringstream oss; + Printer p(oss, 0, false); + p.print(e); + return oss.str(); + } else { + if (ArrayLit* al = e->dyn_cast()) { + std::vector dims(al->dims() - 1); + if (dims.size() != 0) { + dims[0] = al->max(al->dims() - 1) - al->min(al->dims() - 1) + 1; + } + + for (int i = 1; i < al->dims() - 1; i++) { + dims[i] = dims[i - 1] * (al->max(al->dims() - 1 - i) - al->min(al->dims() - 1 - i) + 1); + } + + std::ostringstream oss; + oss << "["; + for (unsigned int i = 0; i < al->size(); i++) { + for (unsigned int j = 0; j < dims.size(); j++) { + if (i % dims[j] == 0) { + oss << "["; + } + } + oss << b_show_json_basic(env, (*al)[i]); + for (unsigned int j = 0; j < dims.size(); j++) { + if (i % dims[j] == dims[j] - 1) { + oss << "]"; + } + } + + if (i < al->size() - 1) oss << ", "; + } + oss << "]"; + + return oss.str(); + } else { + return b_show_json_basic(env, e); + } + } +} + +Expression* b_outputJSON(EnvI& env, Call* call) { return createJSONOutput(env, false, false); } +Expression* b_outputJSONParameters(EnvI& env, Call* call) { + std::vector outputVars; + outputVars.push_back(new StringLit(Location().introduce(), "{\n")); + + class JSONParVisitor : public ItemVisitor { + protected: + EnvI& e; + std::vector& outputVars; + bool first_var; + + public: + JSONParVisitor(EnvI& e0, std::vector& outputVars0) + : e(e0), outputVars(outputVars0), first_var(true) {} + void vVarDeclI(VarDeclI* vdi) { + VarDecl* vd = vdi->e(); + if (vd->ann().contains(constants().ann.rhs_from_assignment)) { + std::ostringstream s; + if (first_var) { + first_var = false; + } else { + s << ",\n"; + } + s << " \"" << vd->id()->str().str() << "\"" + << " : "; + StringLit* sl = new StringLit(Location().introduce(), s.str()); + outputVars.push_back(sl); + + std::vector showArgs(1); + showArgs[0] = vd->id(); + Call* show = new Call(Location().introduce(), "showJSON", showArgs); + show->type(Type::parstring()); + FunctionI* fi = e.model->matchFn(e, show, false); + assert(fi); + show->decl(fi); + outputVars.push_back(show); + } + } + } jsonov(env, outputVars); + + iterItems(jsonov, env.model); + outputVars.push_back(new StringLit(Location().introduce(), "\n}\n")); + return new ArrayLit(Location().introduce(), outputVars); +} + +std::string b_format(EnvI& env, Call* call) { + int width = 0; + int prec = -1; + GCLock lock; + Expression* e; + if (call->n_args() > 1) { + width = static_cast(eval_int(env, call->arg(0)).toInt()); + if (call->n_args() == 2) { + e = eval_par(env, call->arg(1)); + } else { + assert(call->n_args() == 3); + prec = static_cast(eval_int(env, call->arg(1)).toInt()); + if (prec < 0) + throw EvalError(env, call->arg(1)->loc(), "output precision cannot be negative"); + e = eval_par(env, call->arg(2)); + } + } else { + e = eval_par(env, call->arg(0)); + } + if (e->type() == Type::parint()) { + long long int i = eval_int(env, e).toInt(); + std::ostringstream formatted; + if (width > 0) { + formatted.width(width); + } else if (width < 0) { + formatted.width(-width); + formatted.flags(std::ios::left); + } + if (prec != -1) formatted.precision(prec); + formatted << i; + return formatted.str(); + } else if (e->type() == Type::parfloat()) { + FloatVal i = eval_float(env, e); + std::ostringstream formatted; + if (width > 0) { + formatted.width(width); + } else if (width < 0) { + formatted.width(-width); + formatted.flags(std::ios::left); + } + formatted.setf(std::ios::fixed); + formatted.precision(std::numeric_limits::digits10 + 2); + if (prec != -1) formatted.precision(prec); + formatted << i; + return formatted.str(); + } else { + std::string s = show(env, e); + if (prec >= 0 && prec < s.size()) s = s.substr(0, prec); + std::ostringstream oss; + if (s.size() < std::abs(width)) { + int addLeft = width < 0 ? 0 : (width - static_cast(s.size())); + if (addLeft < 0) addLeft = 0; + int addRight = width < 0 ? (-width - static_cast(s.size())) : 0; + if (addRight < 0) addRight = 0; + for (int i = addLeft; i--;) oss << " "; + oss << s; + for (int i = addRight; i--;) oss << " "; + return oss.str(); + } else { + return s; + } + } +} + +std::string b_format_justify_string(EnvI& env, Call* call) { + int width = 0; + GCLock lock; + Expression* e; + width = static_cast(eval_int(env, call->arg(0)).toInt()); + e = eval_par(env, call->arg(1)); + std::string s = eval_string(env, e); + std::ostringstream oss; + if (s.size() < std::abs(width)) { + int addLeft = width < 0 ? 0 : (width - static_cast(s.size())); + if (addLeft < 0) addLeft = 0; + int addRight = width < 0 ? (-width - static_cast(s.size())) : 0; + if (addRight < 0) addRight = 0; + for (int i = addLeft; i--;) oss << " "; + oss << s; + for (int i = addRight; i--;) oss << " "; + return oss.str(); + } else { + return s; + } +} + +std::string b_show_int(EnvI& env, Call* call) { + assert(call->n_args() == 2); + GCLock lock; + Expression* e = eval_par(env, call->arg(1)); + std::ostringstream oss; + if (IntLit* iv = e->dyn_cast()) { + int justify = static_cast(eval_int(env, call->arg(0)).toInt()); + std::ostringstream oss_length; + oss_length << iv->v(); + int iv_length = static_cast(oss_length.str().size()); + int addLeft = justify < 0 ? 0 : (justify - iv_length); + if (addLeft < 0) addLeft = 0; + int addRight = justify < 0 ? (-justify - iv_length) : 0; + if (addRight < 0) addRight = 0; + for (int i = addLeft; i--;) oss << " "; + oss << iv->v(); + for (int i = addRight; i--;) oss << " "; + } else { + Printer p(oss, 0, false); + p.print(e); + } + return oss.str(); +} + +std::string b_show_float(EnvI& env, Call* call) { + assert(call->n_args() == 3); + GCLock lock; + Expression* e = eval_par(env, call->arg(2)); + std::ostringstream oss; + if (FloatLit* fv = e->dyn_cast()) { + int justify = static_cast(eval_int(env, call->arg(0)).toInt()); + int prec = static_cast(eval_int(env, call->arg(1)).toInt()); + if (prec < 0) + throw EvalError(env, call->arg(1)->loc(), + "number of digits in show_float cannot be negative"); + std::ostringstream oss_length; + oss_length << std::setprecision(prec) << std::fixed << fv->v(); + int fv_length = static_cast(oss_length.str().size()); + int addLeft = justify < 0 ? 0 : (justify - fv_length); + if (addLeft < 0) addLeft = 0; + int addRight = justify < 0 ? (-justify - fv_length) : 0; + if (addRight < 0) addRight = 0; + for (int i = addLeft; i--;) oss << " "; + oss << std::setprecision(prec) << std::fixed << fv->v(); + for (int i = addRight; i--;) oss << " "; + } else { + Printer p(oss, 0, false); + p.print(e); + } + return oss.str(); +} + +std::string b_file_path(EnvI&, Call* call) { + return FileUtils::file_path(call->loc().filename().str()); +} + +std::string b_concat(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + std::ostringstream oss; + for (unsigned int i = 0; i < al->size(); i++) { + oss << eval_string(env, (*al)[i]); + } + return oss.str(); +} + +std::string b_join(EnvI& env, Call* call) { + assert(call->n_args() == 2); + std::string sep = eval_string(env, call->arg(0)); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(1)); + std::ostringstream oss; + for (unsigned int i = 0; i < al->size(); i++) { + oss << eval_string(env, (*al)[i]); + if (i < al->size() - 1) oss << sep; + } + return oss.str(); +} + +IntSetVal* b_array_union(EnvI& env, Call* call) { + assert(call->n_args() == 1); + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->size() == 0) return IntSetVal::a(); + IntSetVal* isv = eval_intset(env, (*al)[0]); + for (unsigned int i = 0; i < al->size(); i++) { + IntSetRanges i0(isv); + IntSetRanges i1(eval_intset(env, (*al)[i])); + Ranges::Union u(i0, i1); + isv = IntSetVal::ai(u); + } + return isv; +} + +IntSetVal* b_array_intersect(EnvI& env, Call* call) { + assert(call->n_args() == 1); + ArrayLit* al = eval_array_lit(env, call->arg(0)); + std::vector ranges; + if (al->size() > 0) { + IntSetVal* i0 = eval_intset(env, (*al)[0]); + if (i0->size() > 0) { + IntSetRanges i0r(i0); + IntVal min = i0r.min(); + while (i0r()) { + // Initialize with last interval + IntVal max = i0r.max(); + // Intersect with all other intervals + restart: + for (int j = al->size(); j--;) { + IntSetRanges ij(eval_intset(env, (*al)[j])); + // Skip intervals that are too small + while (ij() && (ij.max() < min)) ++ij; + if (!ij()) goto done; + if (ij.min() > max) { + min = ij.min(); + max = ij.max(); + goto restart; + } + // Now the intervals overlap + if (min < ij.min()) min = ij.min(); + if (max > ij.max()) max = ij.max(); + } + ranges.push_back(IntSetVal::Range(min, max)); + // The next interval must be at least two elements away + min = max + 2; + } + done: + return IntSetVal::a(ranges); + } else { + return IntSetVal::a(); + } + } else { + return IntSetVal::a(); + } +} + +Expression* b_sort_by_int(EnvI& env, Call* call) { + assert(call->n_args() == 2); + ArrayLit* al = eval_array_lit(env, call->arg(0)); + ArrayLit* order_e = eval_array_lit(env, call->arg(1)); + std::vector order(order_e->size()); + std::vector a(order_e->size()); + for (unsigned int i = 0; i < order.size(); i++) { + a[i] = i; + order[i] = eval_int(env, (*order_e)[i]); + } + struct Ord { + std::vector& order; + Ord(std::vector& order0) : order(order0) {} + bool operator()(int i, int j) { return order[i] < order[j]; } + } _ord(order); + std::stable_sort(a.begin(), a.end(), _ord); + std::vector sorted(a.size()); + for (unsigned int i = static_cast(sorted.size()); i--;) sorted[i] = (*al)[a[i]]; + ArrayLit* al_sorted = new ArrayLit(al->loc(), sorted); + al_sorted->type(al->type()); + return al_sorted; +} + +Expression* b_sort_by_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + ArrayLit* al = eval_array_lit(env, call->arg(0)); + ArrayLit* order_e = eval_array_lit(env, call->arg(1)); + std::vector order(order_e->size()); + std::vector a(order_e->size()); + for (unsigned int i = 0; i < order.size(); i++) { + a[i] = i; + order[i] = eval_float(env, (*order_e)[i]); + } + struct Ord { + std::vector& order; + Ord(std::vector& order0) : order(order0) {} + bool operator()(int i, int j) { return order[i] < order[j]; } + } _ord(order); + std::stable_sort(a.begin(), a.end(), _ord); + std::vector sorted(a.size()); + for (unsigned int i = static_cast(sorted.size()); i--;) sorted[i] = (*al)[a[i]]; + ArrayLit* al_sorted = new ArrayLit(al->loc(), sorted); + al_sorted->type(al->type()); + return al_sorted; +} + +Expression* b_sort(EnvI& env, Call* call) { + assert(call->n_args() == 1); + ArrayLit* al = eval_array_lit(env, call->arg(0)); + std::vector sorted(al->size()); + for (unsigned int i = static_cast(sorted.size()); i--;) sorted[i] = (*al)[i]; + struct Ord { + EnvI& env; + Ord(EnvI& env0) : env(env0) {} + bool operator()(Expression* e0, Expression* e1) { + switch (e0->type().bt()) { + case Type::BT_INT: + return eval_int(env, e0) < eval_int(env, e1); + case Type::BT_BOOL: + return eval_bool(env, e0) < eval_bool(env, e1); + case Type::BT_FLOAT: + return eval_float(env, e0) < eval_float(env, e1); + default: + throw EvalError(env, e0->loc(), "unsupported type for sorting"); + } + } + } _ord(env); + std::sort(sorted.begin(), sorted.end(), _ord); + ArrayLit* al_sorted = new ArrayLit(al->loc(), sorted); + al_sorted->type(al->type()); + return al_sorted; +} + +std::default_random_engine& rnd_generator(void) { + // TODO: initiate with seed if given as annotation/in command line + static std::default_random_engine g; + return g; +} + +FloatVal b_normal_float_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double mean = eval_float(env, call->arg(0)).toDouble(); + const double stdv = eval_float(env, call->arg(1)).toDouble(); + std::normal_distribution distribution(mean, stdv); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_normal_int_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double mean = double(eval_int(env, call->arg(0)).toInt()); + const double stdv = eval_float(env, call->arg(1)).toDouble(); + std::normal_distribution distribution(mean, stdv); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_uniform_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double lb = eval_float(env, call->arg(0)).toDouble(); + const double ub = eval_float(env, call->arg(1)).toDouble(); + if (lb > ub) { + std::stringstream ssm; + ssm << "lowerbound of uniform distribution \"" << lb + << "\" is higher than its upperbound: " << ub; + throw EvalError(env, call->arg(0)->loc(), ssm.str()); + } + std::uniform_real_distribution distribution(lb, ub); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +IntVal b_uniform_int(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const long long int lb = eval_int(env, call->arg(0)).toInt(); + const long long int ub = eval_int(env, call->arg(1)).toInt(); + if (lb > ub) { + std::stringstream ssm; + ssm << "lowerbound of uniform distribution \"" << lb + << "\" is higher than its upperbound: " << ub; + throw EvalError(env, call->arg(0)->loc(), ssm.str()); + } + std::uniform_int_distribution distribution(lb, ub); + // return a sample from the distribution + return IntVal(distribution(rnd_generator())); +} + +IntVal b_poisson_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + long long int mean = eval_int(env, call->arg(0)).toInt(); + std::poisson_distribution distribution(mean); + // return a sample from the distribution + return IntVal(distribution(rnd_generator())); +} + +IntVal b_poisson_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + double mean = eval_float(env, call->arg(0)).toDouble(); + std::poisson_distribution distribution(mean); + // return a sample from the distribution + return IntVal(distribution(rnd_generator())); +} + +FloatVal b_gamma_float_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double alpha = eval_float(env, call->arg(0)).toDouble(); + const double beta = eval_float(env, call->arg(1)).toDouble(); + std::gamma_distribution distribution(alpha, beta); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_gamma_int_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double alpha = eval_float(env, call->arg(0)).toDouble(); + const double beta = eval_float(env, call->arg(1)).toDouble(); + std::gamma_distribution distribution(alpha, beta); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_weibull_int_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double shape = double(eval_int(env, call->arg(0)).toInt()); + if (shape < 0) { + std::stringstream ssm; + ssm << "The shape factor for the weibull distribution \"" << shape + << "\" has to be greater than zero."; + throw EvalError(env, call->arg(0)->loc(), ssm.str()); + } + const double scale = eval_float(env, call->arg(1)).toDouble(); + if (scale < 0) { + std::stringstream ssm; + ssm << "The scale factor for the weibull distribution \"" << scale + << "\" has to be greater than zero."; + throw EvalError(env, call->arg(1)->loc(), ssm.str()); + } + std::weibull_distribution distribution(shape, scale); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_weibull_float_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double shape = eval_float(env, call->arg(0)).toDouble(); + if (shape < 0) { + std::stringstream ssm; + ssm << "The shape factor for the weibull distribution \"" << shape + << "\" has to be greater than zero."; + throw EvalError(env, call->arg(0)->loc(), ssm.str()); + } + const double scale = eval_float(env, call->arg(1)).toDouble(); + if (scale < 0) { + std::stringstream ssm; + ssm << "The scale factor for the weibull distribution \"" << scale + << "\" has to be greater than zero."; + throw EvalError(env, call->arg(1)->loc(), ssm.str()); + } + std::weibull_distribution distribution(shape, scale); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_exponential_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double lambda = eval_float(env, call->arg(0)).toDouble(); + if (lambda < 0) { + std::stringstream ssm; + ssm << "The lambda-parameter for the exponential distribution function \"" << lambda + << "\" has to be greater than zero."; + throw EvalError(env, call->arg(0)->loc(), ssm.str()); + } + std::exponential_distribution distribution(lambda); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_exponential_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double lambda = double(eval_int(env, call->arg(0)).toInt()); + if (lambda < 0) { + std::stringstream ssm; + ssm << "The lambda-parameter for the exponential distribution function \"" << lambda + << "\" has to be greater than zero."; + throw EvalError(env, call->arg(0)->loc(), ssm.str()); + } + std::exponential_distribution distribution(lambda); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_lognormal_float_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double mean = eval_float(env, call->arg(0)).toDouble(); + const double stdv = eval_float(env, call->arg(1)).toDouble(); + std::lognormal_distribution distribution(mean, stdv); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_lognormal_int_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double mean = double(eval_int(env, call->arg(0)).toInt()); + const double stdv = eval_float(env, call->arg(1)).toDouble(); + std::lognormal_distribution distribution(mean, stdv); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_chisquared_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double lambda = eval_float(env, call->arg(0)).toDouble(); + std::exponential_distribution distribution(lambda); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_chisquared_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double lambda = double(eval_int(env, call->arg(0)).toInt()); + std::exponential_distribution distribution(lambda); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_cauchy_float_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double mean = eval_float(env, call->arg(0)).toDouble(); + const double scale = eval_float(env, call->arg(1)).toDouble(); + std::cauchy_distribution distribution(mean, scale); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_cauchy_int_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double mean = double(eval_int(env, call->arg(0)).toInt()); + const double scale = eval_float(env, call->arg(1)).toDouble(); + std::cauchy_distribution distribution(mean, scale); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_fdistribution_float_float(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double d1 = eval_float(env, call->arg(0)).toDouble(); + const double d2 = eval_float(env, call->arg(1)).toDouble(); + std::fisher_f_distribution distribution(d1, d2); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_fdistribution_int_int(EnvI& env, Call* call) { + assert(call->n_args() == 2); + const double d1 = double(eval_int(env, call->arg(0)).toInt()); + const double d2 = double(eval_int(env, call->arg(1)).toInt()); + std::fisher_f_distribution distribution(d1, d2); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_tdistribution_float(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double sampleSize = eval_float(env, call->arg(0)).toDouble(); + std::student_t_distribution distribution(sampleSize); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +FloatVal b_tdistribution_int(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double sampleSize = double(eval_int(env, call->arg(0)).toInt()); + std::student_t_distribution distribution(sampleSize); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +IntVal b_discrete_distribution(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + ArrayLit* al = eval_array_lit(env, call->arg(0)); + if (al->dims() != 1) { + std::stringstream ssm; + ssm << "expecting 1-dimensional array of weights for discrete distribution instead of: " << *al + << std::endl; + throw EvalError(env, al->loc(), ssm.str()); + } + std::vector weights(al->size()); + for (unsigned int i = 0; i < al->size(); i++) { + weights[i] = eval_int(env, (*al)[i]).toInt(); + } +#ifdef _MSC_VER + std::size_t i(0); + std::discrete_distribution distribution( + weights.size(), 0.0, 1.0, [&weights, &i](double) { return weights[i++]; }); +#else + std::discrete_distribution distribution(weights.begin(), weights.end()); +#endif + // return a sample from the distribution + IntVal iv = IntVal(distribution(rnd_generator())); + return iv; +} + +bool b_bernoulli(EnvI& env, Call* call) { + assert(call->n_args() == 1); + const double p = eval_float(env, call->arg(0)).toDouble(); + std::bernoulli_distribution distribution(p); + // return a sample from the distribution + return distribution(rnd_generator()); +} + +IntVal b_binomial(EnvI& env, Call* call) { + assert(call->n_args() == 2); + double t = double(eval_int(env, call->arg(0)).toInt()); + double p = eval_float(env, call->arg(1)).toDouble(); + std::binomial_distribution distribution(t, p); + // return a sample from the distribution + return IntVal(distribution(rnd_generator())); +} + +FloatVal b_atan(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + FloatVal f = eval_float(env, call->arg(0)); + return std::atan(f.toDouble()); +} + +FloatVal b_cos(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + FloatVal f = eval_float(env, call->arg(0)); + return std::cos(f.toDouble()); +} + +FloatVal b_sin(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + FloatVal f = eval_float(env, call->arg(0)); + return std::sin(f.toDouble()); +} + +FloatVal b_asin(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + FloatVal f = eval_float(env, call->arg(0)); + return std::asin(f.toDouble()); +} + +FloatVal b_acos(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + FloatVal f = eval_float(env, call->arg(0)); + return std::acos(f.toDouble()); +} + +FloatVal b_tan(EnvI& env, Call* call) { + assert(call->n_args() == 1); + GCLock lock; + FloatVal f = eval_float(env, call->arg(0)); + return std::tan(f.toDouble()); +} + +IntVal b_to_enum(EnvI& env, Call* call) { + assert(call->n_args() == 2); + IntSetVal* isv = eval_intset(env, call->arg(0)); + IntVal v = eval_int(env, call->arg(1)); + if (!isv->contains(v)) + throw ResultUndefinedError(env, call->loc(), "value outside of enum range"); + return v; +} + +IntVal b_enum_next(EnvI& env, Call* call) { + IntSetVal* isv = eval_intset(env, call->arg(0)); + IntVal v = eval_int(env, call->arg(1)); + if (!isv->contains(v + 1)) + throw ResultUndefinedError(env, call->loc(), "value outside of enum range"); + return v + 1; +} + +IntVal b_enum_prev(EnvI& env, Call* call) { + IntSetVal* isv = eval_intset(env, call->arg(0)); + IntVal v = eval_int(env, call->arg(1)); + if (!isv->contains(v - 1)) + throw ResultUndefinedError(env, call->loc(), "value outside of enum range"); + return v - 1; +} + +IntVal b_mzn_compiler_version(EnvI&, Call*) { + return atoi(MZN_VERSION_MAJOR) * 10000 + atoi(MZN_VERSION_MINOR) * 1000 + atoi(MZN_VERSION_PATCH); +} + +Expression* b_slice(EnvI& env, Call* call) { + ArrayLit* al = eval_array_lit(env, call->arg(0)); + + ArrayLit* slice = eval_array_lit(env, call->arg(1)); + std::vector> newSlice(slice->size()); + for (unsigned int i = 0; i < slice->size(); i++) { + IntSetVal* isv = eval_intset(env, (*slice)[i]); + if (isv->size() == 0) { + newSlice[i] = std::pair(1, 0); + } else { + if (isv->size() > 1) + throw ResultUndefinedError(env, call->loc(), "array slice must be contiguous"); + int sl_min = isv->min().isFinite() ? static_cast(isv->min().toInt()) : al->min(i); + int sl_max = isv->max().isFinite() ? static_cast(isv->max().toInt()) : al->max(i); + if (sl_min < al->min(i) || sl_max > al->max(i)) + throw ResultUndefinedError(env, call->loc(), "array slice out of bounds"); + newSlice[i] = std::pair(sl_min, sl_max); + } + } + + std::vector> newDims(call->n_args() - 2); + for (unsigned int i = 0; i < newDims.size(); i++) { + IntSetVal* isv = eval_intset(env, call->arg(2 + i)); + if (isv->size() == 0) { + newDims[i] = std::pair(1, 0); + } else { + newDims[i] = std::pair(static_cast(isv->min().toInt()), + static_cast(isv->max().toInt())); + } + } + ArrayLit* ret = new ArrayLit(al->loc(), al, newDims, newSlice); + ret->type(call->type()); + return ret; +} + +Expression* b_regular_from_string(EnvI& env, Call* call) { +#ifdef HAS_GECODE + using namespace Gecode; + ArrayLit* vars = eval_array_lit(env, call->arg(0)); + std::string expr = eval_string(env, call->arg(1)); + + IntSetVal* dom; + if (vars->size() == 0) { + dom = IntSetVal::a(); + } else { + dom = b_dom_varint(env, (*vars)[0]); + for (unsigned int i = 1; i < vars->size(); i++) { + IntSetRanges isr(dom); + IntSetRanges r(b_dom_varint(env, (*vars)[i])); + Ranges::Union u(isr, r); + dom = IntSetVal::ai(u); + } + } + int card = dom->max().toInt() - dom->min().toInt() + 1; + int offset = 1 - dom->min().toInt(); + + std::unique_ptr regex; + try { + regex = regex_from_string(expr, *dom, env.reverseEnum); + } catch (const std::exception& e) { + throw SyntaxError(call->arg(1)->loc(), e.what()); + } + DFA dfa = DFA(*regex); + + std::vector> reg_trans( + dfa.n_states(), std::vector(card, IntLit::a(IntVal(0)))); + + DFA::Transitions trans(dfa); + while (trans()) { + // std::cerr << trans.i_state() + 1 << " -- " << trans.symbol() << " --> " << + // trans.o_state() + 1 << "\n"; + if (trans.symbol() >= dom->min().toInt() && trans.symbol() <= dom->max().toInt()) { + reg_trans[trans.i_state()][trans.symbol() + offset - 1] = + IntLit::a(IntVal(trans.o_state() + 1)); + } + ++trans; + } + + std::vector args(6); + if (offset == 0) { + args[0] = vars; // x + } else { + std::vector nvars(vars->size()); + IntLit* loffset = IntLit::a(IntVal(offset)); + for (int i = 0; i < nvars.size(); ++i) { + nvars[i] = new BinOp(call->loc().introduce(), (*vars)[i], BOT_PLUS, loffset); + nvars[i]->type(Type::varint()); + } + args[0] = new ArrayLit(call->loc().introduce(), nvars); // x + args[0]->type(Type::varint(1)); + } + args[1] = IntLit::a(IntVal(dfa.n_states())); // Q + args[1]->type(Type::parint()); + args[2] = IntLit::a(IntVal(card)); // S + args[2]->type(Type::parint()); + args[3] = new ArrayLit(call->loc().introduce(), reg_trans); // d + args[3]->type(Type::parint(2)); + args[4] = IntLit::a(IntVal(1)); // q0 + args[4]->type(Type::parint()); + args[5] = new SetLit(call->loc().introduce(), + IntSetVal::a(IntVal(dfa.final_fst() + 1), IntVal(dfa.final_lst()))); // F + args[5]->type(Type::parsetint()); + + auto nc = new Call(call->loc().introduce(), "regular", args); + nc->type(Type::varbool()); + + return nc; +#else + throw FlatteningError( + env, call->loc(), + "MiniZinc was compiled without built-in Gecode, cannot parse regular expression"); +#endif +} + +void registerBuiltins(Env& e) { + EnvI& env = e.envi(); + Model* m = env.model; + + std::vector t_intint(2); + t_intint[0] = Type::parint(); + t_intint[1] = Type::parint(); + + std::vector t_intarray(1); + t_intarray[0] = Type::parint(-1); + + GCLock lock; + + rb(env, m, ASTString("min"), t_intint, b_int_min); + rb(env, m, ASTString("min"), t_intarray, b_int_min); + rb(env, m, ASTString("max"), t_intint, b_int_max); + rb(env, m, ASTString("max"), t_intarray, b_int_max); + rb(env, m, constants().ids.sum, t_intarray, b_sum_int); + rb(env, m, ASTString("product"), t_intarray, b_product_int); + rb(env, m, ASTString("pow"), t_intint, b_pow_int); + + { + std::vector t(2); + t[0] = Type::top(-1); + t[1] = Type::top(-1); + rb(env, m, ASTString("index_sets_agree"), t, b_index_sets_agree); + } + { + std::vector t_anyarray1(1); + t_anyarray1[0] = Type::optvartop(1); + rb(env, m, ASTString("index_set"), t_anyarray1, b_index_set1); + } + { + std::vector t_anyarray2(1); + t_anyarray2[0] = Type::optvartop(2); + rb(env, m, ASTString("index_set_1of2"), t_anyarray2, b_index_set1); + rb(env, m, ASTString("index_set_2of2"), t_anyarray2, b_index_set2); + } + { + std::vector t_anyarray3(1); + t_anyarray3[0] = Type::optvartop(3); + rb(env, m, ASTString("index_set_1of3"), t_anyarray3, b_index_set1); + rb(env, m, ASTString("index_set_2of3"), t_anyarray3, b_index_set2); + rb(env, m, ASTString("index_set_3of3"), t_anyarray3, b_index_set3); + } + { + std::vector t_anyarray4(1); + t_anyarray4[0] = Type::optvartop(4); + rb(env, m, ASTString("index_set_1of4"), t_anyarray4, b_index_set1); + rb(env, m, ASTString("index_set_2of4"), t_anyarray4, b_index_set2); + rb(env, m, ASTString("index_set_3of4"), t_anyarray4, b_index_set3); + rb(env, m, ASTString("index_set_4of4"), t_anyarray4, b_index_set4); + } + { + std::vector t_anyarray5(1); + t_anyarray5[0] = Type::optvartop(5); + rb(env, m, ASTString("index_set_1of5"), t_anyarray5, b_index_set1); + rb(env, m, ASTString("index_set_2of5"), t_anyarray5, b_index_set2); + rb(env, m, ASTString("index_set_3of5"), t_anyarray5, b_index_set3); + rb(env, m, ASTString("index_set_4of5"), t_anyarray5, b_index_set4); + rb(env, m, ASTString("index_set_5of5"), t_anyarray5, b_index_set5); + } + { + std::vector t_anyarray6(1); + t_anyarray6[0] = Type::optvartop(6); + rb(env, m, ASTString("index_set_1of6"), t_anyarray6, b_index_set1); + rb(env, m, ASTString("index_set_2of6"), t_anyarray6, b_index_set2); + rb(env, m, ASTString("index_set_3of6"), t_anyarray6, b_index_set3); + rb(env, m, ASTString("index_set_4of6"), t_anyarray6, b_index_set4); + rb(env, m, ASTString("index_set_5of6"), t_anyarray6, b_index_set5); + rb(env, m, ASTString("index_set_6of6"), t_anyarray6, b_index_set6); + } + { + std::vector t_arrayXd(1); + t_arrayXd[0] = Type::top(-1); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); + t_arrayXd[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); + t_arrayXd[0] = Type::vartop(-1); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); + t_arrayXd[0] = Type::optvartop(-1); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d_list); + } + { + std::vector t_arrayXd(2); + t_arrayXd[0] = Type::parsetint(); + t_arrayXd[1] = Type::top(-1); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); + t_arrayXd[1].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); + t_arrayXd[1] = Type::vartop(-1); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); + t_arrayXd[1] = Type::optvartop(-1); + rb(env, m, ASTString("array1d"), t_arrayXd, b_array1d); + } + { + std::vector t_arrayXd(2); + t_arrayXd[0] = Type::optvartop(-1); + t_arrayXd[1] = Type::top(-1); + rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); + t_arrayXd[1].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); + t_arrayXd[1] = Type::vartop(-1); + rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); + t_arrayXd[1] = Type::optvartop(-1); + rb(env, m, ASTString("arrayXd"), t_arrayXd, b_arrayXd); + } + { + std::vector t_arrayXd(3); + t_arrayXd[0] = Type::parsetint(); + t_arrayXd[1] = Type::parsetint(); + t_arrayXd[2] = Type::top(-1); + rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); + t_arrayXd[2].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); + t_arrayXd[2] = Type::vartop(-1); + rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); + t_arrayXd[2] = Type::optvartop(-1); + rb(env, m, ASTString("array2d"), t_arrayXd, b_array2d); + } + { + std::vector t_arrayXd(4); + t_arrayXd[0] = Type::parsetint(); + t_arrayXd[1] = Type::parsetint(); + t_arrayXd[2] = Type::parsetint(); + t_arrayXd[3] = Type::top(-1); + rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); + t_arrayXd[3].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); + t_arrayXd[3] = Type::vartop(-1); + rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); + t_arrayXd[3] = Type::optvartop(-1); + rb(env, m, ASTString("array3d"), t_arrayXd, b_array3d); + } + { + std::vector t_arrayXd(5); + t_arrayXd[0] = Type::parsetint(); + t_arrayXd[1] = Type::parsetint(); + t_arrayXd[2] = Type::parsetint(); + t_arrayXd[3] = Type::parsetint(); + t_arrayXd[4] = Type::top(-1); + rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); + t_arrayXd[4].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); + t_arrayXd[4] = Type::vartop(-1); + rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); + t_arrayXd[4] = Type::optvartop(-1); + rb(env, m, ASTString("array4d"), t_arrayXd, b_array4d); + } + { + std::vector t_arrayXd(6); + t_arrayXd[0] = Type::parsetint(); + t_arrayXd[1] = Type::parsetint(); + t_arrayXd[2] = Type::parsetint(); + t_arrayXd[3] = Type::parsetint(); + t_arrayXd[4] = Type::parsetint(); + t_arrayXd[5] = Type::top(-1); + rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); + t_arrayXd[5].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); + t_arrayXd[5] = Type::vartop(-1); + rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); + t_arrayXd[5] = Type::optvartop(-1); + rb(env, m, ASTString("array5d"), t_arrayXd, b_array5d); + } + { + std::vector t_arrayXd(7); + t_arrayXd[0] = Type::parsetint(); + t_arrayXd[1] = Type::parsetint(); + t_arrayXd[2] = Type::parsetint(); + t_arrayXd[3] = Type::parsetint(); + t_arrayXd[4] = Type::parsetint(); + t_arrayXd[5] = Type::parsetint(); + t_arrayXd[6] = Type::top(-1); + rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); + t_arrayXd[6].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); + t_arrayXd[6] = Type::vartop(-1); + rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); + t_arrayXd[6] = Type::optvartop(-1); + rb(env, m, ASTString("array6d"), t_arrayXd, b_array6d); + } + { + std::vector stv(3); + stv[0] = Type::partop(-1); + stv[1] = Type::parsetint(1); + stv[2] = Type::parsetint(); + rb(env, m, ASTString("slice_1d"), stv, b_slice); + stv[0] = Type::vartop(-1); + rb(env, m, ASTString("slice_1d"), stv, b_slice); + stv[0] = Type::optvartop(-1); + rb(env, m, ASTString("slice_1d"), stv, b_slice); + stv[0] = Type::optpartop(-1); + rb(env, m, ASTString("slice_1d"), stv, b_slice); + + stv.push_back(Type::parsetint()); + stv[0] = Type::partop(-1); + rb(env, m, ASTString("slice_2d"), stv, b_slice); + stv[0] = Type::vartop(-1); + rb(env, m, ASTString("slice_2d"), stv, b_slice); + stv[0] = Type::optvartop(-1); + rb(env, m, ASTString("slice_2d"), stv, b_slice); + stv[0] = Type::optpartop(-1); + rb(env, m, ASTString("slice_2d"), stv, b_slice); + + stv.push_back(Type::parsetint()); + stv[0] = Type::partop(-1); + rb(env, m, ASTString("slice_3d"), stv, b_slice); + stv[0] = Type::vartop(-1); + rb(env, m, ASTString("slice_3d"), stv, b_slice); + stv[0] = Type::optvartop(-1); + rb(env, m, ASTString("slice_3d"), stv, b_slice); + stv[0] = Type::optpartop(-1); + rb(env, m, ASTString("slice_3d"), stv, b_slice); + + stv.push_back(Type::parsetint()); + stv[0] = Type::partop(-1); + rb(env, m, ASTString("slice_4d"), stv, b_slice); + stv[0] = Type::vartop(-1); + rb(env, m, ASTString("slice_4d"), stv, b_slice); + stv[0] = Type::optvartop(-1); + rb(env, m, ASTString("slice_4d"), stv, b_slice); + stv[0] = Type::optpartop(-1); + rb(env, m, ASTString("slice_4d"), stv, b_slice); + + stv.push_back(Type::parsetint()); + stv[0] = Type::partop(-1); + rb(env, m, ASTString("slice_5d"), stv, b_slice); + stv[0] = Type::vartop(-1); + rb(env, m, ASTString("slice_5d"), stv, b_slice); + stv[0] = Type::optvartop(-1); + rb(env, m, ASTString("slice_5d"), stv, b_slice); + stv[0] = Type::optpartop(-1); + rb(env, m, ASTString("slice_5d"), stv, b_slice); + + stv.push_back(Type::parsetint()); + stv[0] = Type::partop(-1); + rb(env, m, ASTString("slice_6d"), stv, b_slice); + stv[0] = Type::vartop(-1); + rb(env, m, ASTString("slice_6d"), stv, b_slice); + stv[0] = Type::optvartop(-1); + rb(env, m, ASTString("slice_6d"), stv, b_slice); + stv[0] = Type::optpartop(-1); + rb(env, m, ASTString("slice_6d"), stv, b_slice); + } + { + std::vector t(2); + t[0] = Type::parbool(); + t[1] = Type::parstring(); + rb(env, m, constants().ids.assert, t, b_assert_bool); + } + { + std::vector t(3); + t[0] = Type::parbool(); + t[1] = Type::parstring(); + t[2] = Type::top(); + rb(env, m, constants().ids.assert, t, b_assert); + t[2] = Type::vartop(); + rb(env, m, constants().ids.assert, t, b_assert); + t[2] = Type::optvartop(); + rb(env, m, constants().ids.assert, t, b_assert); + t[2] = Type::top(-1); + rb(env, m, constants().ids.assert, t, b_assert); + t[2] = Type::vartop(-1); + rb(env, m, constants().ids.assert, t, b_assert); + t[2] = Type::optvartop(-1); + rb(env, m, constants().ids.assert, t, b_assert); + } + { + std::vector t(1); + t[0] = Type::parstring(); + rb(env, m, ASTString("abort"), t, b_abort); + rb(env, m, constants().ids.trace, t, b_trace); + rb(env, m, ASTString("trace_stdout"), t, b_trace_stdout); + } + { + std::vector t(2); + t[0] = Type::parstring(); + t[1] = Type::top(); + rb(env, m, constants().ids.trace, t, b_trace); + rb(env, m, ASTString("trace_stdout"), t, b_trace_stdout); + t[1] = Type::vartop(); + rb(env, m, constants().ids.trace, t, b_trace); + rb(env, m, ASTString("trace_stdout"), t, b_trace_stdout); + t[1] = Type::optvartop(); + rb(env, m, constants().ids.trace, t, b_trace); + rb(env, m, ASTString("trace_stdout"), t, b_trace_stdout); + } + { + rb(env, m, ASTString("mzn_in_redundant_constraint"), std::vector(), + b_in_redundant_constraint); + } + { + std::vector t_length(1); + t_length[0] = Type::optvartop(-1); + rb(env, m, ASTString("length"), t_length, b_length); + } + { + std::vector t(1); + t[0] = Type::parbool(); + rb(env, m, constants().ids.bool2int, t, b_bool2int); + } + { + std::vector t(1); + t[0] = Type::parbool(-1); + rb(env, m, constants().ids.forall, t, b_forall_par); + rb(env, m, constants().ids.exists, t, b_exists_par); + rb(env, m, ASTString("xorall"), t, b_xorall_par); + rb(env, m, ASTString("iffall"), t, b_iffall_par); + } + { + std::vector t(2); + t[0] = Type::parbool(-1); + t[1] = Type::parbool(-1); + rb(env, m, constants().ids.clause, t, b_clause_par); + } + { + std::vector t(1); + t[0] = Type::varsetint(); + rb(env, m, ASTString("ub"), t, b_ub_set); + rb(env, m, ASTString("lb"), t, b_lb_set); + } + { + std::vector t(1); + t[0] = Type::varsetint(1); + rb(env, m, ASTString("ub_array"), t, b_array_ub_set); + } + { + std::vector t(1); + t[0] = Type::varint(); + rb(env, m, ASTString("dom"), t, b_dom_varint); + } + { + std::vector t(1); + t[0] = Type::varint(-1); + rb(env, m, ASTString("dom_array"), t, b_dom_array); + rb(env, m, ASTString("dom_bounds_array"), t, b_dom_bounds_array); + } + { + std::vector t(1); + t[0] = Type::parsetint(); + rb(env, m, ASTString("min"), t, b_min_parsetint); + } + { + std::vector t(1); + t[0] = Type::parsetint(); + rb(env, m, ASTString("max"), t, b_max_parsetint); + } + { + std::vector t(1); + t[0] = Type::varint(); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("lb"), t, b_lb_varoptint); + } + { + std::vector t(1); + t[0] = Type::varint(); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("ub"), t, b_ub_varoptint); + } + { + std::vector t(1); + t[0] = Type::varint(); + rb(env, m, ASTString("lb"), t, b_lb_varoptint); + } + { + std::vector t(1); + t[0] = Type::varint(); + rb(env, m, ASTString("ub"), t, b_ub_varoptint); + } + { + std::vector t(1); + t[0] = Type::varint(-1); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("lb_array"), t, b_array_lb_int); + } + { + std::vector t(1); + t[0] = Type::varint(-1); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("ub_array"), t, b_array_ub_int); + } + { + std::vector t(1); + t[0] = Type::varfloat(); + rb(env, m, ASTString("lb"), t, b_lb_varoptfloat); + } + { + std::vector t(1); + t[0] = Type::varfloat(); + rb(env, m, ASTString("ub"), t, b_ub_varoptfloat); + } + { + std::vector t(1); + t[0] = Type::varfloat(-1); + rb(env, m, ASTString("lb_array"), t, b_array_lb_float); + } + { + std::vector t(1); + t[0] = Type::varfloat(-1); + rb(env, m, ASTString("ub_array"), t, b_array_ub_float); + } + { + std::vector t(1); + t[0] = Type::parsetint(); + rb(env, m, ASTString("card"), t, b_card); + } + { + std::vector t(1); + t[0] = Type::parint(); + rb(env, m, ASTString("abs"), t, b_abs_int); + t[0] = Type::parfloat(); + rb(env, m, ASTString("abs"), t, b_abs_float); + } + { + std::vector t(1); + t[0] = Type::varint(); + rb(env, m, ASTString("has_bounds"), t, b_has_bounds_int); + } + { + std::vector t(1); + t[0] = Type::varfloat(); + rb(env, m, ASTString("has_bounds"), t, b_has_bounds_float); + } + { + std::vector t(1); + t[0] = Type::varsetint(); + rb(env, m, ASTString("has_ub_set"), t, b_has_ub_set); + } + { + std::vector t(1); + t[0] = Type::optvartop(); + rb(env, m, ASTString("is_fixed"), t, b_is_fixed); + } + { + std::vector t(1); + t[0] = Type::optvartop(-1); + rb(env, m, ASTString("is_fixed"), t, b_is_fixed_array); + } + { + std::vector t(1); + t[0] = Type::optvartop(); + rb(env, m, ASTString("fix"), t, b_fix_bool); + rb(env, m, ASTString("fix"), t, b_fix_int); + rb(env, m, ASTString("fix"), t, b_fix_set); + rb(env, m, ASTString("fix"), t, b_fix_float); + } + { + std::vector t(1); + t[0] = Type::optvartop(1); + rb(env, m, ASTString("fix"), t, b_fix_array); + } + { + std::vector t(1); + t[0] = Type::parint(); + rb(env, m, ASTString("int2float"), t, b_int2float); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("ceil"), t, b_ceil); + rb(env, m, ASTString("floor"), t, b_floor); + rb(env, m, ASTString("round"), t, b_round); + rb(env, m, ASTString("log10"), t, b_log10); + rb(env, m, ASTString("log2"), t, b_log2); + rb(env, m, ASTString("ln"), t, b_ln); + rb(env, m, ASTString("exp"), t, b_exp); + rb(env, m, ASTString("sqrt"), t, b_sqrt); + t.push_back(Type::parfloat()); + rb(env, m, ASTString("log"), t, b_log); + rb(env, m, ASTString("pow"), t, b_pow); + } + { + std::vector t(1); + t[0] = Type::parfloat(1); + rb(env, m, constants().ids.sum, t, b_sum_float); + rb(env, m, ASTString("product"), t, b_product_float); + } + { + std::vector t(1); + t[0] = Type::parfloat(1); + rb(env, m, ASTString("min"), t, b_float_min); + rb(env, m, ASTString("max"), t, b_float_max); + + t[0] = Type::parfloat(); + t.push_back(Type::parfloat()); + rb(env, m, ASTString("min"), t, b_float_min); + rb(env, m, ASTString("max"), t, b_float_max); + } + { + std::vector t(1); + t[0] = Type::parsetint(); + rb(env, m, ASTString("set2array"), t, b_set2array); + } + { + std::vector t(1); + t[0] = Type::parstring(); + rb(env, m, ASTString("string_length"), t, b_string_length); + } + { rb(env, m, ASTString("file_path"), std::vector(), b_file_path); } + { + std::vector t(1); + t[0] = Type::vartop(); + rb(env, m, ASTString("show"), t, b_show); + rb(env, m, ASTString("showJSON"), t, b_show_json); + t[0] = Type::vartop(); + t[0].st(Type::ST_SET); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("show"), t, b_show); + rb(env, m, ASTString("showJSON"), t, b_show_json); + t[0] = Type::vartop(-1); + rb(env, m, ASTString("show"), t, b_show); + rb(env, m, ASTString("showJSON"), t, b_show_json); + } + { + std::vector t(1); + t[0] = Type::parstring(); + rb(env, m, ASTString("showDznId"), t, b_showDznId); + } + { + std::vector t(3); + t[0] = t[1] = Type::parint(); + t[2] = Type::vartop(); + rb(env, m, ASTString("format"), t, b_format); + t[2] = Type::vartop(); + t[2].st(Type::ST_SET); + t[2].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("format"), t, b_format); + t[2] = Type::vartop(-1); + rb(env, m, ASTString("format"), t, b_format); + } + { + std::vector t(2); + t[0] = Type::parint(); + t[1] = Type::vartop(); + rb(env, m, ASTString("format"), t, b_format); + t[1] = Type::vartop(); + t[1].st(Type::ST_SET); + t[1].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("format"), t, b_format); + t[1] = Type::vartop(-1); + rb(env, m, ASTString("format"), t, b_format); + t[1] = Type::parstring(); + rb(env, m, ASTString("format_justify_string"), t, b_format_justify_string); + } + { + std::vector t; + rb(env, m, ASTString("outputJSON"), t, b_outputJSON); + rb(env, m, ASTString("outputJSONParameters"), t, b_outputJSONParameters); + } + { + std::vector t(2); + t[0] = Type::parint(); + t[1] = Type::varint(); + rb(env, m, ASTString("show_int"), t, b_show_int); + } + { + std::vector t(3); + t[0] = Type::parint(); + t[1] = Type::parint(); + t[2] = Type::varfloat(); + rb(env, m, ASTString("show_float"), t, b_show_float); + } + { + std::vector t(1); + t[0] = Type::parstring(1); + rb(env, m, ASTString("concat"), t, b_concat); + } + { + std::vector t(2); + t[0] = Type::parstring(); + t[1] = Type::parstring(1); + rb(env, m, ASTString("join"), t, b_join); + } + { + std::vector t(2); + t[0] = Type::varint(); + t[1] = Type::varint(); + rb(env, m, ASTString("compute_div_bounds"), t, b_compute_div_bounds); + } + { + std::vector t(1); + t[0] = Type::parsetint(1); + rb(env, m, ASTString("array_intersect"), t, b_array_intersect); + rb(env, m, ASTString("array_union"), t, b_array_union); + } + { + std::vector t(1); + t[0] = Type::parint(); + t[0].ot(Type::OT_OPTIONAL); + t[0].bt(Type::BT_TOP); + rb(env, m, ASTString("occurs"), t, b_occurs); + rb(env, m, ASTString("deopt"), t, b_deopt_expr); + t[0].bt(Type::BT_INT); + rb(env, m, ASTString("deopt"), t, b_deopt_int); + t[0].bt(Type::BT_BOOL); + rb(env, m, ASTString("deopt"), t, b_deopt_bool); + t[0].bt(Type::BT_FLOAT); + rb(env, m, ASTString("deopt"), t, b_deopt_float); + t[0].bt(Type::BT_STRING); + rb(env, m, ASTString("deopt"), t, b_deopt_string); + t[0].bt(Type::BT_INT); + t[0].st(Type::ST_SET); + rb(env, m, ASTString("deopt"), t, b_deopt_intset); + } + { + std::vector t(2); + t[0] = Type::varbot(1); + t[1] = Type::parint(1); + rb(env, m, ASTString("sort_by"), t, b_sort_by_int); + t[0] = Type::bot(1); + rb(env, m, ASTString("sort_by"), t, b_sort_by_int); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("sort_by"), t, b_sort_by_int); + } + { + std::vector t(2); + t[0] = Type::varbot(1); + t[1] = Type::parfloat(1); + rb(env, m, ASTString("sort_by"), t, b_sort_by_float); + t[0] = Type::bot(1); + rb(env, m, ASTString("sort_by"), t, b_sort_by_float); + t[0].ot(Type::OT_OPTIONAL); + rb(env, m, ASTString("sort_by"), t, b_sort_by_float); + } + { + std::vector t(1); + t[0] = Type::parint(1); + rb(env, m, ASTString("sort"), t, b_sort); + rb(env, m, ASTString("arg_min"), t, b_arg_min_int); + rb(env, m, ASTString("arg_max"), t, b_arg_max_int); + t[0] = Type::parbool(1); + rb(env, m, ASTString("sort"), t, b_sort); + t[0] = Type::parfloat(1); + rb(env, m, ASTString("sort"), t, b_sort); + rb(env, m, ASTString("arg_min"), t, b_arg_min_float); + rb(env, m, ASTString("arg_max"), t, b_arg_max_float); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("atan"), t, b_atan); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("cos"), t, b_cos); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("sin"), t, b_sin); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("asin"), t, b_asin); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("acos"), t, b_acos); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("tan"), t, b_tan); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("normal"), t, b_normal_float_float); + t[0] = Type::parint(); + rb(env, m, ASTString("normal"), t, b_normal_int_float); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("uniform"), t, b_uniform_float); + t[0] = Type::parint(); + t[1] = Type::parint(); + rb(env, m, ASTString("uniform"), t, b_uniform_int); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("poisson"), t, b_poisson_float); + t[0] = Type::parint(); + rb(env, m, ASTString("poisson"), t, b_poisson_int); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("gamma"), t, b_gamma_float_float); + t[0] = Type::parint(); + rb(env, m, ASTString("gamma"), t, b_gamma_int_float); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("weibull"), t, b_weibull_float_float); + t[0] = Type::parint(); + rb(env, m, ASTString("weibull"), t, b_weibull_int_float); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("exponential"), t, b_exponential_float); + t[0] = Type::parint(); + rb(env, m, ASTString("exponential"), t, b_exponential_int); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("lognormal"), t, b_lognormal_float_float); + t[0] = Type::parint(); + rb(env, m, ASTString("lognormal"), t, b_lognormal_int_float); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("chisquared"), t, b_chisquared_float); + t[0] = Type::parint(); + rb(env, m, ASTString("chisquared"), t, b_chisquared_int); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("cauchy"), t, b_cauchy_float_float); + t[0] = Type::parint(); + rb(env, m, ASTString("cauchy"), t, b_cauchy_int_float); + } + { + std::vector t(2); + t[0] = Type::parfloat(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("fdistribution"), t, b_fdistribution_float_float); + t[0] = Type::parint(); + t[1] = Type::parint(); + rb(env, m, ASTString("fdistribution"), t, b_fdistribution_int_int); + } + { + std::vector t(1); + t[0] = Type::parfloat(); + rb(env, m, ASTString("tdistribution"), t, b_tdistribution_float); + t[0] = Type::parint(); + rb(env, m, ASTString("tdistribution"), t, b_tdistribution_int); + } + { + std::vector t(1); + t[0] = Type::parint(1); + rb(env, m, ASTString("discrete_distribution"), t, b_discrete_distribution); + } + { + std::vector t(1); + t[0] = Type::parint(); + rb(env, m, ASTString("bernoulli"), t, b_bernoulli); + } + { + std::vector t(2); + t[0] = Type::parint(); + t[1] = Type::parfloat(); + rb(env, m, ASTString("binomial"), t, b_binomial); + } + { + std::vector t(2); + t[0] = Type::parsetint(); + t[1] = Type::parint(); + rb(env, m, ASTString("to_enum"), t, b_to_enum); + rb(env, m, ASTString("enum_next"), t, b_enum_next); + rb(env, m, ASTString("enum_prev"), t, b_enum_prev); + } + { rb(env, m, ASTString("mzn_compiler_version"), std::vector(), b_mzn_compiler_version); } + { + std::vector t(2); + t[0] = Type::varint(1); + t[1] = Type::parstring(); + rb(env, m, ASTString("fzn_regular"), t, b_regular_from_string, true); + } +} + +} // namespace MiniZinc diff --git a/lib/c_interface.cpp b/lib/c_interface.cpp new file mode 100644 index 0000000..ba6ac34 --- /dev/null +++ b/lib/c_interface.cpp @@ -0,0 +1,133 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include + +#include +#include + +using namespace MiniZinc; + +class Instance { +public: + Instance(std::string file, std::string data_file, std::string solver) + : slv({"--solver", solver, file, data_file}){}; + + MznSolver slv; + std::string result; +}; + +void set_rnd_seed(int seed) { + dynamic_cast(primitiveMap()[PrimitiveMap::UNIFORM])->setSeed(seed); +} + +MZNInstance minizinc_instance_init(const char* mza_file, const char* data_file, + const char* solver) { + auto inst = new Instance(mza_file, data_file, solver); + return reinterpret_cast(inst); +} + +void minizinc_instance_destroy(MZNInstance _inst) { + auto inst = reinterpret_cast(_inst); + delete inst; +} + +void minizinc_add_call(MZNInstance _inst, const char* call, ...) { + auto inst = reinterpret_cast(_inst); + auto it = inst->slv.resolve_call.find(call); + assert(it != inst->slv.resolve_call.end()); + + std::vector args; + va_list my_args; + va_start(my_args, call); + for (int i = 0; i < it->second.second; ++i) { + int num = va_arg(my_args, int); + args.emplace_back(num); + } + + inst->slv.interpreter->call(it->second.first, std::move(args)); +} + +void minizinc_set_solution(MZNInstance _inst, int def, int sol) { + auto inst = reinterpret_cast(_inst); + auto it = inst->slv.interpreter->solutions.emplace(def, sol); +} + +void minizinc_output_dict(MZNInstance _inst, bool b) { + auto inst = reinterpret_cast(_inst); + inst->slv.output_dict = b; +} + +void minizinc_push_state(MZNInstance _inst) { + auto inst = reinterpret_cast(_inst); + inst->slv.interpreter->trail.save_state(inst->slv.interpreter); + inst->slv.pushToSolver(); +} +void minizinc_pop_state(MZNInstance _inst) { + auto inst = reinterpret_cast(_inst); + inst->slv.interpreter->trail.untrail(inst->slv.interpreter); + inst->slv.popFromSolver(); +} + +void minizinc_print_hedge(MZNInstance _inst) { + auto inst = reinterpret_cast(_inst); + inst->slv.interpreter->dumpState(std::cerr); +} + +void minizinc_set_limit(MZNInstance _inst, int limit) { + auto inst = reinterpret_cast(_inst); + auto opt = static_cast(inst->slv.getSI_OPT()); + opt->nodes = limit; +} + +std::string status_to_string(SolverInstance::Status s) { + switch (s) { + case SolverInstance::OPT: + return "OPT"; + case SolverInstance::SAT: + return "SAT"; + case SolverInstance::UNSAT: + return "UNSAT"; + case SolverInstance::UNBND: + return "UNBND"; + case SolverInstance::UNSATorUNBND: + return "UNSATorUNBND"; + case SolverInstance::UNKNOWN: + return "UNKNOWN"; + case SolverInstance::ERROR: + return "ERROR"; + case SolverInstance::NONE: + return "NONE"; + } +} + +const char* minizinc_solve(MZNInstance _inst) { + auto inst = reinterpret_cast(_inst); + auto result = inst->slv.run(); + + std::stringstream ss; + ss << "{"; + ss << "\"status\": \"" << status_to_string(result.first) << "\","; + if (result.first == MiniZinc::SolverInstance::SAT || + result.first == MiniZinc::SolverInstance::OPT) { + ss << "\"solution\": " << result.second; + } else { + ss << R"("solution": "")"; + } + ss << "}"; + + inst->result = ss.str(); + return inst->result.c_str(); +} diff --git a/lib/cached/lexer.yy.cpp b/lib/cached/lexer.yy.cpp new file mode 100644 index 0000000..0ab949b --- /dev/null +++ b/lib/cached/lexer.yy.cpp @@ -0,0 +1,3540 @@ + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define mzn_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer mzn_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define mzn_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer mzn_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define mzn_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer mzn_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define mzn_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string mzn_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define mzn_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes mzn_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define mzn_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer mzn_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define mzn_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer mzn_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define mzn_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state mzn_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define mzn_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer mzn_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define mzn_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state mzn_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define mzn_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state mzn_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define mzn_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack mzn_yyensure_buffer_stack +#endif + +#ifdef yylex +#define mzn_yylex_ALREADY_DEFINED +#else +#define yylex mzn_yylex +#endif + +#ifdef yyrestart +#define mzn_yyrestart_ALREADY_DEFINED +#else +#define yyrestart mzn_yyrestart +#endif + +#ifdef yylex_init +#define mzn_yylex_init_ALREADY_DEFINED +#else +#define yylex_init mzn_yylex_init +#endif + +#ifdef yylex_init_extra +#define mzn_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra mzn_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define mzn_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy mzn_yylex_destroy +#endif + +#ifdef yyget_debug +#define mzn_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug mzn_yyget_debug +#endif + +#ifdef yyset_debug +#define mzn_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug mzn_yyset_debug +#endif + +#ifdef yyget_extra +#define mzn_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra mzn_yyget_extra +#endif + +#ifdef yyset_extra +#define mzn_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra mzn_yyset_extra +#endif + +#ifdef yyget_in +#define mzn_yyget_in_ALREADY_DEFINED +#else +#define yyget_in mzn_yyget_in +#endif + +#ifdef yyset_in +#define mzn_yyset_in_ALREADY_DEFINED +#else +#define yyset_in mzn_yyset_in +#endif + +#ifdef yyget_out +#define mzn_yyget_out_ALREADY_DEFINED +#else +#define yyget_out mzn_yyget_out +#endif + +#ifdef yyset_out +#define mzn_yyset_out_ALREADY_DEFINED +#else +#define yyset_out mzn_yyset_out +#endif + +#ifdef yyget_leng +#define mzn_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng mzn_yyget_leng +#endif + +#ifdef yyget_text +#define mzn_yyget_text_ALREADY_DEFINED +#else +#define yyget_text mzn_yyget_text +#endif + +#ifdef yyget_lineno +#define mzn_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno mzn_yyget_lineno +#endif + +#ifdef yyset_lineno +#define mzn_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno mzn_yyset_lineno +#endif + +#ifdef yyget_column +#define mzn_yyget_column_ALREADY_DEFINED +#else +#define yyget_column mzn_yyget_column +#endif + +#ifdef yyset_column +#define mzn_yyset_column_ALREADY_DEFINED +#else +#define yyset_column mzn_yyset_column +#endif + +#ifdef yywrap +#define mzn_yywrap_ALREADY_DEFINED +#else +#define yywrap mzn_yywrap +#endif + +#ifdef yyget_lval +#define mzn_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval mzn_yyget_lval +#endif + +#ifdef yyset_lval +#define mzn_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval mzn_yyset_lval +#endif + +#ifdef yyget_lloc +#define mzn_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc mzn_yyget_lloc +#endif + +#ifdef yyset_lloc +#define mzn_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc mzn_yyset_lloc +#endif + +#ifdef yyalloc +#define mzn_yyalloc_ALREADY_DEFINED +#else +#define yyalloc mzn_yyalloc +#endif + +#ifdef yyrealloc +#define mzn_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc mzn_yyrealloc +#endif + +#ifdef yyfree +#define mzn_yyfree_ALREADY_DEFINED +#else +#define yyfree mzn_yyfree +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define mzn_yywrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; +#define YY_NUM_RULES 181 +#define YY_END_OF_BUFFER 182 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[500] = + { 0, + 0, 0, 168, 168, 168, 168, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 182, 180, 3, 2, + 180, 167, 180, 23, 180, 164, 103, 99, 33, 101, + 33, 105, 29, 29, 33, 112, 122, 116, 142, 19, + 180, 21, 107, 37, 180, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 33, 180, 180, 180, 1, 168, + 178, 177, 175, 177, 1, 176, 177, 16, 18, 17, + 1, 6, 8, 7, 1, 11, 13, 12, 1, 165, + 166, 124, 0, 163, 23, 0, 0, 143, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 109, 126, 34, 14, + 134, 0, 29, 0, 0, 0, 36, 128, 114, 111, + 120, 118, 142, 20, 132, 144, 144, 0, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 56, 61, 142, 142, 142, 142, 142, 142, 72, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 142, 142, 142, 22, + 137, 136, 139, 138, 151, 0, 0, 0, 0, 168, + 172, 171, 173, 171, 169, 170, 174, 16, 15, 6, + + 5, 11, 10, 162, 163, 0, 104, 100, 0, 102, + 0, 0, 106, 0, 113, 0, 0, 123, 0, 117, + 0, 0, 108, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 30, 0, 32, 0, 27, 28, 130, + 144, 144, 0, 179, 38, 40, 142, 142, 142, 142, + 142, 142, 46, 142, 142, 142, 142, 142, 142, 142, + 142, 63, 64, 142, 142, 142, 68, 70, 74, 142, + 75, 142, 142, 142, 79, 142, 142, 142, 142, 142, + 142, 142, 142, 142, 142, 142, 94, 142, 97, 152, + 153, 154, 145, 146, 147, 150, 155, 156, 161, 160, + + 157, 158, 159, 148, 149, 162, 125, 110, 127, 35, + 135, 129, 0, 115, 121, 119, 133, 0, 0, 62, + 0, 0, 0, 0, 0, 0, 0, 0, 140, 9, + 0, 0, 0, 0, 144, 142, 142, 42, 43, 142, + 142, 48, 50, 142, 53, 142, 142, 142, 142, 142, + 142, 65, 142, 142, 142, 142, 142, 142, 142, 142, + 142, 142, 142, 88, 89, 24, 142, 91, 142, 142, + 142, 131, 0, 47, 0, 69, 71, 0, 0, 0, + 0, 98, 0, 31, 0, 0, 26, 144, 142, 41, + 142, 142, 142, 52, 25, 54, 142, 142, 142, 142, + + 142, 142, 142, 142, 142, 142, 80, 142, 142, 142, + 142, 90, 92, 142, 96, 49, 0, 0, 0, 0, + 0, 0, 26, 144, 142, 142, 142, 51, 142, 142, + 142, 142, 142, 142, 73, 142, 77, 142, 81, 82, + 142, 142, 142, 0, 0, 0, 0, 93, 144, 142, + 142, 45, 142, 57, 142, 142, 142, 142, 142, 78, + 142, 86, 142, 0, 83, 0, 0, 144, 142, 142, + 55, 58, 142, 66, 67, 142, 84, 142, 0, 0, + 87, 144, 142, 142, 59, 76, 142, 0, 85, 141, + 39, 44, 142, 60, 142, 142, 142, 95, 0 + + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 2, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 5, 6, 1, 7, 8, 1, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, + 19, 19, 19, 19, 19, 20, 20, 21, 14, 22, + 23, 24, 1, 1, 25, 25, 25, 25, 26, 25, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, + 27, 27, 27, 27, 27, 27, 27, 29, 27, 27, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + + 40, 41, 42, 43, 44, 45, 27, 46, 47, 48, + 49, 50, 27, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 14, 60, 14, 61, 1, 62, 1, 1, + 63, 1, 1, 64, 65, 66, 67, 68, 1, 1, + 1, 1, 1, 69, 1, 70, 1, 71, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 72, 1, 73, + 1, 1, 1, 74, 75, 1, 76, 77, 78, 79, + 1, 80, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 81, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 82, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[84] = + { 0, + 1, 1, 2, 3, 1, 4, 5, 1, 1, 1, + 1, 6, 1, 1, 1, 7, 1, 8, 8, 8, + 1, 1, 1, 1, 9, 9, 10, 9, 10, 1, + 4, 1, 1, 11, 12, 9, 9, 9, 9, 9, + 9, 10, 10, 10, 10, 10, 10, 10, 10, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3 + } ; + +static const flex_int16_t yy_base[521] = + { 0, + 0, 0, 81, 85, 89, 93, 91, 95, 97, 98, + 99, 101, 850, 849, 848, 847, 857, 862, 862, 862, + 833, 862, 848, 0, 180, 862, 862, 841, 862, 829, + 836, 74, 99, 111, 830, 99, 827, 826, 0, 788, + 830, 862, 862, 797, 0, 84, 796, 72, 94, 94, + 90, 102, 101, 110, 795, 108, 116, 803, 117, 147, + 794, 805, 797, 790, 806, 194, 757, 148, 862, 0, + 862, 862, 862, 212, 862, 862, 220, 0, 862, 819, + 0, 0, 862, 818, 0, 0, 862, 817, 0, 862, + 862, 862, 0, 0, 0, 124, 138, 862, 168, 222, + + 214, 224, 177, 227, 216, 239, 816, 179, 235, 240, + 243, 245, 247, 250, 254, 284, 862, 862, 862, 820, + 862, 264, 290, 299, 306, 268, 862, 807, 862, 862, + 862, 862, 0, 862, 862, 0, 793, 794, 115, 777, + 778, 774, 777, 783, 124, 771, 207, 776, 772, 772, + 0, 282, 766, 766, 760, 768, 776, 761, 0, 760, + 759, 760, 770, 771, 755, 754, 760, 754, 227, 757, + 751, 762, 747, 750, 749, 754, 746, 756, 744, 862, + 862, 862, 862, 862, 862, 220, 286, 254, 269, 0, + 862, 862, 862, 862, 862, 862, 862, 0, 862, 0, + + 862, 0, 862, 0, 0, 271, 862, 862, 328, 862, + 329, 330, 862, 785, 862, 342, 341, 862, 344, 862, + 345, 784, 862, 346, 347, 358, 359, 348, 360, 361, + 362, 365, 780, 362, 390, 396, 399, 378, 322, 862, + 0, 746, 755, 862, 740, 0, 752, 741, 746, 733, + 748, 742, 0, 742, 737, 733, 727, 742, 739, 730, + 731, 734, 0, 720, 728, 727, 0, 0, 0, 720, + 0, 730, 719, 723, 0, 711, 721, 712, 723, 723, + 708, 712, 719, 712, 717, 707, 711, 703, 0, 862, + 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, + + 862, 862, 862, 862, 862, 0, 862, 862, 862, 862, + 862, 862, 390, 862, 862, 862, 862, 402, 395, 862, + 411, 413, 414, 418, 421, 423, 422, 425, 862, 862, + 445, 418, 419, 459, 713, 699, 693, 0, 0, 697, + 695, 0, 704, 706, 0, 706, 692, 691, 689, 694, + 690, 0, 663, 659, 644, 650, 635, 630, 632, 623, + 630, 600, 606, 0, 0, 0, 598, 0, 586, 597, + 592, 862, 450, 862, 457, 862, 862, 458, 464, 466, + 471, 862, 464, 467, 481, 472, 485, 543, 544, 0, + 528, 532, 536, 0, 0, 0, 532, 536, 530, 521, + + 528, 527, 517, 530, 528, 525, 0, 523, 511, 511, + 517, 0, 0, 509, 0, 862, 497, 498, 500, 502, + 503, 498, 502, 500, 497, 506, 488, 0, 483, 490, + 474, 453, 417, 398, 0, 419, 0, 392, 0, 0, + 408, 400, 376, 504, 505, 514, 515, 862, 359, 351, + 342, 0, 336, 0, 325, 298, 281, 273, 258, 0, + 251, 0, 266, 517, 862, 516, 528, 240, 226, 224, + 0, 0, 216, 0, 0, 227, 0, 206, 529, 530, + 862, 180, 152, 141, 0, 0, 151, 531, 862, 0, + 0, 0, 145, 862, 111, 55, 51, 0, 862, 582, + + 594, 606, 618, 626, 636, 648, 653, 656, 658, 668, + 680, 692, 704, 708, 711, 715, 719, 724, 729, 731 + } ; + +static const flex_int16_t yy_def[521] = + { 0, + 499, 1, 500, 500, 500, 500, 501, 501, 502, 502, + 503, 503, 1, 1, 1, 1, 499, 499, 499, 499, + 499, 499, 504, 505, 506, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 507, 499, + 499, 499, 499, 508, 509, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 499, 499, 499, 499, 499, 510, + 499, 499, 499, 499, 499, 499, 499, 511, 499, 499, + 511, 512, 499, 499, 512, 513, 499, 499, 513, 499, + 499, 499, 514, 515, 505, 506, 506, 499, 506, 506, + + 506, 506, 506, 506, 506, 506, 499, 506, 506, 506, + 506, 506, 506, 506, 506, 506, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 507, 499, 499, 516, 516, 517, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 510, + 499, 499, 499, 499, 499, 499, 499, 511, 499, 512, + + 499, 513, 499, 518, 515, 506, 499, 499, 506, 499, + 506, 506, 499, 499, 499, 506, 506, 499, 506, 499, + 506, 499, 499, 506, 506, 506, 506, 506, 506, 506, + 506, 506, 499, 499, 499, 499, 499, 519, 499, 499, + 516, 516, 517, 499, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + + 499, 499, 499, 499, 499, 518, 499, 499, 499, 499, + 499, 499, 506, 499, 499, 499, 499, 506, 506, 499, + 506, 506, 506, 506, 506, 506, 506, 506, 499, 499, + 499, 520, 520, 499, 516, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 499, 506, 499, 506, 499, 499, 506, 506, 506, + 506, 499, 499, 499, 499, 499, 499, 516, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 499, 506, 506, 506, 506, + 506, 499, 499, 516, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 506, 506, 506, 506, 499, 516, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, + 507, 507, 507, 506, 499, 506, 506, 516, 507, 507, + 507, 507, 507, 507, 507, 507, 507, 507, 506, 506, + 499, 516, 507, 507, 507, 507, 507, 506, 499, 516, + 507, 507, 507, 499, 507, 507, 507, 507, 0, 499, + + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499 + } ; + +static const flex_int16_t yy_nxt[946] = + { 0, + 18, 19, 20, 19, 21, 22, 23, 24, 25, 26, + 18, 27, 28, 29, 30, 31, 32, 33, 34, 34, + 35, 36, 37, 38, 39, 39, 39, 39, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 39, 39, 52, 39, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 39, 39, 65, + 66, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 67, 68, 69, 71, 72, 120, 73, 71, 72, 498, + 73, 71, 72, 79, 76, 71, 72, 79, 76, 83, + + 83, 87, 80, 87, 121, 497, 80, 142, 84, 84, + 88, 74, 88, 128, 122, 74, 123, 123, 123, 77, + 143, 129, 130, 77, 124, 148, 122, 125, 123, 123, + 123, 139, 98, 144, 140, 149, 124, 145, 124, 146, + 153, 147, 151, 150, 154, 155, 98, 126, 159, 152, + 124, 162, 165, 156, 499, 125, 166, 160, 157, 496, + 206, 161, 245, 75, 252, 167, 163, 75, 499, 168, + 169, 75, 246, 81, 170, 75, 207, 81, 253, 85, + 85, 89, 495, 89, 97, 213, 171, 223, 98, 172, + 493, 99, 100, 492, 101, 102, 103, 173, 499, 491, + + 174, 104, 105, 106, 175, 181, 182, 214, 183, 499, + 107, 186, 108, 187, 188, 189, 184, 191, 109, 490, + 192, 193, 210, 110, 218, 191, 111, 112, 192, 197, + 208, 113, 98, 114, 209, 215, 115, 211, 219, 212, + 116, 216, 194, 98, 499, 255, 499, 220, 98, 217, + 194, 98, 499, 98, 499, 98, 487, 499, 98, 195, + 256, 221, 98, 278, 196, 499, 486, 195, 485, 499, + 499, 484, 196, 499, 483, 499, 279, 499, 224, 307, + 499, 234, 234, 234, 499, 239, 239, 225, 290, 291, + 292, 226, 98, 227, 482, 232, 232, 230, 232, 478, + + 228, 499, 231, 477, 229, 122, 232, 123, 123, 123, + 476, 235, 475, 235, 499, 124, 236, 236, 236, 260, + 474, 237, 261, 238, 238, 238, 301, 302, 303, 124, + 238, 238, 304, 305, 262, 473, 308, 309, 310, 239, + 239, 238, 238, 238, 238, 238, 238, 293, 294, 314, + 312, 295, 315, 316, 98, 320, 98, 296, 499, 499, + 499, 297, 298, 299, 300, 313, 98, 98, 98, 98, + 98, 499, 499, 329, 499, 499, 499, 499, 499, 234, + 234, 234, 472, 471, 324, 470, 318, 331, 499, 499, + 499, 499, 499, 333, 469, 499, 322, 325, 372, 321, + + 319, 331, 468, 374, 327, 334, 326, 236, 236, 236, + 98, 323, 328, 236, 236, 236, 332, 332, 332, 98, + 499, 376, 377, 332, 332, 499, 98, 334, 463, 98, + 98, 98, 499, 382, 332, 332, 332, 332, 332, 332, + 462, 499, 373, 499, 499, 385, 385, 461, 499, 460, + 375, 499, 499, 499, 459, 499, 458, 383, 416, 383, + 379, 380, 384, 384, 384, 98, 98, 385, 385, 378, + 381, 386, 98, 386, 98, 457, 387, 387, 387, 98, + 499, 384, 384, 384, 384, 384, 384, 499, 499, 387, + 387, 387, 456, 422, 499, 422, 499, 418, 423, 423, + + 423, 499, 387, 387, 387, 98, 98, 417, 98, 420, + 98, 448, 98, 465, 419, 423, 423, 423, 421, 423, + 423, 423, 98, 98, 98, 98, 455, 499, 499, 454, + 499, 453, 499, 499, 499, 499, 481, 98, 489, 494, + 452, 451, 447, 464, 499, 499, 499, 499, 444, 450, + 445, 446, 449, 466, 479, 467, 443, 442, 499, 499, + 499, 499, 441, 440, 439, 438, 437, 436, 480, 435, + 434, 433, 432, 431, 430, 429, 428, 427, 426, 425, + 424, 488, 70, 70, 70, 70, 70, 70, 70, 70, + 70, 70, 70, 70, 78, 78, 78, 78, 78, 78, + + 78, 78, 78, 78, 78, 78, 82, 82, 82, 82, + 82, 82, 82, 82, 82, 82, 82, 82, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 94, 415, 414, 413, 94, 94, 95, 412, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 96, 411, + 410, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 133, 133, 133, 133, 136, 136, 138, 138, 190, 409, + 408, 407, 190, 190, 190, 190, 190, 190, 190, 190, + 198, 406, 198, 198, 198, 405, 198, 198, 198, 198, + 198, 198, 200, 404, 200, 200, 200, 403, 200, 200, + + 200, 200, 200, 200, 202, 402, 202, 202, 202, 401, + 202, 202, 202, 202, 202, 202, 204, 204, 205, 205, + 205, 205, 241, 241, 241, 241, 243, 243, 243, 243, + 243, 306, 306, 306, 306, 238, 238, 238, 332, 332, + 400, 399, 398, 397, 396, 395, 394, 393, 392, 391, + 390, 389, 388, 371, 370, 369, 368, 367, 366, 365, + 364, 363, 362, 361, 360, 359, 358, 357, 356, 355, + 354, 353, 352, 351, 350, 349, 348, 347, 346, 345, + 344, 343, 342, 341, 340, 339, 338, 337, 336, 244, + 335, 330, 317, 311, 289, 288, 287, 286, 285, 284, + + 283, 282, 281, 280, 277, 276, 275, 274, 273, 272, + 271, 270, 269, 268, 267, 266, 265, 264, 263, 259, + 258, 257, 254, 251, 250, 249, 248, 247, 244, 242, + 240, 233, 222, 203, 201, 199, 185, 180, 179, 178, + 177, 176, 164, 158, 141, 137, 135, 134, 132, 131, + 127, 119, 118, 117, 93, 92, 499, 91, 91, 90, + 90, 17, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499 + } ; + +static const flex_int16_t yy_chk[946] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 3, 32, 3, 4, 4, 497, + 4, 5, 5, 7, 5, 6, 6, 8, 6, 9, + + 10, 11, 7, 12, 32, 496, 8, 48, 9, 10, + 11, 3, 12, 36, 33, 4, 33, 33, 33, 5, + 48, 36, 36, 6, 33, 51, 34, 33, 34, 34, + 34, 46, 96, 49, 46, 51, 34, 49, 33, 50, + 53, 50, 52, 51, 53, 54, 97, 33, 56, 52, + 34, 57, 59, 54, 96, 33, 59, 56, 54, 495, + 97, 56, 139, 3, 145, 59, 57, 4, 97, 59, + 59, 5, 139, 7, 59, 6, 99, 8, 145, 9, + 10, 11, 493, 12, 25, 103, 60, 108, 25, 60, + 487, 25, 25, 484, 25, 25, 25, 60, 99, 483, + + 60, 25, 25, 25, 60, 66, 66, 103, 66, 108, + 25, 68, 25, 68, 68, 68, 66, 74, 25, 482, + 74, 74, 101, 25, 105, 77, 25, 25, 77, 77, + 100, 25, 102, 25, 100, 104, 25, 101, 105, 102, + 25, 104, 74, 109, 101, 147, 105, 106, 110, 104, + 77, 111, 100, 112, 102, 113, 478, 104, 114, 74, + 147, 106, 115, 169, 74, 109, 476, 77, 473, 106, + 110, 470, 77, 111, 469, 112, 169, 113, 109, 206, + 114, 122, 122, 122, 115, 126, 126, 110, 186, 186, + 186, 111, 116, 112, 468, 116, 116, 114, 116, 463, + + 113, 206, 115, 461, 113, 123, 116, 123, 123, 123, + 459, 124, 458, 124, 116, 123, 124, 124, 124, 152, + 457, 125, 152, 125, 125, 125, 188, 188, 188, 123, + 125, 125, 189, 189, 152, 456, 209, 211, 212, 239, + 239, 125, 125, 125, 125, 125, 125, 187, 187, 217, + 216, 187, 219, 221, 224, 225, 228, 187, 209, 211, + 212, 187, 187, 187, 187, 216, 226, 227, 229, 230, + 231, 217, 216, 232, 219, 221, 224, 225, 228, 234, + 234, 234, 455, 453, 228, 451, 224, 234, 226, 227, + 229, 230, 231, 238, 450, 232, 226, 228, 313, 225, + + 224, 234, 449, 319, 230, 238, 229, 235, 235, 235, + 318, 227, 231, 236, 236, 236, 237, 237, 237, 321, + 313, 322, 323, 237, 237, 319, 324, 238, 443, 325, + 327, 326, 318, 328, 237, 237, 237, 237, 237, 237, + 442, 321, 318, 322, 323, 332, 333, 441, 324, 438, + 321, 325, 327, 326, 436, 328, 434, 331, 373, 331, + 325, 326, 331, 331, 331, 375, 378, 332, 333, 324, + 327, 334, 379, 334, 380, 433, 334, 334, 334, 381, + 373, 383, 383, 383, 384, 384, 384, 375, 378, 386, + 386, 386, 432, 385, 379, 385, 380, 378, 385, 385, + + 385, 381, 387, 387, 387, 417, 418, 375, 419, 380, + 420, 421, 444, 445, 379, 422, 422, 422, 381, 423, + 423, 423, 446, 447, 466, 464, 431, 417, 418, 430, + 419, 429, 420, 421, 444, 445, 467, 479, 480, 488, + 427, 426, 420, 444, 446, 447, 466, 464, 417, 425, + 418, 419, 424, 446, 464, 447, 414, 411, 467, 479, + 480, 488, 410, 409, 408, 406, 405, 404, 466, 403, + 402, 401, 400, 399, 398, 397, 393, 392, 391, 389, + 388, 479, 500, 500, 500, 500, 500, 500, 500, 500, + 500, 500, 500, 500, 501, 501, 501, 501, 501, 501, + + 501, 501, 501, 501, 501, 501, 502, 502, 502, 502, + 502, 502, 502, 502, 502, 502, 502, 502, 503, 503, + 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, + 504, 371, 370, 369, 504, 504, 505, 367, 505, 505, + 505, 505, 505, 505, 505, 505, 505, 505, 506, 363, + 362, 506, 506, 506, 506, 506, 506, 506, 506, 506, + 507, 507, 507, 507, 508, 508, 509, 509, 510, 361, + 360, 359, 510, 510, 510, 510, 510, 510, 510, 510, + 511, 358, 511, 511, 511, 357, 511, 511, 511, 511, + 511, 511, 512, 356, 512, 512, 512, 355, 512, 512, + + 512, 512, 512, 512, 513, 354, 513, 513, 513, 353, + 513, 513, 513, 513, 513, 513, 514, 514, 515, 515, + 515, 515, 516, 516, 516, 516, 517, 517, 517, 517, + 517, 518, 518, 518, 518, 519, 519, 519, 520, 520, + 351, 350, 349, 348, 347, 346, 344, 343, 341, 340, + 337, 336, 335, 288, 287, 286, 285, 284, 283, 282, + 281, 280, 279, 278, 277, 276, 274, 273, 272, 270, + 266, 265, 264, 262, 261, 260, 259, 258, 257, 256, + 255, 254, 252, 251, 250, 249, 248, 247, 245, 243, + 242, 233, 222, 214, 179, 178, 177, 176, 175, 174, + + 173, 172, 171, 170, 168, 167, 166, 165, 164, 163, + 162, 161, 160, 158, 157, 156, 155, 154, 153, 150, + 149, 148, 146, 144, 143, 142, 141, 140, 138, 137, + 128, 120, 107, 88, 84, 80, 67, 65, 64, 63, + 62, 61, 58, 55, 47, 44, 41, 40, 38, 37, + 35, 31, 30, 28, 23, 21, 17, 16, 15, 14, + 13, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, + 499, 499, 499, 499, 499 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + * Main authors: + * Guido Tack + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#if defined __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wdeprecated" +#elif defined _MSC_VER +#pragma warning(push, 1) +#endif + +namespace MiniZinc{ class ParserLocation; } +#define YYLTYPE MiniZinc::ParserLocation +#define YYLTYPE_IS_DECLARED 1 +#define YYLTYPE_IS_TRIVIAL 0 + +#include + +int utf8len(const char* s) { + int l=0; + for (int i=0; s[i] != '\0'; i++) + if ((s[i] & 0xc0) != 0x80) + l++; + return l; +} + +int yy_input_proc(char* buf, int size, yyscan_t yyscanner); +#define YY_INPUT(buf, result, max_size) \ + result = yy_input_proc(buf, max_size, yyscanner); + +#define YY_USER_ACTION \ + { MiniZinc::ParserState* parm = \ + static_cast(yyget_extra(yyscanner)); \ + yylloc->first_line(yylloc->last_line()); \ + yylloc->first_column(yylloc->last_column()+1); \ + if(parm->hadNewline) { \ + parm->hadNewline=false; \ + parm->lineStartPos += parm->nTokenNextStart; \ + parm->nTokenNextStart=1; \ + yylloc->last_line(yylloc->last_line()+1); \ + yylloc->first_line(yylloc->last_line()); \ + yylloc->first_column(1); \ + } \ + if(yytext[0] == '\n') { \ + parm->hadNewline=true; \ + parm->nTokenNextStart+=0; \ + } else { \ + parm->nTokenNextStart+=yyleng; \ + } \ + yylloc->last_column(yylloc->first_column()+utf8len(yytext)-1); \ + } + +bool hexstrtointval(const char* s, long long int& v) { + std::istringstream iss(s); + iss >> std::hex >> v; + return !iss.fail(); +} + +bool octstrtointval(const char* s, long long int& v) { + std::istringstream iss(s); + iss >> std::oct >> v; + return !iss.fail(); +} + +bool fast_strtointval(const char* s, long long int& v) { + MiniZinc::IntVal x = 0; + try { + for (; *s != '\0'; ++s) { + x = (x*10) + (*s - '0'); + } + } catch (MiniZinc::ArithmeticError&) { + return false; + } + v = x.toInt(); + return true; +} + +bool strtofloatval(const char* s, double& v) { + std::istringstream iss(s); + iss >> v; + return !iss.fail(); +} + +void clearBuffer(void* parm) { + MiniZinc::ParserState* pp = + static_cast(parm); + pp->stringBuffer = ""; +} + +void appendBufferString(void* parm, const char* s) { + MiniZinc::ParserState* pp = + static_cast(parm); + pp->stringBuffer += s; +} + +void appendBufferChar(void* parm, char s) { + MiniZinc::ParserState* pp = + static_cast(parm); + pp->stringBuffer += s; +} + +char* bufferData(void* parm) { + MiniZinc::ParserState* pp = + static_cast(parm); + return strdup(pp->stringBuffer.c_str()); +} + +#define INITIAL 0 +#define string 1 +#define string_quote 2 +#define multilinecomment 3 +#define doccomment 4 +#define doccomment_file 5 +#define bracket_exp 6 +#define quoted_exp 7 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +static int yy_init_globals ( yyscan_t yyscanner ); + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr , yyscan_t yyscanner); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif + +#endif + + static void yy_push_state ( int _new_state , yyscan_t yyscanner); + + static void yy_pop_state ( yyscan_t yyscanner ); + + static int yy_top_state ( yyscan_t yyscanner ); + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 500 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 862 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +{ return MZN_INVALID_NULL; } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +{ } + YY_BREAK +case 3: +YY_RULE_SETUP +{ /* ignore whitespace */ } + YY_BREAK +case 4: +YY_RULE_SETUP +{ yy_push_state(doccomment,yyscanner); clearBuffer(yyget_extra(yyscanner)); } + YY_BREAK + +case 5: +YY_RULE_SETUP +{ yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_DOC_COMMENT; } + YY_BREAK +case 6: +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK +case 7: +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK + +case 9: +YY_RULE_SETUP +{ yy_push_state(doccomment_file,yyscanner); clearBuffer(yyget_extra(yyscanner)); } + YY_BREAK + +case 10: +YY_RULE_SETUP +{ yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_DOC_FILE_COMMENT; } + YY_BREAK +case 11: +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK +case 12: +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK +case 13: +/* rule 13 can match eol */ +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK + +case 14: +YY_RULE_SETUP +{ yy_push_state(multilinecomment,yyscanner); } + YY_BREAK + +case 15: +YY_RULE_SETUP +{ yy_pop_state(yyscanner); } + YY_BREAK +case 16: +YY_RULE_SETUP +{ } + YY_BREAK +case 17: +YY_RULE_SETUP +{ } + YY_BREAK +case 18: +/* rule 18 can match eol */ +YY_RULE_SETUP +{ } + YY_BREAK + +case 19: +YY_RULE_SETUP +{ return MZN_LEFT_BRACKET; } + YY_BREAK +case 20: +YY_RULE_SETUP +{ return MZN_LEFT_2D_BRACKET; } + YY_BREAK +case 21: +YY_RULE_SETUP +{ return MZN_RIGHT_BRACKET; } + YY_BREAK +case 22: +YY_RULE_SETUP +{ return MZN_RIGHT_2D_BRACKET; } + YY_BREAK +case 23: +YY_RULE_SETUP +{ /* ignore comments */ } + YY_BREAK +case 24: +YY_RULE_SETUP +{ yylval->iValue = 1; return MZN_BOOL_LITERAL; } + YY_BREAK +case 25: +YY_RULE_SETUP +{ yylval->iValue = 0; return MZN_BOOL_LITERAL; } + YY_BREAK +case 26: +YY_RULE_SETUP +{ + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } + YY_BREAK +case 27: +YY_RULE_SETUP +{ + if (hexstrtointval(yytext+2, yylval->iValue)) + return MZN_INTEGER_LITERAL; + else + return MZN_INVALID_INTEGER_LITERAL; + } + YY_BREAK +case 28: +YY_RULE_SETUP +{ + if (octstrtointval(yytext+2, yylval->iValue)) + return MZN_INTEGER_LITERAL; + else + return MZN_INVALID_INTEGER_LITERAL; + } + YY_BREAK +case 29: +YY_RULE_SETUP +{ + if (fast_strtointval(yytext, yylval->iValue)) + return MZN_INTEGER_LITERAL; + else + return MZN_INVALID_INTEGER_LITERAL; + } + YY_BREAK +case 30: +YY_RULE_SETUP +{ + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } + YY_BREAK +case 31: +YY_RULE_SETUP +{ + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } + YY_BREAK +case 32: +YY_RULE_SETUP +{ + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } + YY_BREAK +case 33: +YY_RULE_SETUP +{ + return *yytext; } + YY_BREAK +case 34: +YY_RULE_SETUP +{ return MZN_DOTDOT; } + YY_BREAK +case 35: +YY_RULE_SETUP +{ return MZN_DOTDOT_QUOTED; } + YY_BREAK +case 36: +YY_RULE_SETUP +{ return MZN_COLONCOLON; } + YY_BREAK +case 37: +YY_RULE_SETUP +{ return MZN_UNDERSCORE; } + YY_BREAK +case 38: +YY_RULE_SETUP +{ return MZN_ANN; } + YY_BREAK +case 39: +YY_RULE_SETUP +{ return MZN_ANNOTATION; } + YY_BREAK +case 40: +YY_RULE_SETUP +{ return MZN_ANY; } + YY_BREAK +case 41: +YY_RULE_SETUP +{ return MZN_ARRAY; } + YY_BREAK +case 42: +YY_RULE_SETUP +{ return MZN_BOOL; } + YY_BREAK +case 43: +YY_RULE_SETUP +{ return MZN_CASE; } + YY_BREAK +case 44: +YY_RULE_SETUP +{ return MZN_CONSTRAINT; } + YY_BREAK +case 45: +YY_RULE_SETUP +{ return MZN_DEFAULT; } + YY_BREAK +case 46: +YY_RULE_SETUP +{ return MZN_IDIV; } + YY_BREAK +case 47: +YY_RULE_SETUP +{ return MZN_IDIV_QUOTED; } + YY_BREAK +case 48: +YY_RULE_SETUP +{ return MZN_DIFF; } + YY_BREAK +case 49: +YY_RULE_SETUP +{ return MZN_DIFF_QUOTED; } + YY_BREAK +case 50: +YY_RULE_SETUP +{ return MZN_ELSE; } + YY_BREAK +case 51: +YY_RULE_SETUP +{ return MZN_ELSEIF; } + YY_BREAK +case 52: +YY_RULE_SETUP +{ return MZN_ENDIF; } + YY_BREAK +case 53: +YY_RULE_SETUP +{ return MZN_ENUM; } + YY_BREAK +case 54: +YY_RULE_SETUP +{ return MZN_FLOAT; } + YY_BREAK +case 55: +YY_RULE_SETUP +{ return MZN_FUNCTION; } + YY_BREAK +case 56: +YY_RULE_SETUP +{ return MZN_IF; } + YY_BREAK +case 57: +YY_RULE_SETUP +{ return MZN_INCLUDE; } + YY_BREAK +case 58: +YY_RULE_SETUP +{ return MZN_INFINITY; } + YY_BREAK +case 59: +YY_RULE_SETUP +{ return MZN_INTERSECT; } + YY_BREAK +case 60: +YY_RULE_SETUP +{ return MZN_INTERSECT_QUOTED; } + YY_BREAK +case 61: +YY_RULE_SETUP +{ return MZN_IN; } + YY_BREAK +case 62: +YY_RULE_SETUP +{ return MZN_IN_QUOTED; } + YY_BREAK +case 63: +YY_RULE_SETUP +{ return MZN_INT; } + YY_BREAK +case 64: +YY_RULE_SETUP +{ return MZN_LET; } + YY_BREAK +case 65: +YY_RULE_SETUP +{ return MZN_LIST; } + YY_BREAK +case 66: +YY_RULE_SETUP +{ yylval->bValue = false; return MZN_MAXIMIZE; } + YY_BREAK +case 67: +YY_RULE_SETUP +{ yylval->bValue = true; return MZN_MINIMIZE; } + YY_BREAK +case 68: +YY_RULE_SETUP +{ return MZN_MOD; } + YY_BREAK +case 69: +YY_RULE_SETUP +{ return MZN_MOD_QUOTED; } + YY_BREAK +case 70: +YY_RULE_SETUP +{ return MZN_NOT; } + YY_BREAK +case 71: +YY_RULE_SETUP +{ return MZN_NOT_QUOTED; } + YY_BREAK +case 72: +YY_RULE_SETUP +{ return MZN_OF; } + YY_BREAK +case 73: +YY_RULE_SETUP +{ return MZN_OUTPUT; } + YY_BREAK +case 74: +YY_RULE_SETUP +{ return MZN_OPT; } + YY_BREAK +case 75: +YY_RULE_SETUP +{ return MZN_PAR; } + YY_BREAK +case 76: +YY_RULE_SETUP +{ return MZN_PREDICATE; } + YY_BREAK +case 77: +YY_RULE_SETUP +{ return MZN_RECORD; } + YY_BREAK +case 78: +YY_RULE_SETUP +{ return MZN_SATISFY; } + YY_BREAK +case 79: +YY_RULE_SETUP +{ return MZN_SET; } + YY_BREAK +case 80: +YY_RULE_SETUP +{ return MZN_SOLVE; } + YY_BREAK +case 81: +YY_RULE_SETUP +{ return MZN_STRING; } + YY_BREAK +case 82: +YY_RULE_SETUP +{ return MZN_SUBSET; } + YY_BREAK +case 83: +YY_RULE_SETUP +{ return MZN_SUBSET_QUOTED; } + YY_BREAK +case 84: +YY_RULE_SETUP +{ return MZN_SUPERSET; } + YY_BREAK +case 85: +YY_RULE_SETUP +{ return MZN_SUPERSET_QUOTED; } + YY_BREAK +case 86: +YY_RULE_SETUP +{ return MZN_SYMDIFF; } + YY_BREAK +case 87: +YY_RULE_SETUP +{ return MZN_SYMDIFF_QUOTED; } + YY_BREAK +case 88: +YY_RULE_SETUP +{ return MZN_TEST; } + YY_BREAK +case 89: +YY_RULE_SETUP +{ return MZN_THEN; } + YY_BREAK +case 90: +YY_RULE_SETUP +{ return MZN_TUPLE; } + YY_BREAK +case 91: +YY_RULE_SETUP +{ return MZN_TYPE; } + YY_BREAK +case 92: +YY_RULE_SETUP +{ return MZN_UNION; } + YY_BREAK +case 93: +YY_RULE_SETUP +{ return MZN_UNION_QUOTED; } + YY_BREAK +case 94: +YY_RULE_SETUP +{ return MZN_VAR; } + YY_BREAK +case 95: +YY_RULE_SETUP +{ return MZN_VARIANT_RECORD; } + YY_BREAK +case 96: +YY_RULE_SETUP +{ return MZN_WHERE; } + YY_BREAK +case 97: +YY_RULE_SETUP +{ return MZN_XOR; } + YY_BREAK +case 98: +YY_RULE_SETUP +{ return MZN_XOR_QUOTED; } + YY_BREAK +case 99: +YY_RULE_SETUP +{ return MZN_PLUS; } + YY_BREAK +case 100: +YY_RULE_SETUP +{ return MZN_PLUS_QUOTED; } + YY_BREAK +case 101: +YY_RULE_SETUP +{ return MZN_MINUS; } + YY_BREAK +case 102: +YY_RULE_SETUP +{ return MZN_MINUS_QUOTED; } + YY_BREAK +case 103: +YY_RULE_SETUP +{ return MZN_MULT; } + YY_BREAK +case 104: +YY_RULE_SETUP +{ return MZN_MULT_QUOTED; } + YY_BREAK +case 105: +YY_RULE_SETUP +{ return MZN_DIV; } + YY_BREAK +case 106: +YY_RULE_SETUP +{ return MZN_DIV_QUOTED; } + YY_BREAK +case 107: +YY_RULE_SETUP +{ return MZN_POW; } + YY_BREAK +case 108: +YY_RULE_SETUP +{ return MZN_POW_QUOTED; } + YY_BREAK +case 109: +YY_RULE_SETUP +{ return MZN_PLUSPLUS; } + YY_BREAK +case 110: +YY_RULE_SETUP +{ return MZN_PLUSPLUS_QUOTED; } + YY_BREAK +case 111: +YY_RULE_SETUP +{ return MZN_ABSENT; } + YY_BREAK +case 112: +YY_RULE_SETUP +{ return MZN_LE; } + YY_BREAK +case 113: +YY_RULE_SETUP +{ return MZN_LE_QUOTED; } + YY_BREAK +case 114: +YY_RULE_SETUP +{ return MZN_LQ; } + YY_BREAK +case 115: +YY_RULE_SETUP +{ return MZN_LQ_QUOTED; } + YY_BREAK +case 116: +YY_RULE_SETUP +{ return MZN_GR; } + YY_BREAK +case 117: +YY_RULE_SETUP +{ return MZN_GR_QUOTED; } + YY_BREAK +case 118: +YY_RULE_SETUP +{ return MZN_GQ; } + YY_BREAK +case 119: +YY_RULE_SETUP +{ return MZN_GQ_QUOTED; } + YY_BREAK +case 120: +YY_RULE_SETUP +{ return MZN_EQ; } + YY_BREAK +case 121: +YY_RULE_SETUP +{ return MZN_EQ_QUOTED; } + YY_BREAK +case 122: +YY_RULE_SETUP +{ return MZN_EQ; } + YY_BREAK +case 123: +YY_RULE_SETUP +{ return MZN_EQ_QUOTED; } + YY_BREAK +case 124: +YY_RULE_SETUP +{ return MZN_NQ; } + YY_BREAK +case 125: +YY_RULE_SETUP +{ return MZN_NQ_QUOTED; } + YY_BREAK +case 126: +YY_RULE_SETUP +{ return MZN_IMPL; } + YY_BREAK +case 127: +YY_RULE_SETUP +{ return MZN_IMPL_QUOTED; } + YY_BREAK +case 128: +YY_RULE_SETUP +{ return MZN_RIMPL; } + YY_BREAK +case 129: +YY_RULE_SETUP +{ return MZN_RIMPL_QUOTED; } + YY_BREAK +case 130: +YY_RULE_SETUP +{ return MZN_EQUIV; } + YY_BREAK +case 131: +YY_RULE_SETUP +{ return MZN_EQUIV_QUOTED; } + YY_BREAK +case 132: +YY_RULE_SETUP +{ return MZN_OR; } + YY_BREAK +case 133: +YY_RULE_SETUP +{ return MZN_OR_QUOTED; } + YY_BREAK +case 134: +YY_RULE_SETUP +{ return MZN_AND; } + YY_BREAK +case 135: +YY_RULE_SETUP +{ return MZN_AND_QUOTED; } + YY_BREAK +case 136: +YY_RULE_SETUP +{ return MZN_WEAK_PLUS; } + YY_BREAK +case 137: +YY_RULE_SETUP +{ return MZN_WEAK_MULT; } + YY_BREAK +case 138: +YY_RULE_SETUP +{ return MZN_WEAK_EQ; } + YY_BREAK +case 139: +YY_RULE_SETUP +{ return MZN_WEAK_MINUS; } + YY_BREAK +case 140: +YY_RULE_SETUP +{ + yylval->sValue = strdup(yytext+1); + yylval->sValue[strlen(yytext)-2] = 0; + return MZN_IDENTIFIER; } + YY_BREAK +case 141: +YY_RULE_SETUP +{ yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; } + YY_BREAK +case 142: +YY_RULE_SETUP +{ + yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; } + YY_BREAK +case 143: +YY_RULE_SETUP +{ + yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; } + YY_BREAK +case 144: +YY_RULE_SETUP +{ + MiniZinc::ParserState* parm = + static_cast(yyget_extra(yyscanner)); + if (parm->isFlatZinc) { + yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; + } else { + return FLATZINC_IDENTIFIER; + } + } + YY_BREAK +case 145: +YY_RULE_SETUP +{ yylval->sValue = strdup("forall"); return MZN_IDENTIFIER; } + YY_BREAK +case 146: +YY_RULE_SETUP +{ yylval->sValue = strdup("exists"); return MZN_IDENTIFIER; } + YY_BREAK +case 147: +YY_RULE_SETUP +{ return MZN_IN; } + YY_BREAK +case 148: +YY_RULE_SETUP +{ return MZN_SUBSET; } + YY_BREAK +case 149: +YY_RULE_SETUP +{ return MZN_SUPERSET; } + YY_BREAK +case 150: +YY_RULE_SETUP +{ return MZN_INFINITY; } + YY_BREAK +case 151: +YY_RULE_SETUP +{ return MZN_NOT; } + YY_BREAK +case 152: +YY_RULE_SETUP +{ return MZN_RIMPL; } + YY_BREAK +case 153: +YY_RULE_SETUP +{ return MZN_IMPL; } + YY_BREAK +case 154: +YY_RULE_SETUP +{ return MZN_EQUIV; } + YY_BREAK +case 155: +YY_RULE_SETUP +{ return MZN_AND; } + YY_BREAK +case 156: +YY_RULE_SETUP +{ return MZN_OR; } + YY_BREAK +case 157: +YY_RULE_SETUP +{ return MZN_NQ; } + YY_BREAK +case 158: +YY_RULE_SETUP +{ return MZN_LQ; } + YY_BREAK +case 159: +YY_RULE_SETUP +{ return MZN_GQ; } + YY_BREAK +case 160: +YY_RULE_SETUP +{ return MZN_UNION; } + YY_BREAK +case 161: +YY_RULE_SETUP +{ return MZN_INTERSECT; } + YY_BREAK +case 162: +YY_RULE_SETUP +{ + yylval->sValue = strdup(yytext+1); return MZN_TI_ENUM_IDENTIFIER; } + YY_BREAK +case 163: +YY_RULE_SETUP +{ + yylval->sValue = strdup(yytext+1); return MZN_TI_IDENTIFIER; } + YY_BREAK +case 164: +YY_RULE_SETUP +{ yy_push_state(bracket_exp,yyscanner); return *yytext; } + YY_BREAK +case 165: +YY_RULE_SETUP +{ yy_pop_state(yyscanner); return *yytext; } + YY_BREAK +case 166: +YY_RULE_SETUP +{ yy_pop_state(yyscanner); yy_pop_state(yyscanner); yy_push_state(string_quote,yyscanner); + clearBuffer(yyget_extra(yyscanner)); } + YY_BREAK +case 167: +YY_RULE_SETUP +{ yy_push_state(string,yyscanner); clearBuffer(yyget_extra(yyscanner)); } + YY_BREAK +case 168: +YY_RULE_SETUP +{ appendBufferString(yyget_extra(yyscanner), yytext); } + YY_BREAK +case 169: +YY_RULE_SETUP +{ appendBufferChar(yyget_extra(yyscanner), '\n'); } + YY_BREAK +case 170: +YY_RULE_SETUP +{ appendBufferChar(yyget_extra(yyscanner), '\t'); } + YY_BREAK +case 171: +YY_RULE_SETUP +{ appendBufferChar(yyget_extra(yyscanner), yytext[1]); } + YY_BREAK +case 172: +YY_RULE_SETUP +{ appendBufferChar(yyget_extra(yyscanner), yytext[1]); } + YY_BREAK +case 173: +YY_RULE_SETUP +{ yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_push_state(quoted_exp,yyscanner); return MZN_STRING_QUOTE_START; } + YY_BREAK +case 174: +YY_RULE_SETUP +{ yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_push_state(quoted_exp,yyscanner); return MZN_STRING_QUOTE_MID; } + YY_BREAK +case 175: +YY_RULE_SETUP +{ yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_STRING_LITERAL; } + YY_BREAK +case 176: +YY_RULE_SETUP +{ yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_STRING_QUOTE_END; } + YY_BREAK +case 177: +YY_RULE_SETUP +{ return (unsigned char)yytext[0]; } + YY_BREAK +case 178: +/* rule 178 can match eol */ +YY_RULE_SETUP +{ return MZN_END_OF_LINE_IN_STRING; } + YY_BREAK +case YY_STATE_EOF(string): +case YY_STATE_EOF(string_quote): +{ yy_pop_state(yyscanner); return MZN_UNTERMINATED_STRING; } + YY_BREAK +case 179: +YY_RULE_SETUP +{ + yylval->sValue = strdup(yytext+1); + yylval->sValue[strlen(yytext)-2] = 0; + return MZN_QUOTED_IDENTIFIER; } + YY_BREAK +case 180: +YY_RULE_SETUP +{ return (unsigned char)yytext[0]; } + YY_BREAK +case 181: +YY_RULE_SETUP +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(multilinecomment): +case YY_STATE_EOF(doccomment): +case YY_STATE_EOF(doccomment_file): +case YY_STATE_EOF(bracket_exp): +case YY_STATE_EOF(quoted_exp): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 83); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 500 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 83; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 500 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 499); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp , yyscan_t yyscanner) +{ + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + int number_to_move = yyg->yy_n_chars + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void yy_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void yypop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + + static void yy_push_state (int _new_state , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr >= yyg->yy_start_stack_depth ) + { + yy_size_t new_size; + + yyg->yy_start_stack_depth += YY_START_STACK_INCR; + new_size = (yy_size_t) yyg->yy_start_stack_depth * sizeof( int ); + + if ( ! yyg->yy_start_stack ) + yyg->yy_start_stack = (int *) yyalloc( new_size , yyscanner ); + + else + yyg->yy_start_stack = (int *) yyrealloc( + (void *) yyg->yy_start_stack, new_size , yyscanner ); + + if ( ! yyg->yy_start_stack ) + YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); + } + + yyg->yy_start_stack[yyg->yy_start_stack_ptr++] = YY_START; + + BEGIN(_new_state); +} + + static void yy_pop_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( --yyg->yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yyg->yy_start_stack[yyg->yy_start_stack_ptr]); +} + + static int yy_top_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyg->yy_start_stack[yyg->yy_start_stack_ptr - 1]; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +int yy_input_proc(char* buf, int size, yyscan_t yyscanner) { + MiniZinc::ParserState* parm = + static_cast(yyget_extra(yyscanner)); + return parm->fillBuffer(buf, size); + // work around warning that yyunput is unused + yyunput (0,buf,yyscanner); +} + diff --git a/lib/cached/md5_cached.cmake b/lib/cached/md5_cached.cmake new file mode 100644 index 0000000..0fb3afa --- /dev/null +++ b/lib/cached/md5_cached.cmake @@ -0,0 +1,6 @@ +set(lexer_lxx_md5_cached "4df2e64a04ae95de46bf3d1211997e4d") +set(parser_yxx_md5_cached "fd6255424f75e259f5b8a2c51a2c375d") +set(regex_lexer_lxx_md5_cached "e1a1ab28ed8f0bf77b83431b404333e8") +set(regex_parser_yxx_md5_cached "01499f2c4bd0bcb0364b117b8e10a34e") +set(mza_lexer_lxx_md5_cached "50b781fe1af7ca319cb3e35c1653c72b") +set(mza_parser_yxx_md5_cached "18d4d6b5e5a2b4eac0c0b835e388b479") \ No newline at end of file diff --git a/lib/cached/minizinc/parser.tab.hh b/lib/cached/minizinc/parser.tab.hh new file mode 100644 index 0000000..65225c7 --- /dev/null +++ b/lib/cached/minizinc/parser.tab.hh @@ -0,0 +1,236 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_MZN_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_PARSER_TAB_HH_INCLUDED +# define YY_MZN_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_PARSER_TAB_HH_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int mzn_yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + END = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + MZN_INTEGER_LITERAL = 258, /* "integer literal" */ + MZN_BOOL_LITERAL = 259, /* "bool literal" */ + MZN_FLOAT_LITERAL = 260, /* "float literal" */ + MZN_IDENTIFIER = 261, /* "identifier" */ + MZN_QUOTED_IDENTIFIER = 262, /* "quoted identifier" */ + MZN_STRING_LITERAL = 263, /* "string literal" */ + MZN_STRING_QUOTE_START = 264, /* "interpolated string start" */ + MZN_STRING_QUOTE_MID = 265, /* "interpolated string middle" */ + MZN_STRING_QUOTE_END = 266, /* "interpolated string end" */ + MZN_TI_IDENTIFIER = 267, /* "type-inst identifier" */ + MZN_TI_ENUM_IDENTIFIER = 268, /* "type-inst enum identifier" */ + MZN_DOC_COMMENT = 269, /* "documentation comment" */ + MZN_DOC_FILE_COMMENT = 270, /* "file-level documentation comment" */ + MZN_VAR = 271, /* "var" */ + MZN_PAR = 272, /* "par" */ + MZN_ABSENT = 273, /* "<>" */ + MZN_ANN = 274, /* "ann" */ + MZN_ANNOTATION = 275, /* "annotation" */ + MZN_ANY = 276, /* "any" */ + MZN_ARRAY = 277, /* "array" */ + MZN_BOOL = 278, /* "bool" */ + MZN_CASE = 279, /* "case" */ + MZN_CONSTRAINT = 280, /* "constraint" */ + MZN_DEFAULT = 281, /* "default" */ + MZN_ELSE = 282, /* "else" */ + MZN_ELSEIF = 283, /* "elseif" */ + MZN_ENDIF = 284, /* "endif" */ + MZN_ENUM = 285, /* "enum" */ + MZN_FLOAT = 286, /* "float" */ + MZN_FUNCTION = 287, /* "function" */ + MZN_IF = 288, /* "if" */ + MZN_INCLUDE = 289, /* "include" */ + MZN_INFINITY = 290, /* "infinity" */ + MZN_INT = 291, /* "int" */ + MZN_LET = 292, /* "let" */ + MZN_LIST = 293, /* "list" */ + MZN_MAXIMIZE = 294, /* "maximize" */ + MZN_MINIMIZE = 295, /* "minimize" */ + MZN_OF = 296, /* "of" */ + MZN_OPT = 297, /* "opt" */ + MZN_SATISFY = 298, /* "satisfy" */ + MZN_OUTPUT = 299, /* "output" */ + MZN_PREDICATE = 300, /* "predicate" */ + MZN_RECORD = 301, /* "record" */ + MZN_SET = 302, /* "set" */ + MZN_SOLVE = 303, /* "solve" */ + MZN_STRING = 304, /* "string" */ + MZN_TEST = 305, /* "test" */ + MZN_THEN = 306, /* "then" */ + MZN_TUPLE = 307, /* "tuple" */ + MZN_TYPE = 308, /* "type" */ + MZN_UNDERSCORE = 309, /* "_" */ + MZN_VARIANT_RECORD = 310, /* "variant_record" */ + MZN_WHERE = 311, /* "where" */ + MZN_LEFT_BRACKET = 312, /* "[" */ + MZN_LEFT_2D_BRACKET = 313, /* "[|" */ + MZN_RIGHT_BRACKET = 314, /* "]" */ + MZN_RIGHT_2D_BRACKET = 315, /* "|]" */ + FLATZINC_IDENTIFIER = 316, /* FLATZINC_IDENTIFIER */ + MZN_INVALID_INTEGER_LITERAL = 317, /* "invalid integer literal" */ + MZN_INVALID_FLOAT_LITERAL = 318, /* "invalid float literal" */ + MZN_UNTERMINATED_STRING = 319, /* "unterminated string" */ + MZN_END_OF_LINE_IN_STRING = 320, /* "end of line inside string literal" */ + MZN_INVALID_NULL = 321, /* "null character" */ + MZN_EQUIV = 322, /* "<->" */ + MZN_IMPL = 323, /* "->" */ + MZN_RIMPL = 324, /* "<-" */ + MZN_OR = 325, /* "\\/" */ + MZN_XOR = 326, /* "xor" */ + MZN_AND = 327, /* "/\\" */ + MZN_LE = 328, /* "<" */ + MZN_GR = 329, /* ">" */ + MZN_LQ = 330, /* "<=" */ + MZN_GQ = 331, /* ">=" */ + MZN_EQ = 332, /* "=" */ + MZN_NQ = 333, /* "!=" */ + MZN_WEAK_EQ = 334, /* "~=" */ + MZN_IN = 335, /* "in" */ + MZN_SUBSET = 336, /* "subset" */ + MZN_SUPERSET = 337, /* "superset" */ + MZN_UNION = 338, /* "union" */ + MZN_DIFF = 339, /* "diff" */ + MZN_SYMDIFF = 340, /* "symdiff" */ + MZN_DOTDOT = 341, /* ".." */ + MZN_PLUS = 342, /* "+" */ + MZN_MINUS = 343, /* "-" */ + MZN_WEAK_PLUS = 344, /* "~+" */ + MZN_WEAK_MINUS = 345, /* "~-" */ + MZN_MULT = 346, /* "*" */ + MZN_DIV = 347, /* "/" */ + MZN_IDIV = 348, /* "div" */ + MZN_MOD = 349, /* "mod" */ + MZN_INTERSECT = 350, /* "intersect" */ + MZN_WEAK_MULT = 351, /* "~*" */ + MZN_POW = 352, /* "^" */ + MZN_NOT = 353, /* "not" */ + MZN_PLUSPLUS = 354, /* "++" */ + MZN_COLONCOLON = 355, /* "::" */ + PREC_ANNO = 356, /* PREC_ANNO */ + MZN_EQUIV_QUOTED = 357, /* "'<->'" */ + MZN_IMPL_QUOTED = 358, /* "'->'" */ + MZN_RIMPL_QUOTED = 359, /* "'<-'" */ + MZN_OR_QUOTED = 360, /* "'\\/'" */ + MZN_XOR_QUOTED = 361, /* "'xor'" */ + MZN_AND_QUOTED = 362, /* "'/\\'" */ + MZN_LE_QUOTED = 363, /* "'<'" */ + MZN_GR_QUOTED = 364, /* "'>'" */ + MZN_LQ_QUOTED = 365, /* "'<='" */ + MZN_GQ_QUOTED = 366, /* "'>='" */ + MZN_EQ_QUOTED = 367, /* "'='" */ + MZN_NQ_QUOTED = 368, /* "'!='" */ + MZN_IN_QUOTED = 369, /* "'in'" */ + MZN_SUBSET_QUOTED = 370, /* "'subset'" */ + MZN_SUPERSET_QUOTED = 371, /* "'superset'" */ + MZN_UNION_QUOTED = 372, /* "'union'" */ + MZN_DIFF_QUOTED = 373, /* "'diff'" */ + MZN_SYMDIFF_QUOTED = 374, /* "'symdiff'" */ + MZN_DOTDOT_QUOTED = 375, /* "'..'" */ + MZN_PLUS_QUOTED = 376, /* "'+'" */ + MZN_MINUS_QUOTED = 377, /* "'-'" */ + MZN_MULT_QUOTED = 378, /* "'*'" */ + MZN_DIV_QUOTED = 379, /* "'/'" */ + MZN_IDIV_QUOTED = 380, /* "'div'" */ + MZN_MOD_QUOTED = 381, /* "'mod'" */ + MZN_INTERSECT_QUOTED = 382, /* "'intersect'" */ + MZN_POW_QUOTED = 383, /* "'^'" */ + MZN_NOT_QUOTED = 384, /* "'not'" */ + MZN_COLONCOLON_QUOTED = 385, /* "'::'" */ + MZN_PLUSPLUS_QUOTED = 386 /* "'++'" */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ + long long int iValue; char* sValue; bool bValue; double dValue; + MiniZinc::Item* item; + MiniZinc::VarDecl* vardeclexpr; + std::vector* vardeclexpr_v; + MiniZinc::TypeInst* tiexpr; + std::vector* tiexpr_v; + MiniZinc::Expression* expression; + std::vector* expression_v; + std::vector >* expression_vv; + std::vector > >* expression_vvv; + MiniZinc::Generator* generator; + std::vector* generator_v; + std::vector* string_v; + std::vector >* expression_p; + MiniZinc::Generators* generators; + + + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int mzn_yyparse (void *parm); + +#endif /* !YY_MZN_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_PARSER_TAB_HH_INCLUDED */ diff --git a/lib/cached/minizinc/support/mza_parser.tab.hh b/lib/cached/minizinc/support/mza_parser.tab.hh new file mode 100644 index 0000000..da5bd1f --- /dev/null +++ b/lib/cached/minizinc/support/mza_parser.tab.hh @@ -0,0 +1,157 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_MZA_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_SUPPORT_MZA_PARSER_TAB_HH_INCLUDED +# define YY_MZA_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_SUPPORT_MZA_PARSER_TAB_HH_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int mza_yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + MZA_INT = 258, /* MZA_INT */ + MZA_REG = 259, /* MZA_REG */ + MZA_MODE = 260, /* MZA_MODE */ + MZA_CTX = 261, /* MZA_CTX */ + MZA_ID = 262, /* MZA_ID */ + MZA_COLON = 263, /* ":" */ + MZA_DELAY = 264, /* "D" */ + MZA_GLOBAL = 265, /* "GLOBAL" */ + MZA_ADDI = 266, /* MZA_ADDI */ + MZA_SUBI = 267, /* MZA_SUBI */ + MZA_MULI = 268, /* MZA_MULI */ + MZA_DIVI = 269, /* MZA_DIVI */ + MZA_MODI = 270, /* MZA_MODI */ + MZA_INCI = 271, /* MZA_INCI */ + MZA_DECI = 272, /* MZA_DECI */ + MZA_IMMI = 273, /* MZA_IMMI */ + MZA_CLEAR = 274, /* MZA_CLEAR */ + MZA_LOAD_GLOBAL = 275, /* MZA_LOAD_GLOBAL */ + MZA_STORE_GLOBAL = 276, /* MZA_STORE_GLOBAL */ + MZA_MOV = 277, /* MZA_MOV */ + MZA_JMP = 278, /* MZA_JMP */ + MZA_JMPIF = 279, /* MZA_JMPIF */ + MZA_JMPIFNOT = 280, /* MZA_JMPIFNOT */ + MZA_EQI = 281, /* MZA_EQI */ + MZA_LTI = 282, /* MZA_LTI */ + MZA_LEI = 283, /* MZA_LEI */ + MZA_AND = 284, /* MZA_AND */ + MZA_OR = 285, /* MZA_OR */ + MZA_NOT = 286, /* MZA_NOT */ + MZA_XOR = 287, /* MZA_XOR */ + MZA_ISPAR = 288, /* MZA_ISPAR */ + MZA_ISEMPTY = 289, /* MZA_ISEMPTY */ + MZA_LENGTH = 290, /* MZA_LENGTH */ + MZA_GET_VEC = 291, /* MZA_GET_VEC */ + MZA_GET_ARRAY = 292, /* MZA_GET_ARRAY */ + MZA_LB = 293, /* MZA_LB */ + MZA_UB = 294, /* MZA_UB */ + MZA_DOM = 295, /* MZA_DOM */ + MZA_MAKE_SET = 296, /* MZA_MAKE_SET */ + MZA_DIFF = 297, /* MZA_DIFF */ + MZA_INTERSECTION = 298, /* MZA_INTERSECTION */ + MZA_UNION = 299, /* MZA_UNION */ + MZA_INTERSECT_DOMAIN = 300, /* MZA_INTERSECT_DOMAIN */ + MZA_OPEN_AGGREGATION = 301, /* MZA_OPEN_AGGREGATION */ + MZA_CLOSE_AGGREGATION = 302, /* MZA_CLOSE_AGGREGATION */ + MZA_SIMPLIFY_LIN = 303, /* MZA_SIMPLIFY_LIN */ + MZA_PUSH = 304, /* MZA_PUSH */ + MZA_POP = 305, /* MZA_POP */ + MZA_POST = 306, /* MZA_POST */ + MZA_RET = 307, /* MZA_RET */ + MZA_CALL = 308, /* MZA_CALL */ + MZA_BUILTIN = 309, /* MZA_BUILTIN */ + MZA_TCALL = 310, /* MZA_TCALL */ + MZA_ITER_ARRAY = 311, /* MZA_ITER_ARRAY */ + MZA_ITER_VEC = 312, /* MZA_ITER_VEC */ + MZA_ITER_RANGE = 313, /* MZA_ITER_RANGE */ + MZA_ITER_NEXT = 314, /* MZA_ITER_NEXT */ + MZA_ITER_BREAK = 315, /* MZA_ITER_BREAK */ + MZA_TRACE = 316, /* MZA_TRACE */ + MZA_ABORT = 317 /* MZA_ABORT */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ + + int iValue; + char* sValue; + MiniZinc::BytecodeStream::Instr bValue; + std::list* liValue; + std::list* sliValue; + + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int mza_yyparse (MZAContext& ctx); + +#endif /* !YY_MZA_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_SUPPORT_MZA_PARSER_TAB_HH_INCLUDED */ diff --git a/lib/cached/minizinc/support/regex_parser.tab.hh b/lib/cached/minizinc/support/regex_parser.tab.hh new file mode 100644 index 0000000..ac2fff3 --- /dev/null +++ b/lib/cached/minizinc/support/regex_parser.tab.hh @@ -0,0 +1,99 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_REGEX_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_SUPPORT_REGEX_PARSER_TAB_HH_INCLUDED +# define YY_REGEX_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_SUPPORT_REGEX_PARSER_TAB_HH_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int regex_yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + R_INTEGER = 258, /* R_INTEGER */ + R_GROUP_OPEN = 259, /* "(" */ + R_GROUP_CLOSE = 260, /* ")" */ + R_STAR = 261, /* "*" */ + R_PLUS = 262, /* "+" */ + R_ANY = 263, /* "." */ + R_UNION = 264, /* "|" */ + R_OPTIONAL = 265, /* "?" */ + R_QUANT_OPEN = 266, /* "{" */ + R_QUANT_CLOSE = 267, /* "}" */ + R_COMMA = 268, /* "," */ + R_CLASS_OPEN = 269, /* "[" */ + R_CLASS_CLOSE = 270, /* "]" */ + R_CLASS_RANGE = 271, /* "-" */ + R_CLASS_NEG = 272, /* "^" */ + R_IDENTIFIER = 273 /* R_IDENTIFIER */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ + + int iValue; + char* sValue; + std::unordered_set* setValue; + Gecode::REG* rValue; + + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE regex_yylval; + +int regex_yyparse (REContext& ctx); + +#endif /* !YY_REGEX_YY_USERS_DEKKER1_CODE_GITHUB_COM_MINIZINC_LIBMINIZINC_BYTE_BUILD_INCLUDE_MINIZINC_SUPPORT_REGEX_PARSER_TAB_HH_INCLUDED */ diff --git a/lib/cached/mza_lexer.yy.cpp b/lib/cached/mza_lexer.yy.cpp new file mode 100644 index 0000000..531da28 --- /dev/null +++ b/lib/cached/mza_lexer.yy.cpp @@ -0,0 +1,2495 @@ + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer mza_yy_create_buffer +#define yy_delete_buffer mza_yy_delete_buffer +#define yy_scan_buffer mza_yy_scan_buffer +#define yy_scan_string mza_yy_scan_string +#define yy_scan_bytes mza_yy_scan_bytes +#define yy_init_buffer mza_yy_init_buffer +#define yy_flush_buffer mza_yy_flush_buffer +#define yy_load_buffer_state mza_yy_load_buffer_state +#define yy_switch_to_buffer mza_yy_switch_to_buffer +#define yypush_buffer_state mza_yypush_buffer_state +#define yypop_buffer_state mza_yypop_buffer_state +#define yyensure_buffer_stack mza_yyensure_buffer_stack +#define yy_flex_debug mza_yy_flex_debug +#define yyin mza_yyin +#define yyleng mza_yyleng +#define yylex mza_yylex +#define yylineno mza_yylineno +#define yyout mza_yyout +#define yyrestart mza_yyrestart +#define yytext mza_yytext +#define yywrap mza_yywrap +#define yyalloc mza_yyalloc +#define yyrealloc mza_yyrealloc +#define yyfree mza_yyfree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define mza_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer mza_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define mza_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer mza_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define mza_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer mza_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define mza_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string mza_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define mza_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes mza_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define mza_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer mza_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define mza_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer mza_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define mza_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state mza_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define mza_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer mza_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define mza_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state mza_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define mza_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state mza_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define mza_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack mza_yyensure_buffer_stack +#endif + +#ifdef yylex +#define mza_yylex_ALREADY_DEFINED +#else +#define yylex mza_yylex +#endif + +#ifdef yyrestart +#define mza_yyrestart_ALREADY_DEFINED +#else +#define yyrestart mza_yyrestart +#endif + +#ifdef yylex_init +#define mza_yylex_init_ALREADY_DEFINED +#else +#define yylex_init mza_yylex_init +#endif + +#ifdef yylex_init_extra +#define mza_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra mza_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define mza_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy mza_yylex_destroy +#endif + +#ifdef yyget_debug +#define mza_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug mza_yyget_debug +#endif + +#ifdef yyset_debug +#define mza_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug mza_yyset_debug +#endif + +#ifdef yyget_extra +#define mza_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra mza_yyget_extra +#endif + +#ifdef yyset_extra +#define mza_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra mza_yyset_extra +#endif + +#ifdef yyget_in +#define mza_yyget_in_ALREADY_DEFINED +#else +#define yyget_in mza_yyget_in +#endif + +#ifdef yyset_in +#define mza_yyset_in_ALREADY_DEFINED +#else +#define yyset_in mza_yyset_in +#endif + +#ifdef yyget_out +#define mza_yyget_out_ALREADY_DEFINED +#else +#define yyget_out mza_yyget_out +#endif + +#ifdef yyset_out +#define mza_yyset_out_ALREADY_DEFINED +#else +#define yyset_out mza_yyset_out +#endif + +#ifdef yyget_leng +#define mza_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng mza_yyget_leng +#endif + +#ifdef yyget_text +#define mza_yyget_text_ALREADY_DEFINED +#else +#define yyget_text mza_yyget_text +#endif + +#ifdef yyget_lineno +#define mza_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno mza_yyget_lineno +#endif + +#ifdef yyset_lineno +#define mza_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno mza_yyset_lineno +#endif + +#ifdef yywrap +#define mza_yywrap_ALREADY_DEFINED +#else +#define yywrap mza_yywrap +#endif + +#ifdef yyget_lval +#define mza_yyget_lval_ALREADY_DEFINED +#else +#define yyget_lval mza_yyget_lval +#endif + +#ifdef yyset_lval +#define mza_yyset_lval_ALREADY_DEFINED +#else +#define yyset_lval mza_yyset_lval +#endif + +#ifdef yyget_lloc +#define mza_yyget_lloc_ALREADY_DEFINED +#else +#define yyget_lloc mza_yyget_lloc +#endif + +#ifdef yyset_lloc +#define mza_yyset_lloc_ALREADY_DEFINED +#else +#define yyset_lloc mza_yyset_lloc +#endif + +#ifdef yyalloc +#define mza_yyalloc_ALREADY_DEFINED +#else +#define yyalloc mza_yyalloc +#endif + +#ifdef yyrealloc +#define mza_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc mza_yyrealloc +#endif + +#ifdef yyfree +#define mza_yyfree_ALREADY_DEFINED +#else +#define yyfree mza_yyfree +#endif + +#ifdef yytext +#define mza_yytext_ALREADY_DEFINED +#else +#define yytext mza_yytext +#endif + +#ifdef yyleng +#define mza_yyleng_ALREADY_DEFINED +#else +#define yyleng mza_yyleng +#endif + +#ifdef yyin +#define mza_yyin_ALREADY_DEFINED +#else +#define yyin mza_yyin +#endif + +#ifdef yyout +#define mza_yyout_ALREADY_DEFINED +#else +#define yyout mza_yyout +#endif + +#ifdef yy_flex_debug +#define mza_yy_flex_debug_ALREADY_DEFINED +#else +#define yy_flex_debug mza_yy_flex_debug +#endif + +#ifdef yylineno +#define mza_yylineno_ALREADY_DEFINED +#else +#define yylineno mza_yylineno +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define mza_yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 72 +#define YY_END_OF_BUFFER 73 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[296] = + { 0, + 0, 0, 73, 71, 2, 1, 3, 71, 71, 4, + 6, 69, 69, 69, 7, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 7, 69, 3, 0, 70, 4, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 36, 69, 69, + 69, 69, 69, 69, 69, 69, 28, 69, 69, 69, + 5, 69, 69, 69, 69, 69, 69, 69, 37, 69, + 69, 69, 69, 69, 27, 69, 69, 69, 69, 69, + 69, 69, 38, 24, 65, 69, 69, 69, 67, 69, + + 69, 69, 69, 69, 21, 26, 69, 69, 25, 69, + 69, 20, 69, 29, 69, 69, 48, 69, 69, 50, + 69, 69, 69, 69, 69, 69, 69, 61, 30, 69, + 9, 69, 51, 69, 69, 15, 40, 12, 69, 69, + 69, 16, 69, 14, 69, 69, 69, 69, 69, 69, + 69, 69, 13, 11, 69, 69, 49, 47, 63, 69, + 69, 10, 69, 69, 69, 60, 69, 17, 69, 69, + 69, 69, 69, 69, 69, 69, 31, 69, 22, 69, + 69, 69, 69, 62, 69, 69, 69, 53, 59, 42, + 69, 69, 69, 69, 69, 8, 69, 69, 69, 69, + + 69, 69, 69, 69, 69, 33, 69, 69, 69, 69, + 69, 69, 52, 69, 66, 69, 34, 68, 69, 32, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 55, + 23, 69, 39, 69, 64, 69, 69, 69, 35, 69, + 69, 69, 57, 69, 69, 69, 69, 69, 69, 69, + 69, 54, 58, 56, 69, 69, 69, 69, 69, 69, + 69, 18, 69, 69, 69, 69, 41, 69, 69, 46, + 19, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 43, 44, 45, 0 + + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 5, 1, 6, 1, + 1, 1, 1, 1, 7, 1, 1, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 9, 1, 1, + 1, 1, 1, 1, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 32, + 1, 4, 1, 1, 35, 1, 32, 32, 32, 36, + + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 37, 32, 32, 32, 32, 32, 32, + 32, 32, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[38] = + { 0, + 1, 1, 2, 3, 1, 1, 1, 4, 1, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4 + } ; + +static const flex_int16_t yy_base[299] = + { 0, + 0, 0, 306, 307, 307, 307, 0, 299, 296, 295, + 307, 27, 272, 31, 25, 275, 270, 30, 0, 25, + 277, 31, 46, 274, 32, 34, 54, 45, 53, 56, + 283, 272, 0, 287, 0, 288, 307, 285, 0, 268, + 278, 277, 271, 267, 57, 275, 51, 264, 267, 261, + 254, 258, 47, 61, 63, 267, 255, 0, 66, 269, + 260, 257, 70, 255, 246, 260, 0, 256, 66, 244, + 263, 241, 245, 246, 243, 255, 255, 254, 0, 245, + 250, 234, 233, 241, 0, 237, 236, 246, 227, 236, + 238, 234, 0, 0, 216, 215, 238, 230, 212, 228, + + 231, 222, 233, 215, 223, 0, 224, 226, 0, 224, + 219, 0, 218, 0, 212, 220, 0, 204, 215, 0, + 202, 205, 202, 210, 206, 214, 201, 0, 0, 195, + 0, 194, 0, 195, 207, 0, 0, 0, 197, 75, + 209, 0, 195, 0, 190, 191, 188, 179, 198, 183, + 176, 175, 0, 0, 174, 181, 0, 0, 172, 185, + 191, 0, 183, 189, 179, 0, 183, 0, 165, 185, + 171, 183, 175, 181, 166, 164, 0, 76, 169, 174, + 174, 161, 178, 0, 164, 168, 150, 0, 0, 0, + 161, 173, 166, 154, 168, 0, 163, 164, 143, 149, + + 148, 160, 163, 158, 147, 0, 149, 155, 152, 153, + 151, 149, 0, 148, 0, 153, 0, 0, 150, 0, + 134, 146, 126, 135, 145, 127, 131, 125, 137, 136, + 117, 129, 133, 114, 118, 136, 135, 115, 127, 0, + 0, 131, 0, 114, 0, 105, 115, 111, 0, 74, + 103, 116, 0, 121, 124, 119, 111, 120, 116, 105, + 115, 0, 0, 0, 106, 110, 107, 114, 107, 99, + 97, 0, 110, 96, 97, 106, 0, 93, 82, 0, + 0, 79, 95, 86, 84, 82, 74, 73, 73, 72, + 70, 0, 0, 0, 307, 109, 113, 42 + + } ; + +static const flex_int16_t yy_def[299] = + { 0, + 295, 1, 295, 295, 295, 295, 296, 297, 295, 295, + 295, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 296, 297, 295, 295, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, + 298, 298, 298, 298, 0, 295, 295, 295 + + } ; + +static const flex_int16_t yy_nxt[345] = + { 0, + 4, 5, 6, 4, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 19, + 22, 23, 24, 25, 26, 19, 27, 28, 29, 30, + 31, 19, 32, 19, 4, 33, 34, 40, 46, 41, + 44, 58, 47, 51, 59, 39, 53, 54, 48, 42, + 52, 45, 55, 56, 60, 62, 66, 69, 67, 61, + 68, 71, 74, 70, 77, 91, 79, 72, 98, 63, + 88, 99, 100, 75, 76, 64, 102, 73, 80, 78, + 89, 92, 111, 106, 171, 200, 201, 103, 107, 101, + 117, 260, 294, 118, 293, 292, 291, 290, 202, 289, + + 112, 288, 203, 287, 286, 172, 204, 285, 261, 35, + 284, 35, 35, 36, 283, 282, 36, 281, 280, 279, + 278, 277, 276, 275, 274, 273, 272, 271, 270, 269, + 268, 267, 266, 265, 264, 263, 262, 259, 258, 257, + 256, 255, 254, 253, 252, 251, 250, 249, 248, 247, + 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, + 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, + 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, + 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, + 206, 205, 199, 198, 197, 196, 195, 194, 193, 192, + + 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, + 181, 180, 179, 178, 177, 176, 175, 174, 173, 170, + 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, + 159, 158, 157, 156, 155, 154, 153, 152, 151, 150, + 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, + 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, + 129, 128, 127, 126, 125, 124, 123, 122, 121, 120, + 71, 119, 116, 115, 114, 113, 110, 109, 108, 105, + 104, 97, 96, 95, 94, 93, 90, 87, 86, 85, + 84, 83, 38, 37, 71, 82, 81, 65, 57, 50, + + 49, 43, 38, 38, 37, 295, 3, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295 + } ; + +static const flex_int16_t yy_chk[345] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 12, 15, 12, + 14, 22, 15, 18, 22, 298, 20, 20, 15, 12, + 18, 14, 20, 20, 22, 23, 25, 26, 25, 22, + 25, 27, 28, 26, 29, 47, 30, 27, 53, 23, + 45, 53, 54, 28, 28, 23, 55, 27, 30, 29, + 45, 47, 63, 59, 140, 178, 178, 55, 59, 54, + 69, 250, 291, 69, 290, 289, 288, 287, 178, 286, + + 63, 285, 178, 284, 283, 140, 178, 282, 250, 296, + 279, 296, 296, 297, 278, 276, 297, 275, 274, 273, + 271, 270, 269, 268, 267, 266, 265, 261, 260, 259, + 258, 257, 256, 255, 254, 252, 251, 248, 247, 246, + 244, 242, 239, 238, 237, 236, 235, 234, 233, 232, + 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, + 221, 219, 216, 214, 212, 211, 210, 209, 208, 207, + 205, 204, 203, 202, 201, 200, 199, 198, 197, 195, + 194, 193, 192, 191, 187, 186, 185, 183, 182, 181, + 180, 179, 176, 175, 174, 173, 172, 171, 170, 169, + + 167, 165, 164, 163, 161, 160, 159, 156, 155, 152, + 151, 150, 149, 148, 147, 146, 145, 143, 141, 139, + 135, 134, 132, 130, 127, 126, 125, 124, 123, 122, + 121, 119, 118, 116, 115, 113, 111, 110, 108, 107, + 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, + 95, 92, 91, 90, 89, 88, 87, 86, 84, 83, + 82, 81, 80, 78, 77, 76, 75, 74, 73, 72, + 71, 70, 68, 66, 65, 64, 62, 61, 60, 57, + 56, 52, 51, 50, 49, 48, 46, 44, 43, 42, + 41, 40, 38, 36, 34, 32, 31, 24, 21, 17, + + 16, 13, 10, 9, 8, 3, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + * Main authors: + * Jip J. Dekker + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include + +using namespace MiniZinc; + +#define YY_USER_ACTION { yylloc->first_line = yylineno; } + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +YYSTYPE * yyget_lval ( void ); + +void yyset_lval ( YYSTYPE * yylval_param ); + + YYLTYPE *yyget_lloc ( void ); + + void yyset_lloc ( YYLTYPE * yylloc_param ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr ); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param ); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param ) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + YYSTYPE * yylval; + + YYLTYPE * yylloc; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 296 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 307 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +{ yylineno++; } + YY_BREAK +case 2: +YY_RULE_SETUP +{ /* ignore white space */ } + YY_BREAK +case 3: +YY_RULE_SETUP +{ /* ignore comments */ } + YY_BREAK +case 4: +YY_RULE_SETUP +{ yylval->iValue = std::atoi(yytext); return MZA_INT; } + YY_BREAK +case 5: +YY_RULE_SETUP +{ yylval->iValue = std::atoi(yytext+1); return MZA_REG; } + YY_BREAK +case 6: +YY_RULE_SETUP +{ return MZA_COLON; } + YY_BREAK +case 7: +YY_RULE_SETUP +{ return MZA_DELAY; } + YY_BREAK +case 8: +YY_RULE_SETUP +{ return MZA_GLOBAL; } + YY_BREAK +case 9: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ADDI; return MZA_ADDI; } + YY_BREAK +case 10: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::SUBI; return MZA_SUBI; } + YY_BREAK +case 11: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::MULI; return MZA_MULI; } + YY_BREAK +case 12: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::DIVI; return MZA_DIVI; } + YY_BREAK +case 13: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::MODI; return MZA_MODI; } + YY_BREAK +case 14: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::INCI; return MZA_INCI; } + YY_BREAK +case 15: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::DECI; return MZA_DECI; } + YY_BREAK +case 16: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::IMMI; return MZA_IMMI; } + YY_BREAK +case 17: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::CLEAR; return MZA_CLEAR; } + YY_BREAK +case 18: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::LOAD_GLOBAL; return MZA_LOAD_GLOBAL; } + YY_BREAK +case 19: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::STORE_GLOBAL; return MZA_STORE_GLOBAL; } + YY_BREAK +case 20: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::MOV; return MZA_MOV; } + YY_BREAK +case 21: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::JMP; return MZA_JMP; } + YY_BREAK +case 22: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::JMPIF; return MZA_JMPIF; } + YY_BREAK +case 23: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::JMPIFNOT; return MZA_JMPIFNOT; } + YY_BREAK +case 24: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::EQI; return MZA_EQI; } + YY_BREAK +case 25: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::LTI; return MZA_LTI; } + YY_BREAK +case 26: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::LEI; return MZA_LEI; } + YY_BREAK +case 27: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::AND; return MZA_AND; } + YY_BREAK +case 28: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::OR; return MZA_OR; } + YY_BREAK +case 29: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::NOT; return MZA_NOT; } + YY_BREAK +case 30: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::XOR; return MZA_XOR; } + YY_BREAK +case 31: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ISPAR; return MZA_ISPAR; } + YY_BREAK +case 32: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ISEMPTY; return MZA_ISEMPTY; } + YY_BREAK +case 33: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::LENGTH; return MZA_LENGTH; } + YY_BREAK +case 34: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::GET_VEC; return MZA_GET_VEC; } + YY_BREAK +case 35: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::GET_ARRAY; return MZA_GET_ARRAY; } + YY_BREAK +case 36: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::LB; return MZA_LB; } + YY_BREAK +case 37: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::UB; return MZA_UB; } + YY_BREAK +case 38: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::DOM; return MZA_DOM; } + YY_BREAK +case 39: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::MAKE_SET; return MZA_MAKE_SET; } + YY_BREAK +case 40: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::DIFF; return MZA_DIFF; } + YY_BREAK +case 41: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::INTERSECTION; return MZA_INTERSECTION; } + YY_BREAK +case 42: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::UNION; return MZA_UNION; } + YY_BREAK +case 43: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::INTERSECT_DOMAIN; return MZA_INTERSECT_DOMAIN; } + YY_BREAK +case 44: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::OPEN_AGGREGATION; return MZA_OPEN_AGGREGATION; } + YY_BREAK +case 45: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::CLOSE_AGGREGATION; return MZA_CLOSE_AGGREGATION; } + YY_BREAK +case 46: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::SIMPLIFY_LIN; return MZA_SIMPLIFY_LIN; } + YY_BREAK +case 47: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::PUSH; return MZA_PUSH; } + YY_BREAK +case 48: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::POP; return MZA_POP; } + YY_BREAK +case 49: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::POST; return MZA_POST; } + YY_BREAK +case 50: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::RET; return MZA_RET; } + YY_BREAK +case 51: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::CALL; return MZA_CALL; } + YY_BREAK +case 52: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::BUILTIN; return MZA_BUILTIN; } + YY_BREAK +case 53: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::TCALL; return MZA_TCALL; } + YY_BREAK +case 54: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ITER_ARRAY; return MZA_ITER_ARRAY; } + YY_BREAK +case 55: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ITER_VEC; return MZA_ITER_VEC; } + YY_BREAK +case 56: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ITER_RANGE; return MZA_ITER_RANGE; } + YY_BREAK +case 57: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ITER_NEXT; return MZA_ITER_NEXT; } + YY_BREAK +case 58: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ITER_BREAK; return MZA_ITER_BREAK; } + YY_BREAK +case 59: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::TRACE; return MZA_TRACE; } + YY_BREAK +case 60: +YY_RULE_SETUP +{ yylval->bValue = BytecodeStream::ABORT; return MZA_ABORT; } + YY_BREAK +case 61: +YY_RULE_SETUP +{ yylval->iValue = AggregationCtx::VCTX_VEC; return MZA_CTX; } + YY_BREAK +case 62: +YY_RULE_SETUP +{ yylval->iValue = AggregationCtx::VCTX_OTHER; return MZA_CTX; } + YY_BREAK +case 63: +YY_RULE_SETUP +{ yylval->iValue = BytecodeProc::ROOT; return MZA_MODE; } + YY_BREAK +case 64: +YY_RULE_SETUP +{ yylval->iValue = BytecodeProc::ROOT_NEG; return MZA_MODE; } + YY_BREAK +case 65: +YY_RULE_SETUP +{ yylval->iValue = BytecodeProc::FUN; return MZA_MODE; } + YY_BREAK +case 66: +YY_RULE_SETUP +{ yylval->iValue = BytecodeProc::FUN_NEG; return MZA_MODE; } + YY_BREAK +case 67: +YY_RULE_SETUP +{ yylval->iValue = BytecodeProc::IMP; return MZA_MODE; } + YY_BREAK +case 68: +YY_RULE_SETUP +{ yylval->iValue = BytecodeProc::IMP_NEG; return MZA_MODE; } + YY_BREAK +case 69: +YY_RULE_SETUP +{ yylval->sValue = strdup(yytext); return MZA_ID; } + YY_BREAK +case 70: +YY_RULE_SETUP +{ yylval->sValue = strdup(yytext); return MZA_ID; } + YY_BREAK +case 71: +YY_RULE_SETUP +{ + /* Catch all */ + throw std::runtime_error("Illegal token in regular expression: '" + std::string(yytext) + "'"); +} + YY_BREAK +case 72: +YY_RULE_SETUP +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 4); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 296 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 4; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 296 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 295); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp ) +{ + char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + int number_to_move = (yy_n_chars) + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + diff --git a/lib/cached/mza_parser.tab.cpp b/lib/cached/mza_parser.tab.cpp new file mode 100644 index 0000000..35422ce --- /dev/null +++ b/lib/cached/mza_parser.tab.cpp @@ -0,0 +1,2210 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30706 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.6" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse mza_yyparse +#define yylex mza_yylex +#define yyerror mza_yyerror +#define yydebug mza_yydebug +#define yynerrs mza_yynerrs + +/* First part of user prologue. */ + +#include +#include + +#include + +//Anonymous struct for when yyparse is exported +typedef struct MZAContext MZAContext; +#include + +using namespace MiniZinc; + +typedef struct yy_buffer_state *YY_BUFFER_STATE; +YY_BUFFER_STATE mza_yy_scan_string ( const char* yy_str ); + +extern int yylex(YYSTYPE*, YYLTYPE*); +extern FILE* yyin; + +struct ProcPatch { + int code; + BytecodeProc::Mode mode; + std::vector>> patch; + ProcPatch(int code0, BytecodeProc::Mode mode0, std::vector>> patch0) + : code(code0), mode(mode0), patch(std::move(patch0)) {} +}; + +typedef struct MZAContext { + std::vector& procs; + std::unordered_map& proc_map; + int& max_glob; + std::vector to_patch; + BytecodeStream proc_body; + std::unordered_map labels; + std::vector> patch_labels; + std::vector>> patch_procs; +} MZAContext; + +void yyerror(YYLTYPE* location, MZAContext& ctx, const char* s); + + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_MZA_INT = 3, /* MZA_INT */ + YYSYMBOL_MZA_REG = 4, /* MZA_REG */ + YYSYMBOL_MZA_MODE = 5, /* MZA_MODE */ + YYSYMBOL_MZA_CTX = 6, /* MZA_CTX */ + YYSYMBOL_MZA_ID = 7, /* MZA_ID */ + YYSYMBOL_MZA_COLON = 8, /* ":" */ + YYSYMBOL_MZA_DELAY = 9, /* "D" */ + YYSYMBOL_MZA_GLOBAL = 10, /* "GLOBAL" */ + YYSYMBOL_MZA_ADDI = 11, /* MZA_ADDI */ + YYSYMBOL_MZA_SUBI = 12, /* MZA_SUBI */ + YYSYMBOL_MZA_MULI = 13, /* MZA_MULI */ + YYSYMBOL_MZA_DIVI = 14, /* MZA_DIVI */ + YYSYMBOL_MZA_MODI = 15, /* MZA_MODI */ + YYSYMBOL_MZA_INCI = 16, /* MZA_INCI */ + YYSYMBOL_MZA_DECI = 17, /* MZA_DECI */ + YYSYMBOL_MZA_IMMI = 18, /* MZA_IMMI */ + YYSYMBOL_MZA_CLEAR = 19, /* MZA_CLEAR */ + YYSYMBOL_MZA_LOAD_GLOBAL = 20, /* MZA_LOAD_GLOBAL */ + YYSYMBOL_MZA_STORE_GLOBAL = 21, /* MZA_STORE_GLOBAL */ + YYSYMBOL_MZA_MOV = 22, /* MZA_MOV */ + YYSYMBOL_MZA_JMP = 23, /* MZA_JMP */ + YYSYMBOL_MZA_JMPIF = 24, /* MZA_JMPIF */ + YYSYMBOL_MZA_JMPIFNOT = 25, /* MZA_JMPIFNOT */ + YYSYMBOL_MZA_EQI = 26, /* MZA_EQI */ + YYSYMBOL_MZA_LTI = 27, /* MZA_LTI */ + YYSYMBOL_MZA_LEI = 28, /* MZA_LEI */ + YYSYMBOL_MZA_AND = 29, /* MZA_AND */ + YYSYMBOL_MZA_OR = 30, /* MZA_OR */ + YYSYMBOL_MZA_NOT = 31, /* MZA_NOT */ + YYSYMBOL_MZA_XOR = 32, /* MZA_XOR */ + YYSYMBOL_MZA_ISPAR = 33, /* MZA_ISPAR */ + YYSYMBOL_MZA_ISEMPTY = 34, /* MZA_ISEMPTY */ + YYSYMBOL_MZA_LENGTH = 35, /* MZA_LENGTH */ + YYSYMBOL_MZA_GET_VEC = 36, /* MZA_GET_VEC */ + YYSYMBOL_MZA_GET_ARRAY = 37, /* MZA_GET_ARRAY */ + YYSYMBOL_MZA_LB = 38, /* MZA_LB */ + YYSYMBOL_MZA_UB = 39, /* MZA_UB */ + YYSYMBOL_MZA_DOM = 40, /* MZA_DOM */ + YYSYMBOL_MZA_MAKE_SET = 41, /* MZA_MAKE_SET */ + YYSYMBOL_MZA_DIFF = 42, /* MZA_DIFF */ + YYSYMBOL_MZA_INTERSECTION = 43, /* MZA_INTERSECTION */ + YYSYMBOL_MZA_UNION = 44, /* MZA_UNION */ + YYSYMBOL_MZA_INTERSECT_DOMAIN = 45, /* MZA_INTERSECT_DOMAIN */ + YYSYMBOL_MZA_OPEN_AGGREGATION = 46, /* MZA_OPEN_AGGREGATION */ + YYSYMBOL_MZA_CLOSE_AGGREGATION = 47, /* MZA_CLOSE_AGGREGATION */ + YYSYMBOL_MZA_SIMPLIFY_LIN = 48, /* MZA_SIMPLIFY_LIN */ + YYSYMBOL_MZA_PUSH = 49, /* MZA_PUSH */ + YYSYMBOL_MZA_POP = 50, /* MZA_POP */ + YYSYMBOL_MZA_POST = 51, /* MZA_POST */ + YYSYMBOL_MZA_RET = 52, /* MZA_RET */ + YYSYMBOL_MZA_CALL = 53, /* MZA_CALL */ + YYSYMBOL_MZA_BUILTIN = 54, /* MZA_BUILTIN */ + YYSYMBOL_MZA_TCALL = 55, /* MZA_TCALL */ + YYSYMBOL_MZA_ITER_ARRAY = 56, /* MZA_ITER_ARRAY */ + YYSYMBOL_MZA_ITER_VEC = 57, /* MZA_ITER_VEC */ + YYSYMBOL_MZA_ITER_RANGE = 58, /* MZA_ITER_RANGE */ + YYSYMBOL_MZA_ITER_NEXT = 59, /* MZA_ITER_NEXT */ + YYSYMBOL_MZA_ITER_BREAK = 60, /* MZA_ITER_BREAK */ + YYSYMBOL_MZA_TRACE = 61, /* MZA_TRACE */ + YYSYMBOL_MZA_ABORT = 62, /* MZA_ABORT */ + YYSYMBOL_YYACCEPT = 63, /* $accept */ + YYSYMBOL_procedures = 64, /* procedures */ + YYSYMBOL_procedure = 65, /* procedure */ + YYSYMBOL_mode = 66, /* mode */ + YYSYMBOL_delay = 67, /* delay */ + YYSYMBOL_instructions = 68, /* instructions */ + YYSYMBOL_labeled_instr = 69, /* labeled_instr */ + YYSYMBOL_labels = 70, /* labels */ + YYSYMBOL_label = 71, /* label */ + YYSYMBOL_instruction = 72, /* instruction */ + YYSYMBOL_instrIR = 73, /* instrIR */ + YYSYMBOL_instrI = 74, /* instrI */ + YYSYMBOL_instrR = 75, /* instrR */ + YYSYMBOL_instrRR = 76, /* instrRR */ + YYSYMBOL_instrRS = 77, /* instrRS */ + YYSYMBOL_instrRRR = 78, /* instrRRR */ + YYSYMBOL_instrRRS = 79, /* instrRRS */ + YYSYMBOL_instrRRIRRR = 80, /* instrRRIRRR */ + YYSYMBOL_instrS = 81, /* instrS */ + YYSYMBOL_instr = 82, /* instr */ + YYSYMBOL_registers = 83 /* registers */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 105 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 63 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 21 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 80 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 121 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 317 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 154, 154, 156, 159, 205, 206, 209, 210, 212, + 214, 217, 226, 227, 230, 233, 238, 244, 251, 257, + 264, 272, 281, 289, 300, 307, 320, 331, 340, 347, + 353, 360, 368, 376, 388, 391, 394, 395, 396, 397, + 398, 399, 400, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 415, 416, 417, 418, 421, 422, 423, + 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, + 434, 435, 436, 439, 442, 445, 448, 449, 450, 454, + 455 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "MZA_INT", "MZA_REG", + "MZA_MODE", "MZA_CTX", "MZA_ID", "\":\"", "\"D\"", "\"GLOBAL\"", + "MZA_ADDI", "MZA_SUBI", "MZA_MULI", "MZA_DIVI", "MZA_MODI", "MZA_INCI", + "MZA_DECI", "MZA_IMMI", "MZA_CLEAR", "MZA_LOAD_GLOBAL", + "MZA_STORE_GLOBAL", "MZA_MOV", "MZA_JMP", "MZA_JMPIF", "MZA_JMPIFNOT", + "MZA_EQI", "MZA_LTI", "MZA_LEI", "MZA_AND", "MZA_OR", "MZA_NOT", + "MZA_XOR", "MZA_ISPAR", "MZA_ISEMPTY", "MZA_LENGTH", "MZA_GET_VEC", + "MZA_GET_ARRAY", "MZA_LB", "MZA_UB", "MZA_DOM", "MZA_MAKE_SET", + "MZA_DIFF", "MZA_INTERSECTION", "MZA_UNION", "MZA_INTERSECT_DOMAIN", + "MZA_OPEN_AGGREGATION", "MZA_CLOSE_AGGREGATION", "MZA_SIMPLIFY_LIN", + "MZA_PUSH", "MZA_POP", "MZA_POST", "MZA_RET", "MZA_CALL", "MZA_BUILTIN", + "MZA_TCALL", "MZA_ITER_ARRAY", "MZA_ITER_VEC", "MZA_ITER_RANGE", + "MZA_ITER_NEXT", "MZA_ITER_BREAK", "MZA_TRACE", "MZA_ABORT", "$accept", + "procedures", "procedure", "mode", "delay", "instructions", + "labeled_instr", "labels", "label", "instruction", "instrIR", "instrI", + "instrR", "instrRR", "instrRS", "instrRRR", "instrRRS", "instrRRIRRR", + "instrS", "instr", "registers", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317 +}; +#endif + +#define YYPACT_NINF (-32) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-5) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + -32, 58, -32, -6, -32, -5, -3, -32, 57, 52, + -32, -32, 59, -32, -7, 54, -32, -32, -32, -32, + -32, -32, -32, -32, -32, 60, 61, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, 65, -32, -32, -32, -32, -32, -32, -32, + -32, 50, -32, -32, -32, -32, -32, -32, 64, 63, + 66, -32, -32, -32, -32, -32, -32, -32, -32, -32, + 69, 70, 71, 72, 73, 74, 78, 79, 67, -32, + -32, 80, 82, -32, -32, -32, -32, 81, -32, 83, + 85, -32, -32, 87, 86, 88, 90, 91, -32, -32, + -32, 92, 84, 92, 94, -32, -32, -32, 95, 93, + 98, -32, -32, -32, -32, -32, 99, 92, 100, 101, + -32 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 2, 0, 1, 0, 3, 0, 5, 6, 0, 7, + 8, 9, 12, 10, 0, 0, 57, 58, 59, 60, + 61, 36, 37, 34, 52, 0, 0, 43, 75, 53, + 54, 62, 63, 64, 65, 66, 44, 67, 45, 46, + 47, 68, 0, 50, 49, 51, 48, 69, 70, 71, + 72, 0, 77, 74, 38, 39, 40, 76, 0, 0, + 0, 55, 56, 73, 41, 35, 42, 78, 13, 11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, + 14, 0, 0, 79, 28, 29, 30, 0, 79, 0, + 0, 16, 18, 0, 0, 0, 0, 0, 24, 31, + 32, 33, 0, 26, 0, 17, 19, 20, 0, 0, + 0, 80, 79, 27, 22, 21, 0, 25, 0, 0, + 23 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, + -31 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 1, 4, 8, 11, 12, 13, 14, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 101 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 15, 5, 7, 6, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 84, 103, 2, -4, + 9, 10, 80, 81, 0, 82, 3, -4, 83, 87, + 88, 89, 90, 91, 98, 92, 93, 94, 95, 85, + 86, 117, 96, 97, 99, 100, 0, 112, 102, 105, + 104, 106, 108, 107, 109, 110, 111, 113, 0, 114, + 115, 116, 0, 118, 119, 120 +}; + +static const yytype_int8 yycheck[] = +{ + 7, 7, 5, 8, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 6, 88, 0, 0, + 3, 9, 8, 3, -1, 4, 8, 8, 3, 5, + 7, 5, 3, 3, 7, 4, 4, 4, 4, 29, + 30, 112, 4, 4, 4, 3, -1, 3, 7, 4, + 7, 4, 4, 7, 4, 4, 4, 3, -1, 4, + 7, 3, -1, 4, 4, 4 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 64, 0, 8, 65, 7, 8, 5, 66, 3, + 9, 67, 68, 69, 70, 7, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 8, 3, 4, 3, 6, 29, 30, 5, 7, 5, + 3, 3, 4, 4, 4, 4, 4, 4, 7, 4, + 3, 83, 7, 83, 7, 4, 4, 7, 4, 4, + 4, 4, 3, 3, 4, 7, 3, 83, 4, 4, + 4 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 63, 64, 64, 65, 66, 66, 67, 67, 68, + 68, 69, 70, 70, 71, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 72, 72, 72, 72, 73, 74, 75, 75, 75, 75, + 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, + 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, + 78, 78, 78, 79, 80, 81, 82, 82, 82, 83, + 83 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 7, 0, 1, 0, 1, 0, + 2, 2, 0, 2, 2, 1, 2, 3, 2, 3, + 3, 4, 4, 7, 2, 5, 3, 4, 2, 2, + 2, 3, 3, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, + 2 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, ctx, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +# ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +# endif /* !defined YY_LOCATION_PRINT */ + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, Location, ctx); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, MZAContext& ctx) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (yylocationp); + YY_USE (ctx); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, MZAContext& ctx) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, ctx); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, + int yyrule, MZAContext& ctx) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], + &(yylsp[(yyi + 1) - (yynrhs)]), ctx); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, ctx); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; + YYLTYPE *yylloc; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, MZAContext& ctx) +{ + YY_USE (yyvaluep); + YY_USE (yylocationp); + YY_USE (ctx); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (MZAContext& ctx) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + /* The location stack: array, bottom, top. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp = yyls; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, &yylloc); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + yyerror_range[1] = yylloc; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 4: /* procedure: ":" MZA_ID ":" mode MZA_INT delay instructions */ + { + // Patch jumps with recorded labels + for (auto& cl : ctx.patch_labels) { + if (ctx.labels.find(cl.second) == ctx.labels.end()) { + throw Error("Error: label " + cl.second + " not found\n"); + } + ctx.proc_body.patchAddress(cl.first, ctx.labels[cl.second]); + } + ctx.labels.clear(); + ctx.patch_labels.clear(); + + ctx.proc_body.setNArgs((yyvsp[-2].iValue)); + + // Add ABORT instruction for error management + if (ctx.proc_body.size() > 0) { + ctx.proc_body.addInstr(BytecodeStream::ABORT); + } + // Store procedure in the correct place + auto mode = static_cast((yyvsp[-3].iValue)); + auto it = ctx.proc_map.find((yyvsp[-5].sValue)); + if (it != ctx.proc_map.end()) { + BytecodeProc& bcp = ctx.procs[it->second]; + if (bcp.mode[mode].size() > 0) { + throw Error("Error: procedure " + std::string((yyvsp[-5].sValue)) + " already defined before with the same mode\n"); + } + if (bcp.nargs != (yyvsp[-2].iValue) ) { + throw Error("Error: procedure " + std::string((yyvsp[-5].sValue)) + " already defined before with different number of arguments\n"); + } + bcp.mode[(yyvsp[-3].iValue)] = ctx.proc_body; + ctx.to_patch.emplace_back(it->second, mode, ctx.patch_procs); + } else { + BytecodeProc bcp; + bcp.name = (yyvsp[-5].sValue); + bcp.mode[mode] = ctx.proc_body; + bcp.nargs = (yyvsp[-2].iValue); + bcp.delay = (yyvsp[-1].iValue); + ctx.proc_map.emplace((yyvsp[-5].sValue), ctx.procs.size()); + ctx.to_patch.emplace_back(ctx.procs.size(), mode, ctx.patch_procs); + ctx.procs.push_back(bcp); + } + ctx.proc_body = BytecodeStream(); + ctx.patch_procs.clear(); + } + break; + + case 5: /* mode: %empty */ + { (yyval.iValue) = BytecodeProc::FUN; } + break; + + case 7: /* delay: %empty */ + { (yyval.iValue) = 0; } + break; + + case 8: /* delay: "D" */ + { (yyval.iValue) = 1; } + break; + + case 11: /* labeled_instr: labels instruction */ + { + for (auto l : *(yyvsp[-1].sliValue)) { + ctx.labels.emplace(l, (yyvsp[0].iValue)); + } + delete (yyvsp[-1].sliValue); + } + break; + + case 12: /* labels: %empty */ + { (yyval.sliValue) = new std::list(); } + break; + + case 13: /* labels: labels label */ + { (yyvsp[-1].sliValue)->push_back((yyvsp[0].sValue)); (yyval.sliValue) = (yyvsp[-1].sliValue); } + break; + + case 15: /* instruction: instr */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[0].bValue)); + } + break; + + case 16: /* instruction: instrI MZA_INT */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-1].bValue)); + ctx.proc_body.addIntVal((yyvsp[0].iValue)); + } + break; + + case 17: /* instruction: instrIR MZA_INT MZA_REG */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.proc_body.addIntVal((yyvsp[-1].iValue)); + ctx.proc_body.addReg((yyvsp[0].iValue)); + } + break; + + case 18: /* instruction: instrR MZA_REG */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-1].bValue)); + ctx.proc_body.addReg((yyvsp[0].iValue)); + } + break; + + case 19: /* instruction: instrRR MZA_REG MZA_REG */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue)); + ctx.proc_body.addReg((yyvsp[0].iValue)); + } + break; + + case 20: /* instruction: instrRS MZA_REG MZA_ID */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue)); + ctx.patch_labels.emplace_back(ctx.proc_body.size(), (yyvsp[0].sValue)); + ctx.proc_body.addSmallInt(0); + } + break; + + case 21: /* instruction: instrRRS MZA_REG MZA_REG MZA_ID */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-3].bValue)); + ctx.proc_body.addReg((yyvsp[-2].iValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue)); + ctx.patch_labels.emplace_back(ctx.proc_body.size(), (yyvsp[0].sValue)); + ctx.proc_body.addSmallInt(0); + } + break; + + case 22: /* instruction: instrRRR MZA_REG MZA_REG MZA_REG */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-3].bValue)); + ctx.proc_body.addReg((yyvsp[-2].iValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue)); + ctx.proc_body.addReg((yyvsp[0].iValue)); + } + break; + + case 23: /* instruction: instrRRIRRR MZA_REG MZA_REG MZA_INT MZA_REG MZA_REG MZA_REG */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-6].bValue)); + ctx.proc_body.addReg((yyvsp[-5].iValue)); + ctx.proc_body.addReg((yyvsp[-4].iValue)); + ctx.proc_body.addIntVal((yyvsp[-3].iValue)); + ctx.proc_body.addReg((yyvsp[-2].iValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue)); + ctx.proc_body.addReg((yyvsp[0].iValue)); + } + break; + + case 24: /* instruction: instrS MZA_ID */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-1].bValue)); + ctx.patch_labels.emplace_back(ctx.proc_body.size(), (yyvsp[0].sValue)); + ctx.proc_body.addSmallInt(0); + } + break; + + case 25: /* instruction: MZA_CALL MZA_MODE MZA_ID MZA_INT registers */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-4].bValue)); + ctx.proc_body.addCharVal((yyvsp[-3].iValue)); + ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair((yyvsp[-2].sValue), (yyvsp[0].liValue)->size())); + ctx.proc_body.addSmallInt(0); + ctx.proc_body.addCharVal((yyvsp[-1].iValue)); + for (auto arg : *(yyvsp[0].liValue)) { + ctx.proc_body.addReg(arg); + } + delete (yyvsp[0].liValue); + } + break; + + case 26: /* instruction: MZA_BUILTIN MZA_ID registers */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair((yyvsp[-1].sValue), (yyvsp[0].liValue)->size())); + ctx.proc_body.addSmallInt(0); + for (auto arg : *(yyvsp[0].liValue)) { + ctx.proc_body.addReg(arg); + } + delete (yyvsp[0].liValue); + } + break; + + case 27: /* instruction: MZA_TCALL MZA_MODE MZA_ID MZA_INT */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-3].bValue)); + ctx.proc_body.addCharVal((yyvsp[-2].iValue)); + ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair((yyvsp[-1].sValue), -1)); + ctx.proc_body.addSmallInt(0); + ctx.proc_body.addCharVal((yyvsp[0].iValue)); + } + break; + + case 28: /* instruction: MZA_OPEN_AGGREGATION MZA_CTX */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-1].bValue)); + ctx.proc_body.addCharVal((yyvsp[0].iValue)); + } + break; + + case 29: /* instruction: MZA_OPEN_AGGREGATION MZA_AND */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-1].bValue)); + ctx.proc_body.addCharVal(AggregationCtx::VCTX_AND); + } + break; + + case 30: /* instruction: MZA_OPEN_AGGREGATION MZA_OR */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-1].bValue)); + ctx.proc_body.addCharVal(AggregationCtx::VCTX_OR); + } + break; + + case 31: /* instruction: MZA_LOAD_GLOBAL MZA_INT MZA_REG */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue), true); + ctx.max_glob = std::max(ctx.max_glob, (yyvsp[-1].iValue)); + ctx.proc_body.addReg((yyvsp[0].iValue)); + } + break; + + case 32: /* instruction: MZA_STORE_GLOBAL MZA_REG MZA_INT */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.proc_body.addReg((yyvsp[-1].iValue)); + ctx.proc_body.addReg((yyvsp[0].iValue), true); + ctx.max_glob = std::max(ctx.max_glob, (yyvsp[0].iValue)); + } + break; + + case 33: /* instruction: MZA_GET_ARRAY MZA_INT registers */ + { + (yyval.iValue) = ctx.proc_body.size(); + ctx.proc_body.addInstr((yyvsp[-2].bValue)); + ctx.proc_body.addIntVal((yyvsp[-1].iValue)); + for (auto arg : *(yyvsp[0].liValue)) { + ctx.proc_body.addReg(arg); + } + delete (yyvsp[0].liValue); + } + break; + + case 79: /* registers: %empty */ + { (yyval.liValue) = new std::list(); } + break; + + case 80: /* registers: registers MZA_REG */ + { (yyvsp[-1].liValue)->push_back((yyvsp[0].iValue)); (yyval.liValue) = (yyvsp[-1].liValue); } + break; + + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken, &yylloc}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (&yylloc, ctx, yymsgp); + if (yysyntax_error_status == YYENOMEM) + goto yyexhaustedlab; + } + } + + yyerror_range[1] = yylloc; + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, ctx); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp, ctx); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + ++yylsp; + YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if 1 +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, ctx, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, ctx); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, ctx); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + + + +#include +#include + +void yyerror(YYLTYPE* location, MZAContext& ctx, const char* s) { + std::ostringstream oss; + oss << "Cannot parse MiniZinc assembly in line " << location->first_line << ": " << std::string(s); + throw Error(oss.str()); +} + +std::pair> parse_mza(const std::string& assembly_str) { + std::vector procs; + std::unordered_map proc_map; + int max_glob; + // Initialise first slots with + for (PrimitiveMap::Primitive* p : primitiveMap()) { + BytecodeProc bcp; + bcp.name = p->name(); + bcp.nargs = p->n_args(); + bcp.delay = false; + procs.push_back(bcp); + proc_map.emplace(bcp.name, p->ident()); + } + mza_yy_scan_string(assembly_str.c_str()); + MZAContext ctx = MZAContext{procs, proc_map, max_glob}; + int err = yyparse(ctx); + if (err != 0) { + throw std::runtime_error("Cannot parse MiniZinc assembly: " + std::to_string(err)); + } + for (auto& p : ctx.to_patch) { + int code = p.code; + BytecodeProc::Mode mode = p.mode; + for (auto& patch : p.patch) { + int& nargs = patch.second.second; + std::string& name = patch.second.first; + + if (nargs >= 0 && nargs != procs[proc_map[name]].nargs) { + throw Error("Error: number of arguments in call to " + patch.second.first + " at position " + + std::to_string(patch.second.second) + " has an invalid number of arguments.\n"); + } + procs[code].mode[mode].patchAddress(patch.first, proc_map[patch.second.first]); + } + } + return {max_glob, procs}; +} diff --git a/lib/cached/parser.tab.cpp b/lib/cached/parser.tab.cpp new file mode 100644 index 0000000..3b7dc76 --- /dev/null +++ b/lib/cached/parser.tab.cpp @@ -0,0 +1,5038 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30706 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.6" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse mzn_yyparse +#define yylex mzn_yylex +#define yyerror mzn_yyerror +#define yydebug mzn_yydebug +#define yynerrs mzn_yynerrs + +/* First part of user prologue. */ + +#define SCANNER static_cast(parm)->yyscanner +#include +#include +#include +#include + +namespace MiniZinc{ class ParserLocation; } +#define YYLTYPE MiniZinc::ParserLocation +#define YYLTYPE_IS_DECLARED 1 +#define YYLTYPE_IS_TRIVIAL 0 + +#define YYMAXDEPTH 10000 +#define YYINITDEPTH 10000 + +#include +#include + +using namespace std; +using namespace MiniZinc; + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + (Current).filename(Rhs[1].filename()); \ + (Current).first_line(Rhs[1].first_line()); \ + (Current).first_column(Rhs[1].first_column()); \ + (Current).last_line(Rhs[N].last_line()); \ + (Current).last_column(Rhs[N].last_column()); + +int mzn_yyparse(void*); +int mzn_yylex(YYSTYPE*, YYLTYPE*, void* scanner); +int mzn_yylex_init (void** scanner); +int mzn_yylex_destroy (void* scanner); +int mzn_yyget_lineno (void* scanner); +void mzn_yyset_extra (void* user_defined ,void* yyscanner ); + +extern int yydebug; + +void yyerror(YYLTYPE* location, void* parm, const string& str) { + ParserState* pp = static_cast(parm); + Model* m = pp->model; + while (m->parent() != NULL) { + m = m->parent(); + pp->err << "(included from file '" << m->filename() << "')" << endl; + } + pp->err << location->toString() << ":" << endl; + pp->printCurrentLine(location->first_column(),location->last_column()); + pp->err << "Error: " << str << std::endl; + pp->hadError = true; + pp->syntaxErrors.push_back(SyntaxError(Location(*location), str)); +} + +bool notInDatafile(YYLTYPE* location, void* parm, const string& item) { + ParserState* pp = static_cast(parm); + if (pp->isDatafile) { + yyerror(location,parm,item+" item not allowed in data file"); + return false; + } + return true; +} + +Expression* createDocComment(const ParserLocation& loc, const std::string& s) { + std::vector args(1); + args[0] = new StringLit(loc, s); + Call* c = new Call(Location(loc), constants().ann.doc_comment, args); + c->type(Type::ann()); + return c; +} + +Expression* createArrayAccess(const ParserLocation& loc, Expression* e, std::vector >& idx) { + Expression* ret = e; + for (unsigned int i=0; i (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_MZN_INTEGER_LITERAL = 3, /* "integer literal" */ + YYSYMBOL_MZN_BOOL_LITERAL = 4, /* "bool literal" */ + YYSYMBOL_MZN_FLOAT_LITERAL = 5, /* "float literal" */ + YYSYMBOL_MZN_IDENTIFIER = 6, /* "identifier" */ + YYSYMBOL_MZN_QUOTED_IDENTIFIER = 7, /* "quoted identifier" */ + YYSYMBOL_MZN_STRING_LITERAL = 8, /* "string literal" */ + YYSYMBOL_MZN_STRING_QUOTE_START = 9, /* "interpolated string start" */ + YYSYMBOL_MZN_STRING_QUOTE_MID = 10, /* "interpolated string middle" */ + YYSYMBOL_MZN_STRING_QUOTE_END = 11, /* "interpolated string end" */ + YYSYMBOL_MZN_TI_IDENTIFIER = 12, /* "type-inst identifier" */ + YYSYMBOL_MZN_TI_ENUM_IDENTIFIER = 13, /* "type-inst enum identifier" */ + YYSYMBOL_MZN_DOC_COMMENT = 14, /* "documentation comment" */ + YYSYMBOL_MZN_DOC_FILE_COMMENT = 15, /* "file-level documentation comment" */ + YYSYMBOL_MZN_VAR = 16, /* "var" */ + YYSYMBOL_MZN_PAR = 17, /* "par" */ + YYSYMBOL_MZN_ABSENT = 18, /* "<>" */ + YYSYMBOL_MZN_ANN = 19, /* "ann" */ + YYSYMBOL_MZN_ANNOTATION = 20, /* "annotation" */ + YYSYMBOL_MZN_ANY = 21, /* "any" */ + YYSYMBOL_MZN_ARRAY = 22, /* "array" */ + YYSYMBOL_MZN_BOOL = 23, /* "bool" */ + YYSYMBOL_MZN_CASE = 24, /* "case" */ + YYSYMBOL_MZN_CONSTRAINT = 25, /* "constraint" */ + YYSYMBOL_MZN_DEFAULT = 26, /* "default" */ + YYSYMBOL_MZN_ELSE = 27, /* "else" */ + YYSYMBOL_MZN_ELSEIF = 28, /* "elseif" */ + YYSYMBOL_MZN_ENDIF = 29, /* "endif" */ + YYSYMBOL_MZN_ENUM = 30, /* "enum" */ + YYSYMBOL_MZN_FLOAT = 31, /* "float" */ + YYSYMBOL_MZN_FUNCTION = 32, /* "function" */ + YYSYMBOL_MZN_IF = 33, /* "if" */ + YYSYMBOL_MZN_INCLUDE = 34, /* "include" */ + YYSYMBOL_MZN_INFINITY = 35, /* "infinity" */ + YYSYMBOL_MZN_INT = 36, /* "int" */ + YYSYMBOL_MZN_LET = 37, /* "let" */ + YYSYMBOL_MZN_LIST = 38, /* "list" */ + YYSYMBOL_MZN_MAXIMIZE = 39, /* "maximize" */ + YYSYMBOL_MZN_MINIMIZE = 40, /* "minimize" */ + YYSYMBOL_MZN_OF = 41, /* "of" */ + YYSYMBOL_MZN_OPT = 42, /* "opt" */ + YYSYMBOL_MZN_SATISFY = 43, /* "satisfy" */ + YYSYMBOL_MZN_OUTPUT = 44, /* "output" */ + YYSYMBOL_MZN_PREDICATE = 45, /* "predicate" */ + YYSYMBOL_MZN_RECORD = 46, /* "record" */ + YYSYMBOL_MZN_SET = 47, /* "set" */ + YYSYMBOL_MZN_SOLVE = 48, /* "solve" */ + YYSYMBOL_MZN_STRING = 49, /* "string" */ + YYSYMBOL_MZN_TEST = 50, /* "test" */ + YYSYMBOL_MZN_THEN = 51, /* "then" */ + YYSYMBOL_MZN_TUPLE = 52, /* "tuple" */ + YYSYMBOL_MZN_TYPE = 53, /* "type" */ + YYSYMBOL_MZN_UNDERSCORE = 54, /* "_" */ + YYSYMBOL_MZN_VARIANT_RECORD = 55, /* "variant_record" */ + YYSYMBOL_MZN_WHERE = 56, /* "where" */ + YYSYMBOL_MZN_LEFT_BRACKET = 57, /* "[" */ + YYSYMBOL_MZN_LEFT_2D_BRACKET = 58, /* "[|" */ + YYSYMBOL_MZN_RIGHT_BRACKET = 59, /* "]" */ + YYSYMBOL_MZN_RIGHT_2D_BRACKET = 60, /* "|]" */ + YYSYMBOL_FLATZINC_IDENTIFIER = 61, /* FLATZINC_IDENTIFIER */ + YYSYMBOL_MZN_INVALID_INTEGER_LITERAL = 62, /* "invalid integer literal" */ + YYSYMBOL_MZN_INVALID_FLOAT_LITERAL = 63, /* "invalid float literal" */ + YYSYMBOL_MZN_UNTERMINATED_STRING = 64, /* "unterminated string" */ + YYSYMBOL_MZN_END_OF_LINE_IN_STRING = 65, /* "end of line inside string literal" */ + YYSYMBOL_MZN_INVALID_NULL = 66, /* "null character" */ + YYSYMBOL_MZN_EQUIV = 67, /* "<->" */ + YYSYMBOL_MZN_IMPL = 68, /* "->" */ + YYSYMBOL_MZN_RIMPL = 69, /* "<-" */ + YYSYMBOL_MZN_OR = 70, /* "\\/" */ + YYSYMBOL_MZN_XOR = 71, /* "xor" */ + YYSYMBOL_MZN_AND = 72, /* "/\\" */ + YYSYMBOL_MZN_LE = 73, /* "<" */ + YYSYMBOL_MZN_GR = 74, /* ">" */ + YYSYMBOL_MZN_LQ = 75, /* "<=" */ + YYSYMBOL_MZN_GQ = 76, /* ">=" */ + YYSYMBOL_MZN_EQ = 77, /* "=" */ + YYSYMBOL_MZN_NQ = 78, /* "!=" */ + YYSYMBOL_MZN_WEAK_EQ = 79, /* "~=" */ + YYSYMBOL_MZN_IN = 80, /* "in" */ + YYSYMBOL_MZN_SUBSET = 81, /* "subset" */ + YYSYMBOL_MZN_SUPERSET = 82, /* "superset" */ + YYSYMBOL_MZN_UNION = 83, /* "union" */ + YYSYMBOL_MZN_DIFF = 84, /* "diff" */ + YYSYMBOL_MZN_SYMDIFF = 85, /* "symdiff" */ + YYSYMBOL_MZN_DOTDOT = 86, /* ".." */ + YYSYMBOL_MZN_PLUS = 87, /* "+" */ + YYSYMBOL_MZN_MINUS = 88, /* "-" */ + YYSYMBOL_MZN_WEAK_PLUS = 89, /* "~+" */ + YYSYMBOL_MZN_WEAK_MINUS = 90, /* "~-" */ + YYSYMBOL_MZN_MULT = 91, /* "*" */ + YYSYMBOL_MZN_DIV = 92, /* "/" */ + YYSYMBOL_MZN_IDIV = 93, /* "div" */ + YYSYMBOL_MZN_MOD = 94, /* "mod" */ + YYSYMBOL_MZN_INTERSECT = 95, /* "intersect" */ + YYSYMBOL_MZN_WEAK_MULT = 96, /* "~*" */ + YYSYMBOL_MZN_POW = 97, /* "^" */ + YYSYMBOL_MZN_NOT = 98, /* "not" */ + YYSYMBOL_MZN_PLUSPLUS = 99, /* "++" */ + YYSYMBOL_MZN_COLONCOLON = 100, /* "::" */ + YYSYMBOL_PREC_ANNO = 101, /* PREC_ANNO */ + YYSYMBOL_MZN_EQUIV_QUOTED = 102, /* "'<->'" */ + YYSYMBOL_MZN_IMPL_QUOTED = 103, /* "'->'" */ + YYSYMBOL_MZN_RIMPL_QUOTED = 104, /* "'<-'" */ + YYSYMBOL_MZN_OR_QUOTED = 105, /* "'\\/'" */ + YYSYMBOL_MZN_XOR_QUOTED = 106, /* "'xor'" */ + YYSYMBOL_MZN_AND_QUOTED = 107, /* "'/\\'" */ + YYSYMBOL_MZN_LE_QUOTED = 108, /* "'<'" */ + YYSYMBOL_MZN_GR_QUOTED = 109, /* "'>'" */ + YYSYMBOL_MZN_LQ_QUOTED = 110, /* "'<='" */ + YYSYMBOL_MZN_GQ_QUOTED = 111, /* "'>='" */ + YYSYMBOL_MZN_EQ_QUOTED = 112, /* "'='" */ + YYSYMBOL_MZN_NQ_QUOTED = 113, /* "'!='" */ + YYSYMBOL_MZN_IN_QUOTED = 114, /* "'in'" */ + YYSYMBOL_MZN_SUBSET_QUOTED = 115, /* "'subset'" */ + YYSYMBOL_MZN_SUPERSET_QUOTED = 116, /* "'superset'" */ + YYSYMBOL_MZN_UNION_QUOTED = 117, /* "'union'" */ + YYSYMBOL_MZN_DIFF_QUOTED = 118, /* "'diff'" */ + YYSYMBOL_MZN_SYMDIFF_QUOTED = 119, /* "'symdiff'" */ + YYSYMBOL_MZN_DOTDOT_QUOTED = 120, /* "'..'" */ + YYSYMBOL_MZN_PLUS_QUOTED = 121, /* "'+'" */ + YYSYMBOL_MZN_MINUS_QUOTED = 122, /* "'-'" */ + YYSYMBOL_MZN_MULT_QUOTED = 123, /* "'*'" */ + YYSYMBOL_MZN_DIV_QUOTED = 124, /* "'/'" */ + YYSYMBOL_MZN_IDIV_QUOTED = 125, /* "'div'" */ + YYSYMBOL_MZN_MOD_QUOTED = 126, /* "'mod'" */ + YYSYMBOL_MZN_INTERSECT_QUOTED = 127, /* "'intersect'" */ + YYSYMBOL_MZN_POW_QUOTED = 128, /* "'^'" */ + YYSYMBOL_MZN_NOT_QUOTED = 129, /* "'not'" */ + YYSYMBOL_MZN_COLONCOLON_QUOTED = 130, /* "'::'" */ + YYSYMBOL_MZN_PLUSPLUS_QUOTED = 131, /* "'++'" */ + YYSYMBOL_132_ = 132, /* ';' */ + YYSYMBOL_133_ = 133, /* '{' */ + YYSYMBOL_134_ = 134, /* '}' */ + YYSYMBOL_135_ = 135, /* '(' */ + YYSYMBOL_136_ = 136, /* ')' */ + YYSYMBOL_137_ = 137, /* ',' */ + YYSYMBOL_138_ = 138, /* ':' */ + YYSYMBOL_139_ = 139, /* '|' */ + YYSYMBOL_YYACCEPT = 140, /* $accept */ + YYSYMBOL_model = 141, /* model */ + YYSYMBOL_item_list = 142, /* item_list */ + YYSYMBOL_item_list_head = 143, /* item_list_head */ + YYSYMBOL_doc_file_comments = 144, /* doc_file_comments */ + YYSYMBOL_semi_or_none = 145, /* semi_or_none */ + YYSYMBOL_item = 146, /* item */ + YYSYMBOL_item_tail = 147, /* item_tail */ + YYSYMBOL_error_item_start = 148, /* error_item_start */ + YYSYMBOL_include_item = 149, /* include_item */ + YYSYMBOL_vardecl_item = 150, /* vardecl_item */ + YYSYMBOL_string_lit_list = 151, /* string_lit_list */ + YYSYMBOL_enum_id_list = 152, /* enum_id_list */ + YYSYMBOL_assign_item = 153, /* assign_item */ + YYSYMBOL_constraint_item = 154, /* constraint_item */ + YYSYMBOL_solve_item = 155, /* solve_item */ + YYSYMBOL_output_item = 156, /* output_item */ + YYSYMBOL_predicate_item = 157, /* predicate_item */ + YYSYMBOL_function_item = 158, /* function_item */ + YYSYMBOL_annotation_item = 159, /* annotation_item */ + YYSYMBOL_operation_item_tail = 160, /* operation_item_tail */ + YYSYMBOL_params = 161, /* params */ + YYSYMBOL_params_list = 162, /* params_list */ + YYSYMBOL_params_list_head = 163, /* params_list_head */ + YYSYMBOL_comma_or_none = 164, /* comma_or_none */ + YYSYMBOL_ti_expr_and_id_or_anon = 165, /* ti_expr_and_id_or_anon */ + YYSYMBOL_ti_expr_and_id = 166, /* ti_expr_and_id */ + YYSYMBOL_ti_expr_list = 167, /* ti_expr_list */ + YYSYMBOL_ti_expr_list_head = 168, /* ti_expr_list_head */ + YYSYMBOL_ti_expr = 169, /* ti_expr */ + YYSYMBOL_base_ti_expr = 170, /* base_ti_expr */ + YYSYMBOL_opt_opt = 171, /* opt_opt */ + YYSYMBOL_base_ti_expr_tail = 172, /* base_ti_expr_tail */ + YYSYMBOL_array_access_expr_list = 173, /* array_access_expr_list */ + YYSYMBOL_array_access_expr_list_head = 174, /* array_access_expr_list_head */ + YYSYMBOL_array_access_expr = 175, /* array_access_expr */ + YYSYMBOL_expr_list = 176, /* expr_list */ + YYSYMBOL_expr_list_head = 177, /* expr_list_head */ + YYSYMBOL_set_expr = 178, /* set_expr */ + YYSYMBOL_expr = 179, /* expr */ + YYSYMBOL_expr_atom_head = 180, /* expr_atom_head */ + YYSYMBOL_expr_atom_head_nonstring = 181, /* expr_atom_head_nonstring */ + YYSYMBOL_string_expr = 182, /* string_expr */ + YYSYMBOL_string_quote_rest = 183, /* string_quote_rest */ + YYSYMBOL_array_access_tail = 184, /* array_access_tail */ + YYSYMBOL_set_literal = 185, /* set_literal */ + YYSYMBOL_set_comp = 186, /* set_comp */ + YYSYMBOL_comp_tail = 187, /* comp_tail */ + YYSYMBOL_generator_list = 188, /* generator_list */ + YYSYMBOL_generator_list_head = 189, /* generator_list_head */ + YYSYMBOL_generator = 190, /* generator */ + YYSYMBOL_generator_eq = 191, /* generator_eq */ + YYSYMBOL_id_list = 192, /* id_list */ + YYSYMBOL_id_list_head = 193, /* id_list_head */ + YYSYMBOL_simple_array_literal = 194, /* simple_array_literal */ + YYSYMBOL_simple_array_literal_2d = 195, /* simple_array_literal_2d */ + YYSYMBOL_simple_array_literal_3d_list = 196, /* simple_array_literal_3d_list */ + YYSYMBOL_simple_array_literal_2d_list = 197, /* simple_array_literal_2d_list */ + YYSYMBOL_simple_array_comp = 198, /* simple_array_comp */ + YYSYMBOL_if_then_else_expr = 199, /* if_then_else_expr */ + YYSYMBOL_elseif_list = 200, /* elseif_list */ + YYSYMBOL_quoted_op = 201, /* quoted_op */ + YYSYMBOL_quoted_op_call = 202, /* quoted_op_call */ + YYSYMBOL_call_expr = 203, /* call_expr */ + YYSYMBOL_comp_or_expr = 204, /* comp_or_expr */ + YYSYMBOL_comp_or_expr_head = 205, /* comp_or_expr_head */ + YYSYMBOL_let_expr = 206, /* let_expr */ + YYSYMBOL_let_vardecl_item_list = 207, /* let_vardecl_item_list */ + YYSYMBOL_comma_or_semi = 208, /* comma_or_semi */ + YYSYMBOL_let_vardecl_item = 209, /* let_vardecl_item */ + YYSYMBOL_annotations = 210, /* annotations */ + YYSYMBOL_annotation_expr = 211, /* annotation_expr */ + YYSYMBOL_ne_annotations = 212, /* ne_annotations */ + YYSYMBOL_id_or_quoted_op = 213 /* id_or_quoted_op */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \ + && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 157 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 5058 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 140 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 74 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 321 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 540 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 386 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 135, 136, 2, 2, 137, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 138, 132, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 133, 139, 134, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 273, 273, 275, 277, 280, 289, 298, 307, 316, + 318, 321, 329, 338, 338, 340, 356, 360, 362, 364, + 365, 367, 369, 371, 373, 375, 378, 378, 378, 379, + 379, 379, 379, 379, 380, 383, 407, 413, 420, 428, + 438, 450, 465, 466, 470, 478, 479, 483, 487, 493, + 495, 502, 507, 512, 519, 523, 531, 541, 548, 557, + 569, 577, 578, 583, 584, 586, 591, 592, 596, 600, + 605, 605, 608, 610, 614, 619, 623, 625, 629, 630, + 636, 645, 648, 656, 664, 673, 682, 691, 704, 705, + 709, 711, 713, 715, 717, 719, 721, 726, 732, 735, + 737, 741, 743, 745, 754, 765, 768, 770, 776, 777, + 779, 781, 783, 785, 794, 803, 805, 807, 809, 811, + 813, 815, 817, 819, 821, 826, 831, 836, 841, 847, + 849, 862, 863, 865, 867, 869, 871, 873, 875, 877, + 879, 881, 883, 885, 887, 889, 891, 893, 895, 897, + 899, 901, 910, 919, 921, 923, 925, 927, 929, 931, + 933, 935, 937, 942, 947, 952, 957, 963, 965, 972, + 984, 986, 990, 992, 994, 996, 999, 1001, 1004, 1006, + 1008, 1010, 1012, 1014, 1015, 1018, 1019, 1022, 1023, 1026, + 1027, 1030, 1031, 1034, 1035, 1038, 1039, 1040, 1045, 1047, + 1053, 1058, 1066, 1073, 1082, 1084, 1089, 1095, 1098, 1101, + 1103, 1105, 1111, 1113, 1115, 1123, 1125, 1128, 1131, 1134, + 1136, 1140, 1142, 1146, 1148, 1159, 1170, 1210, 1213, 1218, + 1225, 1230, 1234, 1240, 1247, 1263, 1264, 1268, 1270, 1272, + 1274, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, + 1294, 1296, 1298, 1300, 1302, 1304, 1306, 1308, 1310, 1312, + 1314, 1316, 1318, 1320, 1322, 1326, 1334, 1366, 1368, 1369, + 1389, 1444, 1447, 1453, 1459, 1461, 1465, 1472, 1481, 1483, + 1491, 1493, 1502, 1502, 1505, 1511, 1522, 1523, 1526, 1528, + 1532, 1536, 1540, 1542, 1544, 1546, 1548, 1550, 1552, 1554, + 1556, 1558, 1560, 1562, 1564, 1566, 1568, 1570, 1572, 1574, + 1576, 1578, 1580, 1582, 1584, 1586, 1588, 1590, 1592, 1594, + 1596, 1598 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "\"integer literal\"", + "\"bool literal\"", "\"float literal\"", "\"identifier\"", + "\"quoted identifier\"", "\"string literal\"", + "\"interpolated string start\"", "\"interpolated string middle\"", + "\"interpolated string end\"", "\"type-inst identifier\"", + "\"type-inst enum identifier\"", "\"documentation comment\"", + "\"file-level documentation comment\"", "\"var\"", "\"par\"", "\"<>\"", + "\"ann\"", "\"annotation\"", "\"any\"", "\"array\"", "\"bool\"", + "\"case\"", "\"constraint\"", "\"default\"", "\"else\"", "\"elseif\"", + "\"endif\"", "\"enum\"", "\"float\"", "\"function\"", "\"if\"", + "\"include\"", "\"infinity\"", "\"int\"", "\"let\"", "\"list\"", + "\"maximize\"", "\"minimize\"", "\"of\"", "\"opt\"", "\"satisfy\"", + "\"output\"", "\"predicate\"", "\"record\"", "\"set\"", "\"solve\"", + "\"string\"", "\"test\"", "\"then\"", "\"tuple\"", "\"type\"", "\"_\"", + "\"variant_record\"", "\"where\"", "\"[\"", "\"[|\"", "\"]\"", "\"|]\"", + "FLATZINC_IDENTIFIER", "\"invalid integer literal\"", + "\"invalid float literal\"", "\"unterminated string\"", + "\"end of line inside string literal\"", "\"null character\"", "\"<->\"", + "\"->\"", "\"<-\"", "\"\\\\/\"", "\"xor\"", "\"/\\\\\"", "\"<\"", + "\">\"", "\"<=\"", "\">=\"", "\"=\"", "\"!=\"", "\"~=\"", "\"in\"", + "\"subset\"", "\"superset\"", "\"union\"", "\"diff\"", "\"symdiff\"", + "\"..\"", "\"+\"", "\"-\"", "\"~+\"", "\"~-\"", "\"*\"", "\"/\"", + "\"div\"", "\"mod\"", "\"intersect\"", "\"~*\"", "\"^\"", "\"not\"", + "\"++\"", "\"::\"", "PREC_ANNO", "\"'<->'\"", "\"'->'\"", "\"'<-'\"", + "\"'\\\\/'\"", "\"'xor'\"", "\"'/\\\\'\"", "\"'<'\"", "\"'>'\"", + "\"'<='\"", "\"'>='\"", "\"'='\"", "\"'!='\"", "\"'in'\"", + "\"'subset'\"", "\"'superset'\"", "\"'union'\"", "\"'diff'\"", + "\"'symdiff'\"", "\"'..'\"", "\"'+'\"", "\"'-'\"", "\"'*'\"", "\"'/'\"", + "\"'div'\"", "\"'mod'\"", "\"'intersect'\"", "\"'^'\"", "\"'not'\"", + "\"'::'\"", "\"'++'\"", "';'", "'{'", "'}'", "'('", "')'", "','", "':'", + "'|'", "$accept", "model", "item_list", "item_list_head", + "doc_file_comments", "semi_or_none", "item", "item_tail", + "error_item_start", "include_item", "vardecl_item", "string_lit_list", + "enum_id_list", "assign_item", "constraint_item", "solve_item", + "output_item", "predicate_item", "function_item", "annotation_item", + "operation_item_tail", "params", "params_list", "params_list_head", + "comma_or_none", "ti_expr_and_id_or_anon", "ti_expr_and_id", + "ti_expr_list", "ti_expr_list_head", "ti_expr", "base_ti_expr", + "opt_opt", "base_ti_expr_tail", "array_access_expr_list", + "array_access_expr_list_head", "array_access_expr", "expr_list", + "expr_list_head", "set_expr", "expr", "expr_atom_head", + "expr_atom_head_nonstring", "string_expr", "string_quote_rest", + "array_access_tail", "set_literal", "set_comp", "comp_tail", + "generator_list", "generator_list_head", "generator", "generator_eq", + "id_list", "id_list_head", "simple_array_literal", + "simple_array_literal_2d", "simple_array_literal_3d_list", + "simple_array_literal_2d_list", "simple_array_comp", "if_then_else_expr", + "elseif_list", "quoted_op", "quoted_op_call", "call_expr", + "comp_or_expr", "comp_or_expr_head", "let_expr", "let_vardecl_item_list", + "comma_or_semi", "let_vardecl_item", "annotations", "annotation_expr", + "ne_annotations", "id_or_quoted_op", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 59, 123, 125, 40, 41, 44, 58, 124 +}; +#endif + +#define YYPACT_NINF (-402) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-89) + +#define yytable_value_is_error(Yyn) \ + ((Yyn) == YYTABLE_NINF) + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 823, -103, -402, -402, -402, -8, -402, 3485, -402, -402, + 1623, -402, -9, -9, -402, -402, 33, -1, -402, 2820, + 51, -402, 2155, 3485, 55, -402, -402, -74, 25, 2687, + 3485, 59, -32, -402, 73, 23, 2953, 532, 3618, 3618, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -53, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, 3086, + 3485, 83, -402, -47, 1357, 219, -402, -402, -402, -402, + -402, -402, -402, -402, -402, -402, -32, -48, -402, 48, + -402, 3706, -402, -402, -402, 23, 23, 23, 23, 23, + 23, -44, -402, 23, -402, 1490, 3219, 3485, 1090, 40, + -29, 3485, 3485, 3485, -37, 11, 4739, -402, -402, -402, + -402, 2421, 2554, -34, 2155, 44, 4739, 28, -36, 4458, + -402, 1889, 2288, -402, 4739, -34, 3653, 2, 4, -34, + 40, -402, 54, 14, 3741, -402, 686, -402, -2, -30, + 31, 31, 3485, -402, 21, 3776, 4026, -402, 1224, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, + -402, -402, 35, 152, 118, 3618, 3618, 3618, 3618, 3618, + 3618, 3618, 3618, 3618, 3618, 3618, 3618, 3618, 3618, 3618, + 3618, 3618, 3618, 3653, 40, 40, 40, 40, 40, 40, + 3485, 40, -402, 3485, 101, 26, -402, 4776, 4739, -402, + 4500, 30, 37, 3219, 41, 41, 41, 3485, 3485, -402, + 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, + 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, + 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, 3485, + 3485, 3485, 3485, 3485, 3653, 120, -402, 121, -402, 956, + 88, 108, 43, -402, 3485, 10, 3903, 3485, -402, -32, + 45, -97, -402, -402, -32, -402, -402, -402, 3485, 3485, + -402, 3653, -32, -402, 3485, -402, 162, -402, 38, -402, + 46, -402, 3352, 3901, -402, 162, 23, 1357, -402, 3485, + 47, 2687, 87, 3800, 29, 29, 29, 277, 53, 53, + 53, 53, 39, 39, 39, 39, 29, 39, 31, 8, + -402, 3867, 4739, -402, 3219, -402, 3485, 3485, 58, 3485, + -402, 130, 3992, -402, 4739, 90, 4830, 4867, 4867, 4921, + 4921, 809, 4958, 4958, 4958, 4958, 4958, 4958, 4958, 4050, + 4050, 4050, 507, 507, 507, 659, 79, 79, 79, 79, + 57, 57, 57, 57, 507, 57, 41, 24, -402, 2687, + 2687, 56, 60, 63, -402, -402, 45, 3485, 154, 2022, + -402, 4739, 62, 229, 232, -402, -402, -402, -402, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -402, + -402, -402, -402, -402, -402, -34, 4401, 163, 235, -402, + 167, -402, 1756, 166, 4739, 4739, -402, 166, 175, 195, + -402, 119, -402, 199, 178, 124, 3485, 3485, -402, -402, + 3485, 128, 40, -402, 4739, 2022, -402, -402, 3485, -402, + 4739, 3485, 4552, -402, 3485, -402, -402, -402, -402, 2022, + -402, 4739, 2288, -402, 3485, -402, -27, -402, -90, -32, + -402, 27, 3485, -402, 3485, 186, -402, -402, 3485, -402, + -402, 3485, -402, 162, -402, 3485, 3485, 262, -402, 131, + 4117, -402, 136, 4151, 4242, 3485, 4276, -402, -402, 4367, + -402, 265, -402, 268, 166, 3485, 3485, 4739, 4739, 3485, + 4739, 4739, -402, 220, 4739, 4594, -402, 3485, -402, -32, + -402, -402, 4739, -402, -402, -402, -402, -402, 4685, 4648, + 4739, 3485, 3485, 166, -402, 3485, 4739, 4739, -402, 4739 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int16 yydefact[] = +{ + 0, 0, 179, 178, 181, 174, 198, 0, 96, 97, + 88, 11, 88, 88, 182, 94, 0, 0, 91, 0, + 0, 92, 88, 0, 0, 180, 90, 0, 0, 89, + 0, 0, 286, 93, 0, 176, 0, 0, 0, 0, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 0, 255, + 256, 257, 259, 260, 261, 262, 258, 264, 263, 0, + 0, 0, 2, 13, 88, 5, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 286, 0, 78, 0, + 81, 95, 108, 170, 171, 183, 185, 187, 189, 191, + 193, 0, 268, 196, 195, 88, 0, 0, 0, 175, + 174, 0, 0, 0, 0, 0, 106, 131, 199, 15, + 89, 0, 0, 63, 88, 0, 49, 38, 0, 0, + 35, 88, 88, 82, 54, 63, 0, 0, 287, 63, + 177, 221, 0, 70, 106, 223, 0, 230, 0, 0, + 129, 130, 0, 204, 0, 106, 0, 1, 14, 4, + 12, 6, 34, 29, 27, 32, 26, 28, 31, 30, + 33, 9, 36, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 184, 186, 188, 190, 192, 194, + 0, 197, 10, 102, 0, 70, 99, 101, 48, 267, + 272, 0, 70, 0, 168, 169, 167, 0, 0, 200, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 84, 0, 83, 0, + 59, 0, 70, 76, 0, 0, 0, 0, 279, 286, + 0, 0, 278, 80, 286, 288, 289, 290, 0, 0, + 51, 0, 286, 222, 71, 105, 0, 227, 0, 226, + 0, 224, 0, 0, 205, 0, 172, 88, 7, 0, + 74, 0, 128, 127, 110, 111, 112, 113, 117, 118, + 124, 125, 119, 120, 121, 122, 115, 126, 123, 116, + 109, 0, 103, 202, 71, 98, 104, 0, 269, 71, + 271, 0, 0, 201, 107, 166, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 165, 145, + 146, 147, 148, 149, 150, 151, 155, 156, 162, 163, + 157, 158, 159, 160, 153, 164, 161, 154, 132, 0, + 0, 0, 0, 70, 68, 72, 73, 0, 0, 71, + 75, 50, 0, 42, 45, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 316, 317, + 318, 319, 315, 320, 321, 63, 235, 284, 0, 283, + 0, 282, 88, 61, 53, 52, 291, 61, 219, 0, + 207, 70, 209, 210, 0, 70, 228, 0, 225, 231, + 0, 0, 173, 8, 37, 66, 85, 266, 0, 100, + 273, 0, 274, 203, 0, 87, 86, 65, 64, 71, + 67, 60, 88, 77, 0, 43, 0, 46, 0, 286, + 233, 0, 0, 74, 0, 0, 281, 280, 0, 55, + 56, 0, 232, 71, 208, 0, 0, 71, 218, 0, + 0, 206, 0, 0, 0, 0, 0, 69, 79, 0, + 40, 0, 39, 0, 61, 0, 0, 285, 276, 0, + 62, 217, 212, 213, 211, 215, 220, 229, 114, 286, + 265, 270, 275, 152, 41, 44, 47, 57, 0, 0, + 277, 0, 0, 61, 234, 0, 214, 216, 58, 236 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -402, -402, -402, -402, 117, -402, -62, 267, -402, -402, + -402, -402, -402, -402, -123, -402, -402, -402, -402, -402, + -401, -121, -167, -402, -185, -178, -125, -402, -402, -17, + -130, 49, -22, 69, -402, -41, -35, 18, 342, -19, + 317, -117, -112, 67, -25, -402, -402, -7, -402, -402, + -197, -196, -402, -402, -402, -402, -402, -137, -402, -402, + -402, -402, -402, -402, -402, -402, -402, -402, -402, -133, + -83, -170, -402, -402 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + 0, 71, 72, 73, 74, 159, 75, 76, 171, 77, + 78, 466, 468, 79, 80, 81, 82, 83, 84, 85, + 479, 260, 372, 373, 285, 374, 86, 261, 262, 87, + 88, 89, 90, 204, 205, 206, 147, 143, 91, 116, + 117, 93, 94, 118, 109, 95, 96, 429, 430, 431, + 432, 433, 434, 435, 97, 98, 148, 149, 99, 100, + 471, 101, 102, 103, 211, 212, 104, 271, 422, 272, + 137, 277, 138, 415 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 126, 142, 273, 172, 129, 128, 269, 133, 268, 288, + 140, 134, 161, 264, 274, 175, 382, 144, 282, 275, + 325, 218, 219, 320, 276, 115, 480, 330, 106, 105, + 291, 221, 500, 120, 154, 419, 175, 420, 175, 123, + 421, 278, 279, 202, 502, 280, 175, 503, 221, 106, + 155, 156, 6, 7, 505, 506, 124, 127, 289, 131, + 175, 121, 122, 130, 221, 135, 132, 383, 136, 107, + 194, 195, 196, 197, 198, 199, 275, 380, 201, 139, + 106, 276, 152, 157, 368, 158, 221, 207, 208, 210, + 173, 200, 214, 215, 216, 174, 298, 213, 217, 256, + 258, 259, 266, 527, 281, 265, 108, 263, 193, 292, + 501, 426, 299, 283, 270, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 254, 190, 191, 108, 192, 193, + 192, 193, 538, 293, 375, 290, 191, 275, 192, 193, + 253, 254, 276, 384, 185, 186, 187, 188, 220, 190, + 191, 284, 192, 193, 252, 294, 253, 254, 300, 301, + 323, 369, 370, 324, 275, 377, 328, 378, 428, 276, + 246, 247, 248, 249, 329, 251, 252, 436, 253, 254, + 379, 321, 445, 418, 322, 437, 417, 193, 460, 453, + 254, 423, 457, 451, 207, 462, 458, 464, 332, 427, + 459, 334, 335, 336, 337, 338, 339, 340, 341, 342, + 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, + 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, + 363, 364, 365, 366, 367, 443, 115, 465, 467, 162, + 472, 473, 376, 478, 163, 381, 484, 474, 416, 164, + 488, 165, 481, 166, 482, 485, 483, 439, 486, 424, + 425, 487, 491, 167, 168, 334, 509, 169, 516, 170, + 517, 442, 519, 525, 526, 297, 531, 119, 492, 446, + 444, 497, 331, 449, 175, 333, 512, 513, 441, 477, + 0, 0, 0, 0, 469, 0, 0, 269, 0, 476, + 489, 0, 0, 0, 0, 207, 0, 355, 450, 0, + 452, 0, 0, 0, 0, 0, 0, 92, 0, 0, + 375, 0, 0, 0, 0, 0, 0, 92, 0, 0, + 0, 0, 498, 0, 375, 0, 0, 0, 0, 92, + 0, 0, 0, 0, 0, 0, 92, 455, 456, 0, + 0, 0, 0, 0, 0, 92, 92, 0, 461, 0, + 0, 0, 463, -89, 181, 182, 183, 184, 185, 186, + 187, 188, 0, 190, 191, 0, 192, 193, 0, 0, + 150, 151, 0, 0, 0, 0, 504, 0, 0, 0, + 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 439, 0, 0, 0, 270, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 490, 92, 0, 0, 0, 0, 0, 376, 493, + 0, 0, 494, 0, 0, 496, 533, 0, 92, 92, + 0, 92, 376, 0, 0, 499, 0, 0, 92, 92, + 0, 0, 0, 507, 0, 508, 0, 0, 0, 510, + 0, 0, 511, 0, 0, 0, 514, 515, 0, 0, + 0, 0, 0, 0, 0, 92, 522, 0, 0, 0, + 0, 0, 439, 0, 0, 0, 528, 529, 0, 0, + 530, 0, 92, 92, 92, 92, 92, 92, 92, 92, + 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 0, 0, 536, 537, 221, 0, 539, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 2, 3, 4, 110, 0, + 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 23, 0, 25, 0, 27, + 0, 0, 0, 0, 0, 0, 92, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 36, + 37, 0, 145, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 0, 251, 252, 0, 253, 254, 0, 0, + 0, 0, 0, 0, 92, 0, 0, 0, 92, 111, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 113, 0, 0, 0, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 114, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 0, 68, 0, 69, 221, 70, 0, 0, + 0, 146, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 92, 92, 0, 2, + 3, 4, 110, 0, 6, 7, 92, 0, 0, 0, + 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, + 0, 25, 0, 27, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, + 35, 0, 0, 36, 37, -89, 242, 243, 244, 245, + 246, 247, 248, 249, 0, 251, 252, 0, 253, 254, + 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 112, 0, 92, 0, 0, 92, + 0, 0, 0, 0, 113, 0, 0, 0, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 114, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 221, 68, 0, 69, + 0, 70, 0, -3, 1, 287, 2, 3, 4, 5, + 0, 6, 7, 0, 0, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 0, 17, 18, 0, 19, 0, + 0, 0, 0, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 0, 0, 0, 29, 0, 30, 31, 0, + -88, 32, 33, 34, 0, 0, 0, 35, 0, 0, + 36, 37, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 0, 253, 254, + 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 0, 68, 0, 69, 371, 70, 2, + 3, 4, 110, 0, 6, 7, 0, 0, 8, 9, + 0, 0, 12, 13, 14, 15, 0, 0, 17, 18, + 0, 0, 0, 0, 0, 0, 0, 21, 0, 23, + 0, 25, 26, 27, 28, 0, 0, 0, 29, 0, + 0, 0, 0, -88, 0, 33, 0, 0, 0, 0, + 35, 0, 0, 36, 37, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 38, 39, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 0, 68, 0, 69, + 0, 70, -66, 2, 3, 4, 110, 0, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 0, 25, 0, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 36, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, + 0, 0, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 114, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 0, 68, 0, 69, 0, 70, 209, 2, 3, 4, + 5, 0, 6, 7, 0, 0, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 0, 17, 18, 0, 19, + 0, 0, 0, 0, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 0, 0, 0, 29, 0, 30, 31, + 0, -88, 32, 33, 34, 0, 0, 0, 35, 0, + 0, 36, 37, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 38, 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 0, 68, 0, 69, 0, 70, + 2, 3, 4, 5, 0, 6, 7, 0, 0, 8, + 9, 10, 160, 12, 13, 14, 15, 16, 0, 17, + 18, 0, 19, 0, 0, 0, 0, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 0, 0, 0, 29, + 0, 30, 31, 0, 0, 32, 33, 34, 0, 0, + 0, 35, 0, 0, 36, 37, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 0, 68, 0, + 69, 0, 70, 2, 3, 4, 5, 0, 6, 7, + 0, 0, 8, 9, 10, 0, 12, 13, 14, 15, + 16, 0, 17, 18, 0, 19, 0, 0, 0, 0, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 0, + 0, 0, 29, 0, 30, 31, 0, 0, 32, 33, + 34, 0, 0, 0, 35, 0, 0, 36, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 38, 39, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 0, 68, 0, 69, 0, 70, 2, 3, 4, 5, + 0, 6, 7, 0, 0, 8, 9, 0, 0, 12, + 13, 14, 15, 16, 0, 17, 18, 0, 19, 0, + 0, 0, 0, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 0, 0, 0, 29, 0, 30, 31, 0, + 0, 32, 33, 34, 0, 0, 0, 35, 0, 0, + 36, 37, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 0, 68, 0, 69, 0, 70, 2, + 3, 4, 110, 0, 6, 7, 0, 0, 8, 9, + 0, 0, 12, 13, 14, 15, 0, 0, 17, 18, + 0, 19, 0, 0, 0, 0, 0, 21, 0, 23, + 0, 25, 26, 27, 28, 0, 0, 0, 29, 0, + 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, + 35, 0, 0, 36, 37, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 38, 39, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 0, 68, 0, 69, + 475, 70, 2, 3, 4, 110, 0, 6, 7, 0, + 0, 8, 9, 0, 0, 12, 13, 14, 15, 0, + 0, 17, 18, 0, 19, 0, 0, 0, 0, 0, + 21, 0, 23, 0, 25, 26, 27, 28, 0, 0, + 0, 29, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 35, 0, 0, 36, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 0, + 68, 0, 69, 0, 70, 2, 3, 4, 110, 0, + 6, 7, 0, 0, 8, 9, 0, 0, 12, 13, + 14, 15, 0, 0, 17, 18, 0, 0, 0, 0, + 0, 0, 0, 21, 0, 23, 0, 25, 26, 27, + 28, 0, 0, 0, 29, 0, 0, 0, 0, -88, + 0, 33, 0, 0, 0, 0, 35, 0, 0, 36, + 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, + 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 0, 68, 0, 69, 0, 70, 2, 3, + 4, 110, 0, 6, 7, 0, 0, 8, 9, 0, + 0, 12, 13, 14, 15, 0, 0, 17, 18, 0, + 0, 0, 0, 0, 0, 0, 21, 0, 23, 0, + 25, 26, 27, 28, 0, 0, 0, 29, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 35, + 0, 0, 36, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 39, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 0, 68, 0, 69, 0, + 70, 2, 3, 4, 110, 0, 6, 7, 0, 0, + 8, 9, 0, 0, 12, 13, 14, 15, 0, 0, + 0, 18, 0, 0, 0, 0, 0, 0, 0, 21, + 0, 23, 0, 25, 26, 27, 0, 0, 0, 0, + 29, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 35, 0, 0, 36, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 0, 68, + 0, 69, 0, 70, 2, 3, 4, 110, 0, 6, + 7, 0, 0, 8, 9, 0, 0, 0, 0, 14, + 15, 0, 0, 0, 18, 0, 0, 0, 0, 0, + 0, 0, 21, 0, 23, 0, 25, 26, 27, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, + 33, 0, 0, 0, 0, 35, 0, 0, 36, 37, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 38, 39, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 0, 68, 0, 69, 0, 70, 2, 3, 4, + 110, 0, 6, 7, 0, 0, 8, 9, 0, 0, + 0, 0, 14, 15, 0, 0, 0, 18, 0, 0, + 0, 0, 0, 0, 0, 21, 0, 23, 0, 25, + 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 257, 0, 33, 0, 0, 0, 0, 35, 0, + 0, 36, 37, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 38, 39, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 0, 68, 0, 69, 0, 70, + 2, 3, 4, 110, 0, 6, 7, 0, 0, 8, + 9, 0, 0, 0, 0, 14, 15, 0, 0, 0, + 18, 0, 0, 0, 0, 0, 0, 0, 21, 0, + 23, 0, 25, 26, 27, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 0, 35, 0, 0, 36, 37, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 0, 68, 0, + 69, 0, 70, 2, 3, 4, 110, 0, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 0, 25, 0, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 36, 37, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 111, 112, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 113, 0, + 125, 0, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 114, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 0, 68, 0, 69, 0, 70, 2, 3, 4, 110, + 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 0, 25, 0, + 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 36, 37, 141, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 111, 112, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 113, 0, 0, 0, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 114, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 0, 68, 0, 69, 0, 70, 2, + 3, 4, 110, 0, 6, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, + 0, 25, 0, 27, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 35, 0, 0, 36, 37, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 112, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 113, 0, 0, 0, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 114, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 0, 68, 0, 69, + 153, 70, 2, 3, 4, 110, 0, 6, 7, 0, + 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 23, 0, 25, 0, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 35, 0, 0, 36, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 203, 111, 112, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 113, 0, 0, + 0, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 114, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 0, + 68, 0, 69, 0, 70, 2, 3, 4, 110, 0, + 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 23, 0, 25, 0, 27, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 36, + 37, 0, 438, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 113, 0, 0, 0, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 114, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 0, 68, 0, 69, 0, 70, 2, 3, + 4, 110, 0, 6, 7, 0, 0, 0, 0, 0, + 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, + 25, 0, 27, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, + 0, 0, 36, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 113, 0, 0, 0, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 114, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 0, 68, 0, 69, 0, + 70, 2, 3, 4, 110, 0, 6, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 23, 0, 25, 0, 27, 2, 3, 4, 110, + 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 35, 0, 0, 36, 37, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 0, 25, 0, + 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 38, 39, 35, 0, 0, + 36, 37, 0, 175, 0, 0, 0, 0, 0, 0, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 221, 68, + 0, 69, 0, 70, 0, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 0, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 221, 68, 176, 69, 0, 70, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 0, 192, 193, 175, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 0, + 253, 254, 0, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 221, 253, 254, 0, 0, -89, + 286, 0, 0, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 0, 192, + 193, 0, 0, 0, 0, 0, 0, 0, 221, 385, + 0, 0, 0, 0, 0, 295, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 0, 253, 254, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 221, + 253, 254, 0, 447, 448, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, + 411, 412, 413, 221, 414, 0, 0, 0, 440, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 221, 0, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 0, 253, 254, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 221, 253, 254, 0, 0, 454, + -89, -89, -89, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 0, 253, + 254, 0, 0, 0, 0, 0, 0, 0, 221, 0, + 0, 0, 296, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 0, 253, 254, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 221, + 253, 254, 0, 518, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 221, 0, 0, 0, 520, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 0, 253, 254, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 221, 253, 254, 0, 521, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 221, 0, + 0, 0, 523, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 470, 0, 0, 0, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 221, 253, 254, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 0, + 253, 254, 0, 524, 0, 0, 0, 221, 0, 267, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 327, 253, 254, 221, + 0, 0, 0, 0, 0, 0, 0, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 0, 253, + 254, 221, 0, 0, 0, 0, 0, 0, 495, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 532, 253, 254, 0, 0, 221, 0, 0, 0, 0, + 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 221, 253, 254, 0, 0, 0, 0, 535, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 534, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 221, 253, 254, 0, + 0, 0, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 221, 253, 254, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 221, 253, 254, + 0, 0, 0, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 326, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 221, 253, 254, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 221, 253, + 254, 0, 0, 0, 0, 0, 0, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 221, 253, 254, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 0, + 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -89, -89, -89, -89, -89, -89, -89, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 0, 253, 254 +}; + +static const yytype_int16 yycheck[] = +{ + 19, 36, 132, 86, 23, 22, 131, 29, 131, 146, + 35, 30, 74, 125, 135, 7, 6, 36, 139, 136, + 205, 10, 11, 193, 136, 7, 427, 212, 57, 132, + 60, 7, 59, 42, 69, 132, 7, 134, 7, 6, + 137, 39, 40, 105, 134, 43, 7, 137, 7, 57, + 69, 70, 8, 9, 27, 28, 57, 6, 60, 133, + 7, 12, 13, 8, 7, 6, 41, 57, 100, 77, + 95, 96, 97, 98, 99, 100, 193, 262, 103, 6, + 57, 193, 135, 0, 254, 132, 7, 106, 107, 108, + 138, 135, 111, 112, 113, 47, 158, 57, 135, 121, + 122, 135, 138, 504, 100, 77, 135, 124, 100, 139, + 137, 281, 77, 59, 131, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 100, 96, 97, 135, 99, 100, + 99, 100, 533, 152, 259, 137, 97, 254, 99, 100, + 99, 100, 254, 133, 91, 92, 93, 94, 137, 96, + 97, 137, 99, 100, 97, 134, 99, 100, 6, 41, + 59, 41, 41, 137, 281, 77, 136, 59, 6, 281, + 91, 92, 93, 94, 137, 96, 97, 139, 99, 100, + 137, 200, 135, 138, 203, 139, 269, 100, 373, 59, + 100, 274, 136, 135, 213, 41, 136, 135, 217, 282, + 137, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 297, 218, 8, 6, 20, + 77, 6, 259, 77, 25, 264, 431, 80, 267, 30, + 435, 32, 77, 34, 59, 56, 137, 292, 80, 278, + 279, 137, 134, 44, 45, 284, 80, 48, 6, 50, + 139, 296, 136, 8, 6, 158, 56, 10, 445, 301, + 299, 459, 213, 324, 7, 218, 483, 483, 295, 422, + -1, -1, -1, -1, 415, -1, -1, 422, -1, 422, + 437, -1, -1, -1, -1, 324, -1, 326, 327, -1, + 329, -1, -1, -1, -1, -1, -1, 0, -1, -1, + 445, -1, -1, -1, -1, -1, -1, 10, -1, -1, + -1, -1, 462, -1, 459, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, 369, 370, -1, + -1, -1, -1, -1, -1, 38, 39, -1, 377, -1, + -1, -1, 379, 86, 87, 88, 89, 90, 91, 92, + 93, 94, -1, 96, 97, -1, 99, 100, -1, -1, + 38, 39, -1, -1, -1, -1, 469, -1, -1, -1, + -1, 74, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 436, -1, -1, -1, 422, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 440, 105, -1, -1, -1, -1, -1, 445, 448, + -1, -1, 451, -1, -1, 454, 519, -1, 121, 122, + -1, 124, 459, -1, -1, 464, -1, -1, 131, 132, + -1, -1, -1, 472, -1, 474, -1, -1, -1, 478, + -1, -1, 481, -1, -1, -1, 485, 486, -1, -1, + -1, -1, -1, -1, -1, 158, 495, -1, -1, -1, + -1, -1, 517, -1, -1, -1, 505, 506, -1, -1, + 509, -1, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + -1, -1, 531, 532, 7, -1, 535, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 3, 4, 5, 6, -1, + 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, + 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 33, -1, 35, -1, 37, + -1, -1, -1, -1, -1, -1, 259, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 54, -1, -1, 57, + 58, -1, 60, 86, 87, 88, 89, 90, 91, 92, + 93, 94, -1, 96, 97, -1, 99, 100, -1, -1, + -1, -1, -1, -1, 297, -1, -1, -1, 301, 87, + 88, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 98, -1, -1, -1, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, -1, 131, -1, 133, 7, 135, -1, -1, + -1, 139, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 369, 370, -1, 3, + 4, 5, 6, -1, 8, 9, 379, -1, -1, -1, + -1, -1, -1, -1, 18, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, + -1, 35, -1, 37, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 422, + 54, -1, -1, 57, 58, 86, 87, 88, 89, 90, + 91, 92, 93, 94, -1, 96, 97, -1, 99, 100, + -1, -1, 445, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 87, 88, -1, 459, -1, -1, 462, + -1, -1, -1, -1, 98, -1, -1, -1, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 7, 131, -1, 133, + -1, 135, -1, 0, 1, 139, 3, 4, 5, 6, + -1, 8, 9, -1, -1, 12, 13, 14, 15, 16, + 17, 18, 19, 20, -1, 22, 23, -1, 25, -1, + -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, + 37, 38, -1, -1, -1, 42, -1, 44, 45, -1, + 47, 48, 49, 50, -1, -1, -1, 54, -1, -1, + 57, 58, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, -1, 99, 100, + 87, 88, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, -1, 131, -1, 133, 1, 135, 3, + 4, 5, 6, -1, 8, 9, -1, -1, 12, 13, + -1, -1, 16, 17, 18, 19, -1, -1, 22, 23, + -1, -1, -1, -1, -1, -1, -1, 31, -1, 33, + -1, 35, 36, 37, 38, -1, -1, -1, 42, -1, + -1, -1, -1, 47, -1, 49, -1, -1, -1, -1, + 54, -1, -1, 57, 58, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 87, 88, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, -1, 131, -1, 133, + -1, 135, 136, 3, 4, 5, 6, -1, 8, 9, + -1, -1, -1, -1, -1, -1, -1, -1, 18, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 33, -1, 35, -1, 37, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 54, -1, -1, 57, 58, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, 88, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 98, -1, + -1, -1, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + -1, 131, -1, 133, -1, 135, 136, 3, 4, 5, + 6, -1, 8, 9, -1, -1, 12, 13, 14, 15, + 16, 17, 18, 19, 20, -1, 22, 23, -1, 25, + -1, -1, -1, -1, 30, 31, 32, 33, 34, 35, + 36, 37, 38, -1, -1, -1, 42, -1, 44, 45, + -1, 47, 48, 49, 50, -1, -1, -1, 54, -1, + -1, 57, 58, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 87, 88, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, -1, 131, -1, 133, -1, 135, + 3, 4, 5, 6, -1, 8, 9, -1, -1, 12, + 13, 14, 15, 16, 17, 18, 19, 20, -1, 22, + 23, -1, 25, -1, -1, -1, -1, 30, 31, 32, + 33, 34, 35, 36, 37, 38, -1, -1, -1, 42, + -1, 44, 45, -1, -1, 48, 49, 50, -1, -1, + -1, 54, -1, -1, 57, 58, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 87, 88, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, -1, 131, -1, + 133, -1, 135, 3, 4, 5, 6, -1, 8, 9, + -1, -1, 12, 13, 14, -1, 16, 17, 18, 19, + 20, -1, 22, 23, -1, 25, -1, -1, -1, -1, + 30, 31, 32, 33, 34, 35, 36, 37, 38, -1, + -1, -1, 42, -1, 44, 45, -1, -1, 48, 49, + 50, -1, -1, -1, 54, -1, -1, 57, 58, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, 88, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + -1, 131, -1, 133, -1, 135, 3, 4, 5, 6, + -1, 8, 9, -1, -1, 12, 13, -1, -1, 16, + 17, 18, 19, 20, -1, 22, 23, -1, 25, -1, + -1, -1, -1, 30, 31, 32, 33, 34, 35, 36, + 37, 38, -1, -1, -1, 42, -1, 44, 45, -1, + -1, 48, 49, 50, -1, -1, -1, 54, -1, -1, + 57, 58, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 87, 88, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, -1, 131, -1, 133, -1, 135, 3, + 4, 5, 6, -1, 8, 9, -1, -1, 12, 13, + -1, -1, 16, 17, 18, 19, -1, -1, 22, 23, + -1, 25, -1, -1, -1, -1, -1, 31, -1, 33, + -1, 35, 36, 37, 38, -1, -1, -1, 42, -1, + -1, -1, -1, -1, -1, 49, -1, -1, -1, -1, + 54, -1, -1, 57, 58, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 87, 88, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, -1, 131, -1, 133, + 134, 135, 3, 4, 5, 6, -1, 8, 9, -1, + -1, 12, 13, -1, -1, 16, 17, 18, 19, -1, + -1, 22, 23, -1, 25, -1, -1, -1, -1, -1, + 31, -1, 33, -1, 35, 36, 37, 38, -1, -1, + -1, 42, -1, -1, -1, -1, -1, -1, 49, -1, + -1, -1, -1, 54, -1, -1, 57, 58, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 87, 88, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, -1, + 131, -1, 133, -1, 135, 3, 4, 5, 6, -1, + 8, 9, -1, -1, 12, 13, -1, -1, 16, 17, + 18, 19, -1, -1, 22, 23, -1, -1, -1, -1, + -1, -1, -1, 31, -1, 33, -1, 35, 36, 37, + 38, -1, -1, -1, 42, -1, -1, -1, -1, 47, + -1, 49, -1, -1, -1, -1, 54, -1, -1, 57, + 58, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, + 88, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, -1, 131, -1, 133, -1, 135, 3, 4, + 5, 6, -1, 8, 9, -1, -1, 12, 13, -1, + -1, 16, 17, 18, 19, -1, -1, 22, 23, -1, + -1, -1, -1, -1, -1, -1, 31, -1, 33, -1, + 35, 36, 37, 38, -1, -1, -1, 42, -1, -1, + -1, -1, -1, -1, 49, -1, -1, -1, -1, 54, + -1, -1, 57, 58, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 87, 88, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, -1, 131, -1, 133, -1, + 135, 3, 4, 5, 6, -1, 8, 9, -1, -1, + 12, 13, -1, -1, 16, 17, 18, 19, -1, -1, + -1, 23, -1, -1, -1, -1, -1, -1, -1, 31, + -1, 33, -1, 35, 36, 37, -1, -1, -1, -1, + 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, + -1, -1, 54, -1, -1, 57, 58, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 87, 88, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, -1, 131, + -1, 133, -1, 135, 3, 4, 5, 6, -1, 8, + 9, -1, -1, 12, 13, -1, -1, -1, -1, 18, + 19, -1, -1, -1, 23, -1, -1, -1, -1, -1, + -1, -1, 31, -1, 33, -1, 35, 36, 37, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 47, -1, + 49, -1, -1, -1, -1, 54, -1, -1, 57, 58, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 87, 88, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, -1, 131, -1, 133, -1, 135, 3, 4, 5, + 6, -1, 8, 9, -1, -1, 12, 13, -1, -1, + -1, -1, 18, 19, -1, -1, -1, 23, -1, -1, + -1, -1, -1, -1, -1, 31, -1, 33, -1, 35, + 36, 37, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 47, -1, 49, -1, -1, -1, -1, 54, -1, + -1, 57, 58, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 87, 88, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, -1, 131, -1, 133, -1, 135, + 3, 4, 5, 6, -1, 8, 9, -1, -1, 12, + 13, -1, -1, -1, -1, 18, 19, -1, -1, -1, + 23, -1, -1, -1, -1, -1, -1, -1, 31, -1, + 33, -1, 35, 36, 37, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 49, -1, -1, -1, + -1, 54, -1, -1, 57, 58, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 87, 88, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, -1, 131, -1, + 133, -1, 135, 3, 4, 5, 6, -1, 8, 9, + -1, -1, -1, -1, -1, -1, -1, -1, 18, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 33, -1, 35, -1, 37, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 54, -1, -1, 57, 58, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 87, 88, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 98, -1, + 100, -1, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + -1, 131, -1, 133, -1, 135, 3, 4, 5, 6, + -1, 8, 9, -1, -1, -1, -1, -1, -1, -1, + -1, 18, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 33, -1, 35, -1, + 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 54, -1, -1, + 57, 58, 59, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 87, 88, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 98, -1, -1, -1, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, -1, 131, -1, 133, -1, 135, 3, + 4, 5, 6, -1, 8, 9, -1, -1, -1, -1, + -1, -1, -1, -1, 18, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 33, + -1, 35, -1, 37, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 54, -1, -1, 57, 58, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 87, 88, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 98, -1, -1, -1, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, -1, 131, -1, 133, + 134, 135, 3, 4, 5, 6, -1, 8, 9, -1, + -1, -1, -1, -1, -1, -1, -1, 18, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 33, -1, 35, -1, 37, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 54, -1, -1, 57, 58, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 86, 87, 88, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 98, -1, -1, + -1, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, -1, + 131, -1, 133, -1, 135, 3, 4, 5, 6, -1, + 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, + 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 33, -1, 35, -1, 37, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 54, -1, -1, 57, + 58, -1, 60, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, + 88, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 98, -1, -1, -1, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, -1, 131, -1, 133, -1, 135, 3, 4, + 5, 6, -1, 8, 9, -1, -1, -1, -1, -1, + -1, -1, -1, 18, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 33, -1, + 35, -1, 37, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 54, + -1, -1, 57, 58, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 87, 88, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 98, -1, -1, -1, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, -1, 131, -1, 133, -1, + 135, 3, 4, 5, 6, -1, 8, 9, -1, -1, + -1, -1, -1, -1, -1, -1, 18, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, -1, 35, -1, 37, 3, 4, 5, 6, + -1, 8, 9, -1, -1, -1, -1, -1, -1, -1, + -1, 18, 54, -1, -1, 57, 58, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 33, -1, 35, -1, + 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 87, 88, 54, -1, -1, + 57, 58, -1, 7, -1, -1, -1, -1, -1, -1, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 7, 131, + -1, 133, -1, 135, -1, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, -1, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 7, 131, 79, 133, -1, 135, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, -1, 99, 100, 7, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, -1, + 99, 100, -1, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 7, 99, 100, -1, -1, 79, + 139, -1, -1, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, -1, 99, + 100, -1, -1, -1, -1, -1, -1, -1, 7, 6, + -1, -1, -1, -1, -1, 139, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, -1, 99, 100, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 7, + 99, 100, -1, 136, 137, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 7, 131, -1, -1, -1, 137, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 7, -1, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + -1, 99, 100, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 7, 99, 100, -1, -1, 137, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, -1, 99, + 100, -1, -1, -1, -1, -1, -1, -1, 7, -1, + -1, -1, 136, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, -1, 99, 100, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 7, + 99, 100, -1, 136, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 7, -1, -1, -1, 136, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + -1, 99, 100, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 7, 99, 100, -1, 136, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, + -1, -1, 136, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 7, 99, 100, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, -1, + 99, 100, -1, 136, -1, -1, -1, 7, -1, 51, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 56, 99, 100, 7, + -1, -1, -1, -1, -1, -1, -1, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, -1, 99, + 100, 7, -1, -1, -1, -1, -1, -1, 56, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 56, 99, 100, -1, -1, 7, -1, -1, -1, -1, + -1, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 7, 99, 100, -1, -1, -1, -1, 51, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 29, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 7, 99, 100, -1, + -1, -1, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 7, 99, 100, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 7, 99, 100, + -1, -1, -1, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 7, 99, 100, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 7, 99, + 100, -1, -1, -1, -1, -1, -1, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 7, 99, 100, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, -1, + 99, 100, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, -1, 99, 100 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 1, 3, 4, 5, 6, 8, 9, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 22, 23, 25, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 42, + 44, 45, 48, 49, 50, 54, 57, 58, 87, 88, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 131, 133, + 135, 141, 142, 143, 144, 146, 147, 149, 150, 153, + 154, 155, 156, 157, 158, 159, 166, 169, 170, 171, + 172, 178, 180, 181, 182, 185, 186, 194, 195, 198, + 199, 201, 202, 203, 206, 132, 57, 77, 135, 184, + 6, 87, 88, 98, 120, 177, 179, 180, 183, 147, + 42, 171, 171, 6, 57, 100, 179, 6, 169, 179, + 8, 133, 41, 172, 179, 6, 100, 210, 212, 6, + 184, 59, 176, 177, 179, 60, 139, 176, 196, 197, + 178, 178, 135, 134, 176, 179, 179, 0, 132, 145, + 15, 146, 20, 25, 30, 32, 34, 44, 45, 48, + 50, 148, 210, 138, 47, 7, 79, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 99, 100, 184, 184, 184, 184, 184, 184, + 135, 184, 146, 86, 173, 174, 175, 179, 179, 136, + 179, 204, 205, 57, 179, 179, 179, 135, 10, 11, + 137, 7, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 99, 100, 47, 172, 47, 172, 135, + 161, 167, 168, 169, 182, 77, 138, 51, 154, 166, + 169, 207, 209, 170, 161, 181, 182, 211, 39, 40, + 43, 100, 161, 59, 137, 164, 139, 139, 197, 60, + 137, 60, 139, 179, 134, 139, 136, 144, 146, 77, + 6, 41, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 211, 179, 179, 59, 137, 164, 86, 56, 136, 137, + 164, 173, 179, 183, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 211, 41, + 41, 1, 162, 163, 165, 166, 169, 77, 59, 137, + 164, 179, 6, 57, 133, 6, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 131, 213, 179, 210, 138, 132, + 134, 137, 208, 210, 179, 179, 211, 210, 6, 187, + 188, 189, 190, 191, 192, 193, 139, 139, 60, 176, + 137, 187, 184, 146, 179, 135, 172, 136, 137, 175, + 179, 135, 179, 59, 137, 172, 172, 136, 136, 137, + 164, 179, 41, 169, 135, 8, 151, 6, 152, 161, + 29, 200, 77, 6, 80, 134, 154, 209, 77, 160, + 160, 77, 59, 137, 164, 56, 80, 137, 164, 197, + 179, 134, 162, 179, 179, 56, 179, 165, 170, 179, + 59, 137, 134, 137, 210, 27, 28, 179, 179, 80, + 179, 179, 190, 191, 179, 179, 6, 139, 136, 136, + 136, 136, 179, 136, 136, 8, 6, 160, 179, 179, + 179, 56, 56, 210, 29, 51, 179, 179, 160, 179 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 140, 141, 142, 142, 143, 143, 143, 143, 143, + 143, 144, 144, 145, 145, 146, 146, 147, 147, 147, + 147, 147, 147, 147, 147, 147, 148, 148, 148, 148, + 148, 148, 148, 148, 148, 149, 150, 150, 150, 150, + 150, 150, 151, 151, 151, 152, 152, 152, 153, 154, + 154, 155, 155, 155, 156, 157, 157, 158, 158, 159, + 159, 160, 160, 161, 161, 161, 162, 162, 163, 163, + 164, 164, 165, 165, 166, 167, 168, 168, 169, 169, + 169, 170, 170, 170, 170, 170, 170, 170, 171, 171, + 172, 172, 172, 172, 172, 172, 172, 172, 173, 174, + 174, 175, 175, 175, 175, 176, 177, 177, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, + 178, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, + 180, 180, 181, 181, 181, 181, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, + 181, 181, 181, 181, 181, 181, 181, 181, 182, 182, + 183, 183, 184, 184, 185, 185, 186, 187, 188, 189, + 189, 189, 189, 189, 189, 190, 190, 191, 192, 193, + 193, 194, 194, 195, 195, 195, 195, 196, 196, 196, + 197, 197, 198, 199, 199, 200, 200, 201, 201, 201, + 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, + 201, 201, 201, 201, 201, 201, 201, 201, 201, 201, + 201, 201, 201, 201, 201, 202, 202, 203, 203, 203, + 203, 204, 205, 205, 205, 205, 206, 206, 207, 207, + 207, 207, 208, 208, 209, 209, 210, 210, 211, 211, + 212, 212, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 213, 213, 213, 213, 213, + 213, 213 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 0, 2, 1, 2, 3, 4, 2, + 3, 1, 2, 0, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 4, 2, 6, + 6, 7, 0, 1, 3, 0, 1, 3, 3, 2, + 4, 3, 4, 4, 2, 5, 5, 7, 8, 3, + 5, 0, 2, 0, 3, 3, 0, 2, 1, 3, + 0, 1, 1, 1, 3, 2, 1, 3, 1, 6, + 3, 1, 2, 3, 3, 4, 5, 5, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 3, 1, 1, 2, 2, 2, 1, 3, 1, 3, + 3, 3, 3, 3, 6, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, + 2, 1, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 6, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 1, 1, 3, 4, 1, 2, 1, 2, 1, 1, + 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, + 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, + 2, 3, 3, 4, 2, 3, 5, 1, 2, 1, + 1, 3, 3, 3, 5, 3, 5, 3, 2, 1, + 3, 2, 3, 2, 3, 4, 3, 2, 3, 5, + 1, 3, 5, 5, 8, 0, 5, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 6, 4, 3, 1, 4, + 7, 2, 1, 3, 3, 5, 6, 7, 1, 1, + 3, 3, 1, 1, 2, 4, 0, 1, 1, 1, + 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, parm, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +# ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +# endif /* !defined YY_LOCATION_PRINT */ + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, Location, parm); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *parm) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (yylocationp); + YY_USE (parm); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, void *parm) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, parm); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, + int yyrule, void *parm) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], + &(yylsp[(yyi + 1) - (yynrhs)]), parm); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, parm); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; + YYLTYPE *yylloc; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, void *parm) +{ + YY_USE (yyvaluep); + YY_USE (yylocationp); + YY_USE (parm); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void *parm) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + /* The location stack: array, bottom, top. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp = yyls; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + +/* User initialization code. */ +{ + GCLock lock; + yylloc.filename(ASTString(static_cast(parm)->filename)); +} + + + yylsp[0] = yylloc; + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + YYLTYPE *yyls1 = yyls; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, &yylloc, SCANNER); + } + + if (yychar <= END) + { + yychar = END; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + yyerror_range[1] = yylloc; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 5: /* item_list_head: item */ + { + ParserState* pp = static_cast(parm); + if ((yyvsp[0].item)) { + pp->model->addItem((yyvsp[0].item)); + GC::unlock(); + GC::lock(); + } + } + break; + + case 6: /* item_list_head: doc_file_comments item */ + { + ParserState* pp = static_cast(parm); + if ((yyvsp[0].item)) { + pp->model->addItem((yyvsp[0].item)); + GC::unlock(); + GC::lock(); + } + } + break; + + case 7: /* item_list_head: item_list_head ';' item */ + { + ParserState* pp = static_cast(parm); + if ((yyvsp[0].item)) { + pp->model->addItem((yyvsp[0].item)); + GC::unlock(); + GC::lock(); + } + } + break; + + case 8: /* item_list_head: item_list_head ';' doc_file_comments item */ + { + ParserState* pp = static_cast(parm); + if ((yyvsp[0].item)) { + pp->model->addItem((yyvsp[0].item)); + GC::unlock(); + GC::lock(); + } + } + break; + + case 9: /* item_list_head: item error_item_start */ +{ yyerror(&(yylsp[0]), parm, "unexpected item, expecting ';' or end of file"); YYERROR; } + break; + + case 11: /* doc_file_comments: "file-level documentation comment" */ + { + ParserState* pp = static_cast(parm); + if (pp->parseDocComments && (yyvsp[0].sValue)) { + pp->model->addDocComment((yyvsp[0].sValue)); + } + free((yyvsp[0].sValue)); + } + break; + + case 12: /* doc_file_comments: doc_file_comments "file-level documentation comment" */ + { + ParserState* pp = static_cast(parm); + if (pp->parseDocComments && (yyvsp[0].sValue)) { + pp->model->addDocComment((yyvsp[0].sValue)); + } + free((yyvsp[0].sValue)); + } + break; + + case 15: /* item: "documentation comment" item_tail */ + { (yyval.item) = (yyvsp[0].item); + ParserState* pp = static_cast(parm); + if (FunctionI* fi = Item::dyn_cast((yyval.item))) { + if (pp->parseDocComments) { + fi->ann().add(createDocComment((yylsp[-1]),(yyvsp[-1].sValue))); + } + } else if (VarDeclI* vdi = Item::dyn_cast((yyval.item))) { + if (pp->parseDocComments) { + vdi->e()->addAnnotation(createDocComment((yylsp[-1]),(yyvsp[-1].sValue))); + } + } else { + yyerror(&(yylsp[0]), parm, "documentation comments are only supported for function, predicate and variable declarations"); + } + free((yyvsp[-1].sValue)); + } + break; + + case 16: /* item: item_tail */ + { (yyval.item) = (yyvsp[0].item); } + break; + + case 17: /* item_tail: include_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"include") ? (yyvsp[0].item) : NULL; } + break; + + case 18: /* item_tail: vardecl_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"variable declaration") ? (yyvsp[0].item) : NULL; } + break; + + case 20: /* item_tail: constraint_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"constraint") ? (yyvsp[0].item) : NULL; } + break; + + case 21: /* item_tail: solve_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"solve") ? (yyvsp[0].item) : NULL; } + break; + + case 22: /* item_tail: output_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"output") ? (yyvsp[0].item) : NULL; } + break; + + case 23: /* item_tail: predicate_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"predicate") ? (yyvsp[0].item) : NULL; } + break; + + case 24: /* item_tail: function_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"predicate") ? (yyvsp[0].item) : NULL; } + break; + + case 25: /* item_tail: annotation_item */ + { (yyval.item)=notInDatafile(&(yyloc),parm,"annotation") ? (yyvsp[0].item) : NULL; } + break; + + case 35: /* include_item: "include" "string literal" */ + { ParserState* pp = static_cast(parm); + map::iterator ret = pp->seenModels.find((yyvsp[0].sValue)); + IncludeI* ii = new IncludeI((yyloc),ASTString((yyvsp[0].sValue))); + (yyval.item) = ii; + if (ret == pp->seenModels.end()) { + Model* im = new Model; + im->setParent(pp->model); + im->setFilename((yyvsp[0].sValue)); + string fpath = FileUtils::dir_name(pp->filename); + string fbase = FileUtils::base_name(pp->filename); + if (fpath=="") + fpath="./"; + ParseWorkItem pm(im, ii, fpath, (yyvsp[0].sValue)); + pp->files.push_back(pm); + ii->m(im); + pp->seenModels.insert(pair((yyvsp[0].sValue),im)); + } else { + ii->m(ret->second, false); + } + free((yyvsp[0].sValue)); + } + break; + + case 36: /* vardecl_item: ti_expr_and_id annotations */ + { if ((yyvsp[-1].vardeclexpr) && (yyvsp[0].expression_v)) (yyvsp[-1].vardeclexpr)->addAnnotations(*(yyvsp[0].expression_v)); + if ((yyvsp[-1].vardeclexpr)) + (yyval.item) = new VarDeclI((yyloc),(yyvsp[-1].vardeclexpr)); + delete (yyvsp[0].expression_v); + } + break; + + case 37: /* vardecl_item: ti_expr_and_id annotations "=" expr */ + { if ((yyvsp[-3].vardeclexpr)) (yyvsp[-3].vardeclexpr)->e((yyvsp[0].expression)); + if ((yyvsp[-3].vardeclexpr) && (yyvsp[-2].expression_v)) (yyvsp[-3].vardeclexpr)->addAnnotations(*(yyvsp[-2].expression_v)); + if ((yyvsp[-3].vardeclexpr)) + (yyval.item) = new VarDeclI((yyloc),(yyvsp[-3].vardeclexpr)); + delete (yyvsp[-2].expression_v); + } + break; + + case 38: /* vardecl_item: "enum" "identifier" */ + { + TypeInst* ti = new TypeInst((yyloc),Type::parsetint()); + ti->setIsEnum(true); + VarDecl* vd = new VarDecl((yyloc),ti,(yyvsp[0].sValue)); + free((yyvsp[0].sValue)); + (yyval.item) = new VarDeclI((yyloc),vd); + } + break; + + case 39: /* vardecl_item: "enum" "identifier" "=" '{' enum_id_list '}' */ + { + TypeInst* ti = new TypeInst((yyloc),Type::parsetint()); + ti->setIsEnum(true); + SetLit* sl = new SetLit((yyloc), *(yyvsp[-1].expression_v)); + VarDecl* vd = new VarDecl((yyloc),ti,(yyvsp[-4].sValue),sl); + free((yyvsp[-4].sValue)); + delete (yyvsp[-1].expression_v); + (yyval.item) = new VarDeclI((yyloc),vd); + } + break; + + case 40: /* vardecl_item: "enum" "identifier" "=" "[" string_lit_list "]" */ + { + TypeInst* ti = new TypeInst((yyloc),Type::parsetint()); + ti->setIsEnum(true); + vector args; + args.push_back(new ArrayLit((yyloc),*(yyvsp[-1].expression_v))); + Call* sl = new Call((yyloc), constants().ids.anonEnumFromStrings, args); + VarDecl* vd = new VarDecl((yyloc),ti,(yyvsp[-4].sValue),sl); + free((yyvsp[-4].sValue)); + delete (yyvsp[-1].expression_v); + (yyval.item) = new VarDeclI((yyloc),vd); + } + break; + + case 41: /* vardecl_item: "enum" "identifier" "=" "identifier" '(' expr ')' */ + { + TypeInst* ti = new TypeInst((yyloc),Type::parsetint()); + ti->setIsEnum(true); + vector args; + args.push_back((yyvsp[-1].expression)); + Call* sl = new Call((yyloc), ASTString((yyvsp[-3].sValue)), args); + VarDecl* vd = new VarDecl((yyloc),ti,(yyvsp[-5].sValue),sl); + free((yyvsp[-5].sValue)); + free((yyvsp[-3].sValue)); + (yyval.item) = new VarDeclI((yyloc),vd); + } + break; + + case 42: /* string_lit_list: %empty */ + { (yyval.expression_v) = new std::vector(); } + break; + + case 43: /* string_lit_list: "string literal" */ + { (yyval.expression_v) = new std::vector(); + (yyval.expression_v)->push_back(new StringLit((yyloc), (yyvsp[0].sValue))); free((yyvsp[0].sValue)); + } + break; + + case 44: /* string_lit_list: string_lit_list ',' "string literal" */ + { (yyval.expression_v) = (yyvsp[-2].expression_v); + if ((yyval.expression_v)) (yyval.expression_v)->push_back(new StringLit((yyloc), (yyvsp[0].sValue))); + free((yyvsp[0].sValue)); + } + break; + + case 45: /* enum_id_list: %empty */ + { (yyval.expression_v) = new std::vector(); } + break; + + case 46: /* enum_id_list: "identifier" */ + { (yyval.expression_v) = new std::vector(); + (yyval.expression_v)->push_back(new Id((yyloc),(yyvsp[0].sValue),NULL)); free((yyvsp[0].sValue)); + } + break; + + case 47: /* enum_id_list: enum_id_list ',' "identifier" */ + { (yyval.expression_v) = (yyvsp[-2].expression_v); if ((yyval.expression_v)) (yyval.expression_v)->push_back(new Id((yyloc),(yyvsp[0].sValue),NULL)); free((yyvsp[0].sValue)); } + break; + + case 48: /* assign_item: "identifier" "=" expr */ + { (yyval.item) = new AssignI((yyloc),(yyvsp[-2].sValue),(yyvsp[0].expression)); + free((yyvsp[-2].sValue)); + } + break; + + case 49: /* constraint_item: "constraint" expr */ + { (yyval.item) = new ConstraintI((yyloc),(yyvsp[0].expression));} + break; + + case 50: /* constraint_item: "constraint" "::" string_expr expr */ + { (yyval.item) = new ConstraintI((yyloc),(yyvsp[0].expression)); + if ((yyvsp[0].expression) && (yyvsp[-1].expression)) + (yyval.item)->cast()->e()->ann().add(new Call((yylsp[-2]), ASTString("mzn_constraint_name"), {(yyvsp[-1].expression)})); + } + break; + + case 51: /* solve_item: "solve" annotations "satisfy" */ + { (yyval.item) = SolveI::sat((yyloc)); + if ((yyval.item) && (yyvsp[-1].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-1].expression_v)); + delete (yyvsp[-1].expression_v); + } + break; + + case 52: /* solve_item: "solve" annotations "minimize" expr */ + { (yyval.item) = SolveI::min((yyloc),(yyvsp[0].expression)); + if ((yyval.item) && (yyvsp[-2].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-2].expression_v)); + delete (yyvsp[-2].expression_v); + } + break; + + case 53: /* solve_item: "solve" annotations "maximize" expr */ + { (yyval.item) = SolveI::max((yyloc),(yyvsp[0].expression)); + if ((yyval.item) && (yyvsp[-2].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-2].expression_v)); + delete (yyvsp[-2].expression_v); + } + break; + + case 54: /* output_item: "output" expr */ + { (yyval.item) = new OutputI((yyloc),(yyvsp[0].expression));} + break; + + case 55: /* predicate_item: "predicate" "identifier" params annotations operation_item_tail */ + { if ((yyvsp[-2].vardeclexpr_v)) (yyval.item) = new FunctionI((yyloc),(yyvsp[-3].sValue),new TypeInst((yyloc), + Type::varbool()),*(yyvsp[-2].vardeclexpr_v),(yyvsp[0].expression)); + if ((yyval.item) && (yyvsp[-1].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-1].expression_v)); + free((yyvsp[-3].sValue)); + delete (yyvsp[-2].vardeclexpr_v); + delete (yyvsp[-1].expression_v); + } + break; + + case 56: /* predicate_item: "test" "identifier" params annotations operation_item_tail */ + { if ((yyvsp[-2].vardeclexpr_v)) (yyval.item) = new FunctionI((yyloc),(yyvsp[-3].sValue),new TypeInst((yyloc), + Type::parbool()),*(yyvsp[-2].vardeclexpr_v),(yyvsp[0].expression)); + if ((yyval.item) && (yyvsp[-1].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-1].expression_v)); + free((yyvsp[-3].sValue)); + delete (yyvsp[-2].vardeclexpr_v); + delete (yyvsp[-1].expression_v); + } + break; + + case 57: /* function_item: "function" ti_expr ':' id_or_quoted_op params annotations operation_item_tail */ + { if ((yyvsp[-2].vardeclexpr_v)) (yyval.item) = new FunctionI((yyloc),(yyvsp[-3].sValue),(yyvsp[-5].tiexpr),*(yyvsp[-2].vardeclexpr_v),(yyvsp[0].expression)); + if ((yyval.item) && (yyvsp[-1].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-1].expression_v)); + free((yyvsp[-3].sValue)); + delete (yyvsp[-2].vardeclexpr_v); + delete (yyvsp[-1].expression_v); + } + break; + + case 58: /* function_item: ti_expr ':' "identifier" '(' params_list ')' annotations operation_item_tail */ + { if ((yyvsp[-3].vardeclexpr_v)) (yyval.item) = new FunctionI((yyloc),(yyvsp[-5].sValue),(yyvsp[-7].tiexpr),*(yyvsp[-3].vardeclexpr_v),(yyvsp[0].expression)); + if ((yyval.item) && (yyvsp[-1].expression_v)) (yyval.item)->cast()->ann().add(*(yyvsp[-1].expression_v)); + free((yyvsp[-5].sValue)); + delete (yyvsp[-3].vardeclexpr_v); + delete (yyvsp[-1].expression_v); + } + break; + + case 59: /* annotation_item: "annotation" "identifier" params */ + { + TypeInst* ti=new TypeInst((yylsp[-2]),Type::ann()); + if ((yyvsp[0].vardeclexpr_v)==NULL || (yyvsp[0].vardeclexpr_v)->empty()) { + VarDecl* vd = new VarDecl((yyloc),ti,(yyvsp[-1].sValue)); + (yyval.item) = new VarDeclI((yyloc),vd); + } else { + (yyval.item) = new FunctionI((yyloc),(yyvsp[-1].sValue),ti,*(yyvsp[0].vardeclexpr_v),NULL); + } + free((yyvsp[-1].sValue)); + delete (yyvsp[0].vardeclexpr_v); + } + break; + + case 60: /* annotation_item: "annotation" "identifier" params "=" expr */ + { TypeInst* ti=new TypeInst((yylsp[-4]),Type::ann()); + if ((yyvsp[-2].vardeclexpr_v)) (yyval.item) = new FunctionI((yyloc),(yyvsp[-3].sValue),ti,*(yyvsp[-2].vardeclexpr_v),(yyvsp[0].expression)); + delete (yyvsp[-2].vardeclexpr_v); + } + break; + + case 61: /* operation_item_tail: %empty */ + { (yyval.expression)=NULL; } + break; + + case 62: /* operation_item_tail: "=" expr */ + { (yyval.expression)=(yyvsp[0].expression); } + break; + + case 63: /* params: %empty */ + { (yyval.vardeclexpr_v)=new vector(); } + break; + + case 64: /* params: '(' params_list ')' */ + { (yyval.vardeclexpr_v)=(yyvsp[-1].vardeclexpr_v); } + break; + + case 65: /* params: '(' error ')' */ + { (yyval.vardeclexpr_v)=new vector(); } + break; + + case 66: /* params_list: %empty */ + { (yyval.vardeclexpr_v)=new vector(); } + break; + + case 67: /* params_list: params_list_head comma_or_none */ + { (yyval.vardeclexpr_v)=(yyvsp[-1].vardeclexpr_v); } + break; + + case 68: /* params_list_head: ti_expr_and_id_or_anon */ + { (yyval.vardeclexpr_v)=new vector(); + if ((yyvsp[0].vardeclexpr)) (yyvsp[0].vardeclexpr)->toplevel(false); + if ((yyvsp[0].vardeclexpr)) (yyval.vardeclexpr_v)->push_back((yyvsp[0].vardeclexpr)); } + break; + + case 69: /* params_list_head: params_list_head ',' ti_expr_and_id_or_anon */ + { (yyval.vardeclexpr_v)=(yyvsp[-2].vardeclexpr_v); + if ((yyvsp[0].vardeclexpr)) (yyvsp[0].vardeclexpr)->toplevel(false); + if ((yyvsp[-2].vardeclexpr_v) && (yyvsp[0].vardeclexpr)) (yyvsp[-2].vardeclexpr_v)->push_back((yyvsp[0].vardeclexpr)); } + break; + + case 72: /* ti_expr_and_id_or_anon: ti_expr_and_id */ + { (yyval.vardeclexpr)=(yyvsp[0].vardeclexpr); } + break; + + case 73: /* ti_expr_and_id_or_anon: ti_expr */ + { if ((yyvsp[0].tiexpr)) (yyval.vardeclexpr)=new VarDecl((yyloc), (yyvsp[0].tiexpr), ""); } + break; + + case 74: /* ti_expr_and_id: ti_expr ':' "identifier" */ + { if ((yyvsp[-2].tiexpr) && (yyvsp[0].sValue)) (yyval.vardeclexpr) = new VarDecl((yyloc), (yyvsp[-2].tiexpr), (yyvsp[0].sValue)); + free((yyvsp[0].sValue)); + } + break; + + case 75: /* ti_expr_list: ti_expr_list_head comma_or_none */ + { (yyval.tiexpr_v)=(yyvsp[-1].tiexpr_v); } + break; + + case 76: /* ti_expr_list_head: ti_expr */ + { (yyval.tiexpr_v)=new vector(); (yyval.tiexpr_v)->push_back((yyvsp[0].tiexpr)); } + break; + + case 77: /* ti_expr_list_head: ti_expr_list_head ',' ti_expr */ + { (yyval.tiexpr_v)=(yyvsp[-2].tiexpr_v); if ((yyvsp[-2].tiexpr_v) && (yyvsp[0].tiexpr)) (yyvsp[-2].tiexpr_v)->push_back((yyvsp[0].tiexpr)); } + break; + + case 79: /* ti_expr: "array" "[" ti_expr_list "]" "of" base_ti_expr */ + { + (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr) && (yyvsp[-3].tiexpr_v)) (yyval.tiexpr)->setRanges(*(yyvsp[-3].tiexpr_v)); + delete (yyvsp[-3].tiexpr_v); + } + break; + + case 80: /* ti_expr: "list" "of" base_ti_expr */ + { + (yyval.tiexpr) = (yyvsp[0].tiexpr); + std::vector ti(1); + ti[0] = new TypeInst((yyloc),Type::parint()); + if ((yyval.tiexpr)) (yyval.tiexpr)->setRanges(ti); + } + break; + + case 81: /* base_ti_expr: base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + } + break; + + case 82: /* base_ti_expr: "opt" base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr)) { + Type tt = (yyval.tiexpr)->type(); + tt.ot(Type::OT_OPTIONAL); + (yyval.tiexpr)->type(tt); + } + } + break; + + case 83: /* base_ti_expr: "par" opt_opt base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr) && (yyvsp[-1].bValue)) { + Type tt = (yyval.tiexpr)->type(); + tt.ot(Type::OT_OPTIONAL); + (yyval.tiexpr)->type(tt); + } + } + break; + + case 84: /* base_ti_expr: "var" opt_opt base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr)) { + Type tt = (yyval.tiexpr)->type(); + tt.ti(Type::TI_VAR); + if ((yyvsp[-1].bValue)) tt.ot(Type::OT_OPTIONAL); + (yyval.tiexpr)->type(tt); + } + } + break; + + case 85: /* base_ti_expr: opt_opt "set" "of" base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr)) { + Type tt = (yyval.tiexpr)->type(); + tt.st(Type::ST_SET); + if ((yyvsp[-3].bValue)) tt.ot(Type::OT_OPTIONAL); + (yyval.tiexpr)->type(tt); + } + } + break; + + case 86: /* base_ti_expr: "par" opt_opt "set" "of" base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr)) { + Type tt = (yyval.tiexpr)->type(); + tt.st(Type::ST_SET); + if ((yyvsp[-3].bValue)) tt.ot(Type::OT_OPTIONAL); + (yyval.tiexpr)->type(tt); + } + } + break; + + case 87: /* base_ti_expr: "var" opt_opt "set" "of" base_ti_expr_tail */ + { (yyval.tiexpr) = (yyvsp[0].tiexpr); + if ((yyval.tiexpr)) { + Type tt = (yyval.tiexpr)->type(); + tt.ti(Type::TI_VAR); + tt.st(Type::ST_SET); + if ((yyvsp[-3].bValue)) tt.ot(Type::OT_OPTIONAL); + (yyval.tiexpr)->type(tt); + } + } + break; + + case 88: /* opt_opt: %empty */ + { (yyval.bValue) = false; } + break; + + case 89: /* opt_opt: "opt" */ + { (yyval.bValue) = true; } + break; + + case 90: /* base_ti_expr_tail: "int" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::parint()); } + break; + + case 91: /* base_ti_expr_tail: "bool" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::parbool()); } + break; + + case 92: /* base_ti_expr_tail: "float" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::parfloat()); } + break; + + case 93: /* base_ti_expr_tail: "string" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::parstring()); } + break; + + case 94: /* base_ti_expr_tail: "ann" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::ann()); } + break; + + case 95: /* base_ti_expr_tail: set_expr */ + { if ((yyvsp[0].expression)) (yyval.tiexpr) = new TypeInst((yyloc),Type(),(yyvsp[0].expression)); } + break; + + case 96: /* base_ti_expr_tail: "type-inst identifier" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::top(), + new TIId((yyloc), (yyvsp[0].sValue))); + free((yyvsp[0].sValue)); + } + break; + + case 97: /* base_ti_expr_tail: "type-inst enum identifier" */ + { (yyval.tiexpr) = new TypeInst((yyloc),Type::parint(), + new TIId((yyloc), (yyvsp[0].sValue))); + free((yyvsp[0].sValue)); + } + break; + + case 99: /* array_access_expr_list_head: array_access_expr */ + { (yyval.expression_v)=new std::vector; (yyval.expression_v)->push_back((yyvsp[0].expression)); } + break; + + case 100: /* array_access_expr_list_head: array_access_expr_list_head ',' array_access_expr */ + { (yyval.expression_v)=(yyvsp[-2].expression_v); if ((yyval.expression_v) && (yyvsp[0].expression)) (yyval.expression_v)->push_back((yyvsp[0].expression)); } + break; + + case 101: /* array_access_expr: expr */ + { (yyval.expression) = (yyvsp[0].expression); } + break; + + case 102: /* array_access_expr: ".." */ + { (yyval.expression)=new SetLit((yyloc), IntSetVal::a(-IntVal::infinity(),IntVal::infinity())); } + break; + + case 103: /* array_access_expr: ".." expr */ + { if ((yyvsp[0].expression)==NULL) { + (yyval.expression) = NULL; + } else if ((yyvsp[0].expression)->isa()) { + (yyval.expression)=new SetLit((yyloc), IntSetVal::a(-IntVal::infinity(),(yyvsp[0].expression)->cast()->v())); + } else { + (yyval.expression)=new BinOp((yyloc), IntLit::a(-IntVal::infinity()), BOT_DOTDOT, (yyvsp[0].expression)); + } + } + break; + + case 104: /* array_access_expr: expr ".." */ + { if ((yyvsp[-1].expression)==NULL) { + (yyval.expression) = NULL; + } else if ((yyvsp[-1].expression)->isa()) { + (yyval.expression)=new SetLit((yyloc), IntSetVal::a((yyvsp[-1].expression)->cast()->v(),IntVal::infinity())); + } else { + (yyval.expression)=new BinOp((yyloc), (yyvsp[-1].expression), BOT_DOTDOT, IntLit::a(IntVal::infinity())); + } + } + break; + + case 106: /* expr_list_head: expr */ + { (yyval.expression_v)=new std::vector; (yyval.expression_v)->push_back((yyvsp[0].expression)); } + break; + + case 107: /* expr_list_head: expr_list_head ',' expr */ + { (yyval.expression_v)=(yyvsp[-2].expression_v); if ((yyval.expression_v) && (yyvsp[0].expression)) (yyval.expression_v)->push_back((yyvsp[0].expression)); } + break; + + case 109: /* set_expr: set_expr "::" annotation_expr */ + { if ((yyvsp[-2].expression) && (yyvsp[0].expression)) (yyvsp[-2].expression)->addAnnotation((yyvsp[0].expression)); (yyval.expression)=(yyvsp[-2].expression); } + break; + + case 110: /* set_expr: set_expr "union" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_UNION, (yyvsp[0].expression)); } + break; + + case 111: /* set_expr: set_expr "diff" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_DIFF, (yyvsp[0].expression)); } + break; + + case 112: /* set_expr: set_expr "symdiff" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_SYMDIFF, (yyvsp[0].expression)); } + break; + + case 113: /* set_expr: set_expr ".." set_expr */ + { if ((yyvsp[-2].expression)==NULL || (yyvsp[0].expression)==NULL) { + (yyval.expression) = NULL; + } else if ((yyvsp[-2].expression)->isa() && (yyvsp[0].expression)->isa()) { + (yyval.expression)=new SetLit((yyloc), IntSetVal::a((yyvsp[-2].expression)->cast()->v(),(yyvsp[0].expression)->cast()->v())); + } else { + (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_DOTDOT, (yyvsp[0].expression)); + } + } + break; + + case 114: /* set_expr: "'..'" '(' expr ',' expr ')' */ + { if ((yyvsp[-3].expression)==NULL || (yyvsp[-1].expression)==NULL) { + (yyval.expression) = NULL; + } else if ((yyvsp[-3].expression)->isa() && (yyvsp[-1].expression)->isa()) { + (yyval.expression)=new SetLit((yyloc), IntSetVal::a((yyvsp[-3].expression)->cast()->v(),(yyvsp[-1].expression)->cast()->v())); + } else { + (yyval.expression)=new BinOp((yyloc), (yyvsp[-3].expression), BOT_DOTDOT, (yyvsp[-1].expression)); + } + } + break; + + case 115: /* set_expr: set_expr "intersect" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_INTERSECT, (yyvsp[0].expression)); } + break; + + case 116: /* set_expr: set_expr "++" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_PLUSPLUS, (yyvsp[0].expression)); } + break; + + case 117: /* set_expr: set_expr "+" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_PLUS, (yyvsp[0].expression)); } + break; + + case 118: /* set_expr: set_expr "-" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_MINUS, (yyvsp[0].expression)); } + break; + + case 119: /* set_expr: set_expr "*" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_MULT, (yyvsp[0].expression)); } + break; + + case 120: /* set_expr: set_expr "/" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_DIV, (yyvsp[0].expression)); } + break; + + case 121: /* set_expr: set_expr "div" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_IDIV, (yyvsp[0].expression)); } + break; + + case 122: /* set_expr: set_expr "mod" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_MOD, (yyvsp[0].expression)); } + break; + + case 123: /* set_expr: set_expr "^" set_expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_POW, (yyvsp[0].expression)); } + break; + + case 124: /* set_expr: set_expr "~+" set_expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~+"), args); + } + break; + + case 125: /* set_expr: set_expr "~-" set_expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~-"), args); + } + break; + + case 126: /* set_expr: set_expr "~*" set_expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~*"), args); + } + break; + + case 127: /* set_expr: set_expr "~=" set_expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~="), args); + } + break; + + case 128: /* set_expr: set_expr "quoted identifier" set_expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), (yyvsp[-1].sValue), args); + free((yyvsp[-1].sValue)); + } + break; + + case 129: /* set_expr: "+" set_expr */ + { (yyval.expression)=new UnOp((yyloc), UOT_PLUS, (yyvsp[0].expression)); } + break; + + case 130: /* set_expr: "-" set_expr */ + { if ((yyvsp[0].expression) && (yyvsp[0].expression)->isa()) { + (yyval.expression) = IntLit::a(-(yyvsp[0].expression)->cast()->v()); + } else if ((yyvsp[0].expression) && (yyvsp[0].expression)->isa()) { + (yyval.expression) = FloatLit::a(-(yyvsp[0].expression)->cast()->v()); + } else { + (yyval.expression)=new UnOp((yyloc), UOT_MINUS, (yyvsp[0].expression)); + } + } + break; + + case 132: /* expr: expr "::" annotation_expr */ + { if ((yyvsp[-2].expression) && (yyvsp[0].expression)) (yyvsp[-2].expression)->addAnnotation((yyvsp[0].expression)); (yyval.expression)=(yyvsp[-2].expression); } + break; + + case 133: /* expr: expr "<->" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_EQUIV, (yyvsp[0].expression)); } + break; + + case 134: /* expr: expr "->" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_IMPL, (yyvsp[0].expression)); } + break; + + case 135: /* expr: expr "<-" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_RIMPL, (yyvsp[0].expression)); } + break; + + case 136: /* expr: expr "\\/" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_OR, (yyvsp[0].expression)); } + break; + + case 137: /* expr: expr "xor" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_XOR, (yyvsp[0].expression)); } + break; + + case 138: /* expr: expr "/\\" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_AND, (yyvsp[0].expression)); } + break; + + case 139: /* expr: expr "<" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_LE, (yyvsp[0].expression)); } + break; + + case 140: /* expr: expr ">" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_GR, (yyvsp[0].expression)); } + break; + + case 141: /* expr: expr "<=" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_LQ, (yyvsp[0].expression)); } + break; + + case 142: /* expr: expr ">=" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_GQ, (yyvsp[0].expression)); } + break; + + case 143: /* expr: expr "=" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_EQ, (yyvsp[0].expression)); } + break; + + case 144: /* expr: expr "!=" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_NQ, (yyvsp[0].expression)); } + break; + + case 145: /* expr: expr "in" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_IN, (yyvsp[0].expression)); } + break; + + case 146: /* expr: expr "subset" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_SUBSET, (yyvsp[0].expression)); } + break; + + case 147: /* expr: expr "superset" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_SUPERSET, (yyvsp[0].expression)); } + break; + + case 148: /* expr: expr "union" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_UNION, (yyvsp[0].expression)); } + break; + + case 149: /* expr: expr "diff" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_DIFF, (yyvsp[0].expression)); } + break; + + case 150: /* expr: expr "symdiff" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_SYMDIFF, (yyvsp[0].expression)); } + break; + + case 151: /* expr: expr ".." expr */ + { if ((yyvsp[-2].expression)==NULL || (yyvsp[0].expression)==NULL) { + (yyval.expression) = NULL; + } else if ((yyvsp[-2].expression)->isa() && (yyvsp[0].expression)->isa()) { + (yyval.expression)=new SetLit((yyloc), IntSetVal::a((yyvsp[-2].expression)->cast()->v(),(yyvsp[0].expression)->cast()->v())); + } else { + (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_DOTDOT, (yyvsp[0].expression)); + } + } + break; + + case 152: /* expr: "'..'" '(' expr ',' expr ')' */ + { if ((yyvsp[-3].expression)==NULL || (yyvsp[-1].expression)==NULL) { + (yyval.expression) = NULL; + } else if ((yyvsp[-3].expression)->isa() && (yyvsp[-1].expression)->isa()) { + (yyval.expression)=new SetLit((yyloc), IntSetVal::a((yyvsp[-3].expression)->cast()->v(),(yyvsp[-1].expression)->cast()->v())); + } else { + (yyval.expression)=new BinOp((yyloc), (yyvsp[-3].expression), BOT_DOTDOT, (yyvsp[-1].expression)); + } + } + break; + + case 153: /* expr: expr "intersect" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_INTERSECT, (yyvsp[0].expression)); } + break; + + case 154: /* expr: expr "++" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_PLUSPLUS, (yyvsp[0].expression)); } + break; + + case 155: /* expr: expr "+" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_PLUS, (yyvsp[0].expression)); } + break; + + case 156: /* expr: expr "-" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_MINUS, (yyvsp[0].expression)); } + break; + + case 157: /* expr: expr "*" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_MULT, (yyvsp[0].expression)); } + break; + + case 158: /* expr: expr "/" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_DIV, (yyvsp[0].expression)); } + break; + + case 159: /* expr: expr "div" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_IDIV, (yyvsp[0].expression)); } + break; + + case 160: /* expr: expr "mod" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_MOD, (yyvsp[0].expression)); } + break; + + case 161: /* expr: expr "^" expr */ + { (yyval.expression)=new BinOp((yyloc), (yyvsp[-2].expression), BOT_POW, (yyvsp[0].expression)); } + break; + + case 162: /* expr: expr "~+" expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~+"), args); + } + break; + + case 163: /* expr: expr "~-" expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~-"), args); + } + break; + + case 164: /* expr: expr "~*" expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~*"), args); + } + break; + + case 165: /* expr: expr "~=" expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), ASTString("~="), args); + } + break; + + case 166: /* expr: expr "quoted identifier" expr */ + { vector args; + args.push_back((yyvsp[-2].expression)); args.push_back((yyvsp[0].expression)); + (yyval.expression)=new Call((yyloc), (yyvsp[-1].sValue), args); + free((yyvsp[-1].sValue)); + } + break; + + case 167: /* expr: "not" expr */ + { (yyval.expression)=new UnOp((yyloc), UOT_NOT, (yyvsp[0].expression)); } + break; + + case 168: /* expr: "+" expr */ + { if (((yyvsp[0].expression) && (yyvsp[0].expression)->isa()) || ((yyvsp[0].expression) && (yyvsp[0].expression)->isa())) { + (yyval.expression) = (yyvsp[0].expression); + } else { + (yyval.expression)=new UnOp((yyloc), UOT_PLUS, (yyvsp[0].expression)); + } + } + break; + + case 169: /* expr: "-" expr */ + { if ((yyvsp[0].expression) && (yyvsp[0].expression)->isa()) { + (yyval.expression) = IntLit::a(-(yyvsp[0].expression)->cast()->v()); + } else if ((yyvsp[0].expression) && (yyvsp[0].expression)->isa()) { + (yyval.expression) = FloatLit::a(-(yyvsp[0].expression)->cast()->v()); + } else { + (yyval.expression)=new UnOp((yyloc), UOT_MINUS, (yyvsp[0].expression)); + } + } + break; + + case 170: /* expr_atom_head: expr_atom_head_nonstring */ + { (yyval.expression)=(yyvsp[0].expression); } + break; + + case 171: /* expr_atom_head: string_expr */ + { (yyval.expression)=(yyvsp[0].expression); } + break; + + case 172: /* expr_atom_head_nonstring: '(' expr ')' */ + { (yyval.expression)=(yyvsp[-1].expression); } + break; + + case 173: /* expr_atom_head_nonstring: '(' expr ')' array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-2].expression), *(yyvsp[0].expression_vv)); delete (yyvsp[0].expression_vv); } + break; + + case 174: /* expr_atom_head_nonstring: "identifier" */ + { (yyval.expression)=new Id((yyloc), (yyvsp[0].sValue), NULL); free((yyvsp[0].sValue)); } + break; + + case 175: /* expr_atom_head_nonstring: "identifier" array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), new Id((yylsp[-1]),(yyvsp[-1].sValue),NULL), *(yyvsp[0].expression_vv)); + free((yyvsp[-1].sValue)); delete (yyvsp[0].expression_vv); } + break; + + case 176: /* expr_atom_head_nonstring: "_" */ + { (yyval.expression)=new AnonVar((yyloc)); } + break; + + case 177: /* expr_atom_head_nonstring: "_" array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), new AnonVar((yyloc)), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 178: /* expr_atom_head_nonstring: "bool literal" */ + { (yyval.expression)=constants().boollit(((yyvsp[0].iValue)!=0)); } + break; + + case 179: /* expr_atom_head_nonstring: "integer literal" */ + { (yyval.expression)=IntLit::a((yyvsp[0].iValue)); } + break; + + case 180: /* expr_atom_head_nonstring: "infinity" */ + { (yyval.expression)=IntLit::a(IntVal::infinity()); } + break; + + case 181: /* expr_atom_head_nonstring: "float literal" */ + { (yyval.expression)=FloatLit::a((yyvsp[0].dValue)); } + break; + + case 182: /* expr_atom_head_nonstring: "<>" */ + { (yyval.expression)=constants().absent; } + break; + + case 184: /* expr_atom_head_nonstring: set_literal array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 186: /* expr_atom_head_nonstring: set_comp array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 188: /* expr_atom_head_nonstring: simple_array_literal array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 190: /* expr_atom_head_nonstring: simple_array_literal_2d array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 192: /* expr_atom_head_nonstring: simple_array_comp array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 194: /* expr_atom_head_nonstring: if_then_else_expr array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 197: /* expr_atom_head_nonstring: call_expr array_access_tail */ + { if ((yyvsp[0].expression_vv)) (yyval.expression)=createArrayAccess((yyloc), (yyvsp[-1].expression), *(yyvsp[0].expression_vv)); + delete (yyvsp[0].expression_vv); } + break; + + case 198: /* string_expr: "string literal" */ + { (yyval.expression)=new StringLit((yyloc), (yyvsp[0].sValue)); free((yyvsp[0].sValue)); } + break; + + case 199: /* string_expr: "interpolated string start" string_quote_rest */ + { (yyval.expression)=new BinOp((yyloc), new StringLit((yyloc), (yyvsp[-1].sValue)), BOT_PLUSPLUS, (yyvsp[0].expression)); + free((yyvsp[-1].sValue)); + } + break; + + case 200: /* string_quote_rest: expr_list_head "interpolated string end" */ + { if ((yyvsp[-1].expression_v)) (yyval.expression)=new BinOp((yyloc), new Call((yyloc), ASTString("format"), *(yyvsp[-1].expression_v)), BOT_PLUSPLUS, new StringLit((yyloc),(yyvsp[0].sValue))); + free((yyvsp[0].sValue)); + delete (yyvsp[-1].expression_v); + } + break; + + case 201: /* string_quote_rest: expr_list_head "interpolated string middle" string_quote_rest */ + { if ((yyvsp[-2].expression_v)) (yyval.expression)=new BinOp((yyloc), new Call((yyloc), ASTString("format"), *(yyvsp[-2].expression_v)), BOT_PLUSPLUS, + new BinOp((yyloc), new StringLit((yyloc),(yyvsp[-1].sValue)), BOT_PLUSPLUS, (yyvsp[0].expression))); + free((yyvsp[-1].sValue)); + delete (yyvsp[-2].expression_v); + } + break; + + case 202: /* array_access_tail: "[" array_access_expr_list "]" */ + { (yyval.expression_vv)=new std::vector >(); + if ((yyvsp[-1].expression_v)) { + (yyval.expression_vv)->push_back(*(yyvsp[-1].expression_v)); + delete (yyvsp[-1].expression_v); + } + } + break; + + case 203: /* array_access_tail: array_access_tail "[" array_access_expr_list "]" */ + { (yyval.expression_vv)=(yyvsp[-3].expression_vv); + if ((yyval.expression_vv) && (yyvsp[-1].expression_v)) { + (yyval.expression_vv)->push_back(*(yyvsp[-1].expression_v)); + delete (yyvsp[-1].expression_v); + } + } + break; + + case 204: /* set_literal: '{' '}' */ + { (yyval.expression) = new SetLit((yyloc), std::vector()); } + break; + + case 205: /* set_literal: '{' expr_list '}' */ + { if ((yyvsp[-1].expression_v)) (yyval.expression) = new SetLit((yyloc), *(yyvsp[-1].expression_v)); + delete (yyvsp[-1].expression_v); } + break; + + case 206: /* set_comp: '{' expr '|' comp_tail '}' */ + { if ((yyvsp[-1].generators)) (yyval.expression) = new Comprehension((yyloc), (yyvsp[-3].expression), *(yyvsp[-1].generators), true); + delete (yyvsp[-1].generators); + } + break; + + case 207: /* comp_tail: generator_list */ + { if ((yyvsp[0].generator_v)) (yyval.generators)=new Generators; (yyval.generators)->_g = *(yyvsp[0].generator_v); delete (yyvsp[0].generator_v); } + break; + + case 209: /* generator_list_head: generator */ + { (yyval.generator_v)=new std::vector; if ((yyvsp[0].generator)) (yyval.generator_v)->push_back(*(yyvsp[0].generator)); delete (yyvsp[0].generator); } + break; + + case 210: /* generator_list_head: generator_eq */ + { (yyval.generator_v)=new std::vector; if ((yyvsp[0].generator)) (yyval.generator_v)->push_back(*(yyvsp[0].generator)); delete (yyvsp[0].generator); } + break; + + case 211: /* generator_list_head: generator_eq "where" expr */ + { (yyval.generator_v)=new std::vector; + if ((yyvsp[-2].generator)) (yyval.generator_v)->push_back(*(yyvsp[-2].generator)); + if ((yyvsp[-2].generator) && (yyvsp[0].expression)) (yyval.generator_v)->push_back(Generator((yyval.generator_v)->size(),(yyvsp[0].expression))); + delete (yyvsp[-2].generator); + } + break; + + case 212: /* generator_list_head: generator_list_head ',' generator */ + { (yyval.generator_v)=(yyvsp[-2].generator_v); if ((yyval.generator_v) && (yyvsp[0].generator)) (yyval.generator_v)->push_back(*(yyvsp[0].generator)); delete (yyvsp[0].generator); } + break; + + case 213: /* generator_list_head: generator_list_head ',' generator_eq */ + { (yyval.generator_v)=(yyvsp[-2].generator_v); if ((yyval.generator_v) && (yyvsp[0].generator)) (yyval.generator_v)->push_back(*(yyvsp[0].generator)); delete (yyvsp[0].generator); } + break; + + case 214: /* generator_list_head: generator_list_head ',' generator_eq "where" expr */ + { (yyval.generator_v)=(yyvsp[-4].generator_v); + if ((yyval.generator_v) && (yyvsp[-2].generator)) (yyval.generator_v)->push_back(*(yyvsp[-2].generator)); + if ((yyval.generator_v) && (yyvsp[-2].generator) && (yyvsp[0].expression)) (yyval.generator_v)->push_back(Generator((yyval.generator_v)->size(),(yyvsp[0].expression))); + delete (yyvsp[-2].generator); + } + break; + + case 215: /* generator: id_list "in" expr */ + { if ((yyvsp[-2].string_v) && (yyvsp[0].expression)) (yyval.generator)=new Generator(*(yyvsp[-2].string_v),(yyvsp[0].expression),NULL); else (yyval.generator)=NULL; delete (yyvsp[-2].string_v); } + break; + + case 216: /* generator: id_list "in" expr "where" expr */ + { if ((yyvsp[-4].string_v) && (yyvsp[-2].expression)) (yyval.generator)=new Generator(*(yyvsp[-4].string_v),(yyvsp[-2].expression),(yyvsp[0].expression)); else (yyval.generator)=NULL; delete (yyvsp[-4].string_v); } + break; + + case 217: /* generator_eq: "identifier" "=" expr */ + { if ((yyvsp[0].expression)) (yyval.generator)=new Generator({(yyvsp[-2].sValue)},NULL,(yyvsp[0].expression)); else (yyval.generator)=NULL; free((yyvsp[-2].sValue)); } + break; + + case 219: /* id_list_head: "identifier" */ + { (yyval.string_v)=new std::vector; (yyval.string_v)->push_back((yyvsp[0].sValue)); free((yyvsp[0].sValue)); } + break; + + case 220: /* id_list_head: id_list_head ',' "identifier" */ + { (yyval.string_v)=(yyvsp[-2].string_v); if ((yyval.string_v) && (yyvsp[0].sValue)) (yyval.string_v)->push_back((yyvsp[0].sValue)); free((yyvsp[0].sValue)); } + break; + + case 221: /* simple_array_literal: "[" "]" */ + { (yyval.expression)=new ArrayLit((yyloc), std::vector()); } + break; + + case 222: /* simple_array_literal: "[" expr_list "]" */ + { if ((yyvsp[-1].expression_v)) (yyval.expression)=new ArrayLit((yyloc), *(yyvsp[-1].expression_v)); delete (yyvsp[-1].expression_v); } + break; + + case 223: /* simple_array_literal_2d: "[|" "|]" */ + { (yyval.expression)=new ArrayLit((yyloc), std::vector >()); } + break; + + case 224: /* simple_array_literal_2d: "[|" simple_array_literal_2d_list "|]" */ + { if ((yyvsp[-1].expression_vv)) { + (yyval.expression)=new ArrayLit((yyloc), *(yyvsp[-1].expression_vv)); + for (unsigned int i=1; i<(yyvsp[-1].expression_vv)->size(); i++) + if ((*(yyvsp[-1].expression_vv))[i].size() != (*(yyvsp[-1].expression_vv))[i-1].size()) + yyerror(&(yylsp[-1]), parm, "syntax error, all sub-arrays of 2d array literal must have the same length"); + delete (yyvsp[-1].expression_vv); + } else { + (yyval.expression) = NULL; + } + } + break; + + case 225: /* simple_array_literal_2d: "[|" simple_array_literal_2d_list '|' "|]" */ + { if ((yyvsp[-2].expression_vv)) { + (yyval.expression)=new ArrayLit((yyloc), *(yyvsp[-2].expression_vv)); + for (unsigned int i=1; i<(yyvsp[-2].expression_vv)->size(); i++) + if ((*(yyvsp[-2].expression_vv))[i].size() != (*(yyvsp[-2].expression_vv))[i-1].size()) + yyerror(&(yylsp[-2]), parm, "syntax error, all sub-arrays of 2d array literal must have the same length"); + delete (yyvsp[-2].expression_vv); + } else { + (yyval.expression) = NULL; + } + } + break; + + case 226: /* simple_array_literal_2d: "[|" simple_array_literal_3d_list "|]" */ + { + if ((yyvsp[-1].expression_vvv)) { + std::vector > dims(3); + dims[0] = std::pair(1,static_cast((yyvsp[-1].expression_vvv)->size())); + if ((yyvsp[-1].expression_vvv)->size()==0) { + dims[1] = std::pair(1,0); + dims[2] = std::pair(1,0); + } else { + dims[1] = std::pair(1,static_cast((*(yyvsp[-1].expression_vvv))[0].size())); + if ((*(yyvsp[-1].expression_vvv))[0].size()==0) { + dims[2] = std::pair(1,0); + } else { + dims[2] = std::pair(1,static_cast((*(yyvsp[-1].expression_vvv))[0][0].size())); + } + } + std::vector a; + for (int i=0; i > >; + } + break; + + case 228: /* simple_array_literal_3d_list: '|' simple_array_literal_2d_list '|' */ + { (yyval.expression_vvv)=new std::vector > >; + if ((yyvsp[-1].expression_vv)) (yyval.expression_vvv)->push_back(*(yyvsp[-1].expression_vv)); + delete (yyvsp[-1].expression_vv); + } + break; + + case 229: /* simple_array_literal_3d_list: simple_array_literal_3d_list ',' '|' simple_array_literal_2d_list '|' */ + { (yyval.expression_vvv)=(yyvsp[-4].expression_vvv); + if ((yyval.expression_vvv) && (yyvsp[-1].expression_vv)) (yyval.expression_vvv)->push_back(*(yyvsp[-1].expression_vv)); + delete (yyvsp[-1].expression_vv); + } + break; + + case 230: /* simple_array_literal_2d_list: expr_list */ + { (yyval.expression_vv)=new std::vector >; + if ((yyvsp[0].expression_v)) (yyval.expression_vv)->push_back(*(yyvsp[0].expression_v)); + delete (yyvsp[0].expression_v); + } + break; + + case 231: /* simple_array_literal_2d_list: simple_array_literal_2d_list '|' expr_list */ + { (yyval.expression_vv)=(yyvsp[-2].expression_vv); if ((yyval.expression_vv) && (yyvsp[0].expression_v)) (yyval.expression_vv)->push_back(*(yyvsp[0].expression_v)); delete (yyvsp[0].expression_v); } + break; + + case 232: /* simple_array_comp: "[" expr '|' comp_tail "]" */ + { if ((yyvsp[-1].generators)) (yyval.expression)=new Comprehension((yyloc), (yyvsp[-3].expression), *(yyvsp[-1].generators), false); + delete (yyvsp[-1].generators); + } + break; + + case 233: /* if_then_else_expr: "if" expr "then" expr "endif" */ + { + std::vector iexps; + iexps.push_back((yyvsp[-3].expression)); + iexps.push_back((yyvsp[-1].expression)); + (yyval.expression)=new ITE((yyloc), iexps, NULL); + } + break; + + case 234: /* if_then_else_expr: "if" expr "then" expr elseif_list "else" expr "endif" */ + { + std::vector iexps; + iexps.push_back((yyvsp[-6].expression)); + iexps.push_back((yyvsp[-4].expression)); + if ((yyvsp[-3].expression_v)) { + for (unsigned int i=0; i<(yyvsp[-3].expression_v)->size(); i+=2) { + iexps.push_back((*(yyvsp[-3].expression_v))[i]); + iexps.push_back((*(yyvsp[-3].expression_v))[i+1]); + } + } + (yyval.expression)=new ITE((yyloc), iexps,(yyvsp[-1].expression)); + delete (yyvsp[-3].expression_v); + } + break; + + case 235: /* elseif_list: %empty */ + { (yyval.expression_v)=new std::vector; } + break; + + case 236: /* elseif_list: elseif_list "elseif" expr "then" expr */ + { (yyval.expression_v)=(yyvsp[-4].expression_v); if ((yyval.expression_v) && (yyvsp[-2].expression) && (yyvsp[0].expression)) { (yyval.expression_v)->push_back((yyvsp[-2].expression)); (yyval.expression_v)->push_back((yyvsp[0].expression)); } } + break; + + case 237: /* quoted_op: "'<->'" */ + { (yyval.iValue)=BOT_EQUIV; } + break; + + case 238: /* quoted_op: "'->'" */ + { (yyval.iValue)=BOT_IMPL; } + break; + + case 239: /* quoted_op: "'<-'" */ + { (yyval.iValue)=BOT_RIMPL; } + break; + + case 240: /* quoted_op: "'\\/'" */ + { (yyval.iValue)=BOT_OR; } + break; + + case 241: /* quoted_op: "'xor'" */ + { (yyval.iValue)=BOT_XOR; } + break; + + case 242: /* quoted_op: "'/\\'" */ + { (yyval.iValue)=BOT_AND; } + break; + + case 243: /* quoted_op: "'<'" */ + { (yyval.iValue)=BOT_LE; } + break; + + case 244: /* quoted_op: "'>'" */ + { (yyval.iValue)=BOT_GR; } + break; + + case 245: /* quoted_op: "'<='" */ + { (yyval.iValue)=BOT_LQ; } + break; + + case 246: /* quoted_op: "'>='" */ + { (yyval.iValue)=BOT_GQ; } + break; + + case 247: /* quoted_op: "'='" */ + { (yyval.iValue)=BOT_EQ; } + break; + + case 248: /* quoted_op: "'!='" */ + { (yyval.iValue)=BOT_NQ; } + break; + + case 249: /* quoted_op: "'in'" */ + { (yyval.iValue)=BOT_IN; } + break; + + case 250: /* quoted_op: "'subset'" */ + { (yyval.iValue)=BOT_SUBSET; } + break; + + case 251: /* quoted_op: "'superset'" */ + { (yyval.iValue)=BOT_SUPERSET; } + break; + + case 252: /* quoted_op: "'union'" */ + { (yyval.iValue)=BOT_UNION; } + break; + + case 253: /* quoted_op: "'diff'" */ + { (yyval.iValue)=BOT_DIFF; } + break; + + case 254: /* quoted_op: "'symdiff'" */ + { (yyval.iValue)=BOT_SYMDIFF; } + break; + + case 255: /* quoted_op: "'+'" */ + { (yyval.iValue)=BOT_PLUS; } + break; + + case 256: /* quoted_op: "'-'" */ + { (yyval.iValue)=BOT_MINUS; } + break; + + case 257: /* quoted_op: "'*'" */ + { (yyval.iValue)=BOT_MULT; } + break; + + case 258: /* quoted_op: "'^'" */ + { (yyval.iValue)=BOT_POW; } + break; + + case 259: /* quoted_op: "'/'" */ + { (yyval.iValue)=BOT_DIV; } + break; + + case 260: /* quoted_op: "'div'" */ + { (yyval.iValue)=BOT_IDIV; } + break; + + case 261: /* quoted_op: "'mod'" */ + { (yyval.iValue)=BOT_MOD; } + break; + + case 262: /* quoted_op: "'intersect'" */ + { (yyval.iValue)=BOT_INTERSECT; } + break; + + case 263: /* quoted_op: "'++'" */ + { (yyval.iValue)=BOT_PLUSPLUS; } + break; + + case 264: /* quoted_op: "'not'" */ + { (yyval.iValue)=-1; } + break; + + case 265: /* quoted_op_call: quoted_op '(' expr ',' expr ')' */ + { if ((yyvsp[-5].iValue)==-1) { + (yyval.expression)=NULL; + yyerror(&(yylsp[-3]), parm, "syntax error, unary operator with two arguments"); + } else { + (yyval.expression)=new BinOp((yyloc), (yyvsp[-3].expression),static_cast((yyvsp[-5].iValue)),(yyvsp[-1].expression)); + } + } + break; + + case 266: /* quoted_op_call: quoted_op '(' expr ')' */ + { int uot=-1; + switch ((yyvsp[-3].iValue)) { + case -1: + uot = UOT_NOT; + break; + case BOT_MINUS: + uot = UOT_MINUS; + break; + case BOT_PLUS: + uot = UOT_PLUS; + break; + default: + yyerror(&(yylsp[-1]), parm, "syntax error, binary operator with unary argument list"); + break; + } + if (uot==-1) + (yyval.expression)=NULL; + else { + if (uot==UOT_PLUS && (yyvsp[-1].expression) && ((yyvsp[-1].expression)->isa() || (yyvsp[-1].expression)->isa())) { + (yyval.expression) = (yyvsp[-1].expression); + } else if (uot==UOT_MINUS && (yyvsp[-1].expression) && (yyvsp[-1].expression)->isa()) { + (yyval.expression) = IntLit::a(-(yyvsp[-1].expression)->cast()->v()); + } else if (uot==UOT_MINUS && (yyvsp[-1].expression) && (yyvsp[-1].expression)->isa()) { + (yyval.expression) = FloatLit::a(-(yyvsp[-1].expression)->cast()->v()); + } else { + (yyval.expression)=new UnOp((yyloc), static_cast(uot),(yyvsp[-1].expression)); + } + } + } + break; + + case 267: /* call_expr: "identifier" '(' ')' */ + { (yyval.expression)=new Call((yyloc), (yyvsp[-2].sValue), std::vector()); free((yyvsp[-2].sValue)); } + break; + + case 269: /* call_expr: "identifier" '(' comp_or_expr ')' */ + { + if ((yyvsp[-1].expression_p)!=NULL) { + bool hadWhere = false; + std::vector args; + for (unsigned int i=0; i<(yyvsp[-1].expression_p)->size(); i++) { + if ((*(yyvsp[-1].expression_p))[i].second) { + yyerror(&(yylsp[-1]), parm, "syntax error, 'where' expression outside generator call"); + hadWhere = true; + (yyval.expression)=NULL; + } + args.push_back((*(yyvsp[-1].expression_p))[i].first); + } + if (!hadWhere) { + (yyval.expression)=new Call((yyloc), (yyvsp[-3].sValue), args); + } + } + free((yyvsp[-3].sValue)); + delete (yyvsp[-1].expression_p); + } + break; + + case 270: /* call_expr: "identifier" '(' comp_or_expr ')' '(' expr ')' */ + { + vector gens; + vector ids; + if ((yyvsp[-4].expression_p)) { + for (unsigned int i=0; i<(yyvsp[-4].expression_p)->size(); i++) { + if (Id* id = Expression::dyn_cast((*(yyvsp[-4].expression_p))[i].first)) { + if ((*(yyvsp[-4].expression_p))[i].second) { + ParserLocation loc = (*(yyvsp[-4].expression_p))[i].second->loc().parserLocation(); + yyerror(&loc, parm, "illegal where expression in generator call"); + } + ids.push_back(id); + } else { + if (BinOp* boe = Expression::dyn_cast((*(yyvsp[-4].expression_p))[i].first)) { + if (boe->lhs() && boe->rhs()) { + Id* id = Expression::dyn_cast(boe->lhs()); + if (id && boe->op() == BOT_IN) { + ids.push_back(id); + gens.push_back(Generator(ids,boe->rhs(),(*(yyvsp[-4].expression_p))[i].second)); + ids = vector(); + } else if (id && boe->op() == BOT_EQ && ids.empty()) { + ids.push_back(id); + gens.push_back(Generator(ids,NULL,boe->rhs())); + if ((*(yyvsp[-4].expression_p))[i].second) { + gens.push_back(Generator(gens.size(),(*(yyvsp[-4].expression_p))[i].second)); + } + ids = vector(); + } else { + ParserLocation loc = (*(yyvsp[-4].expression_p))[i].first->loc().parserLocation(); + yyerror(&loc, parm, "illegal expression in generator call"); + } + } + } else { + ParserLocation loc = (*(yyvsp[-4].expression_p))[i].first->loc().parserLocation(); + yyerror(&loc, parm, "illegal expression in generator call"); + } + } + } + } + if (ids.size() != 0) { + yyerror(&(yylsp[-4]), parm, "illegal expression in generator call"); + } + ParserState* pp = static_cast(parm); + if (pp->hadError) { + (yyval.expression)=NULL; + } else { + Generators g; g._g = gens; + Comprehension* ac = new Comprehension((yyloc), (yyvsp[-1].expression),g,false); + vector args; args.push_back(ac); + (yyval.expression)=new Call((yyloc), (yyvsp[-6].sValue), args); + } + free((yyvsp[-6].sValue)); + delete (yyvsp[-4].expression_p); + } + break; + + case 272: /* comp_or_expr_head: expr */ + { (yyval.expression_p)=new vector >; + if ((yyvsp[0].expression)) { + (yyval.expression_p)->push_back(pair((yyvsp[0].expression),NULL)); + } + } + break; + + case 273: /* comp_or_expr_head: expr "where" expr */ + { (yyval.expression_p)=new vector >; + if ((yyvsp[-2].expression) && (yyvsp[0].expression)) { + (yyval.expression_p)->push_back(pair((yyvsp[-2].expression),(yyvsp[0].expression))); + } + } + break; + + case 274: /* comp_or_expr_head: comp_or_expr_head ',' expr */ + { (yyval.expression_p)=(yyvsp[-2].expression_p); if ((yyval.expression_p) && (yyvsp[0].expression)) (yyval.expression_p)->push_back(pair((yyvsp[0].expression),NULL)); } + break; + + case 275: /* comp_or_expr_head: comp_or_expr_head ',' expr "where" expr */ + { (yyval.expression_p)=(yyvsp[-4].expression_p); if ((yyval.expression_p) && (yyvsp[-2].expression) && (yyvsp[0].expression)) (yyval.expression_p)->push_back(pair((yyvsp[-2].expression),(yyvsp[0].expression))); } + break; + + case 276: /* let_expr: "let" '{' let_vardecl_item_list '}' "in" expr */ + { if ((yyvsp[-3].expression_v) && (yyvsp[0].expression)) { + (yyval.expression)=new Let((yyloc), *(yyvsp[-3].expression_v), (yyvsp[0].expression)); delete (yyvsp[-3].expression_v); + } else { + (yyval.expression)=NULL; + } + } + break; + + case 277: /* let_expr: "let" '{' let_vardecl_item_list comma_or_semi '}' "in" expr */ + { if ((yyvsp[-4].expression_v) && (yyvsp[0].expression)) { + (yyval.expression)=new Let((yyloc), *(yyvsp[-4].expression_v), (yyvsp[0].expression)); delete (yyvsp[-4].expression_v); + } else { + (yyval.expression)=NULL; + } + } + break; + + case 278: /* let_vardecl_item_list: let_vardecl_item */ + { (yyval.expression_v)=new vector; (yyval.expression_v)->push_back((yyvsp[0].vardeclexpr)); } + break; + + case 279: /* let_vardecl_item_list: constraint_item */ + { (yyval.expression_v)=new vector; + if ((yyvsp[0].item)) { + ConstraintI* ce = (yyvsp[0].item)->cast(); + (yyval.expression_v)->push_back(ce->e()); + ce->e(NULL); + } + } + break; + + case 280: /* let_vardecl_item_list: let_vardecl_item_list comma_or_semi let_vardecl_item */ + { (yyval.expression_v)=(yyvsp[-2].expression_v); if ((yyval.expression_v) && (yyvsp[0].vardeclexpr)) (yyval.expression_v)->push_back((yyvsp[0].vardeclexpr)); } + break; + + case 281: /* let_vardecl_item_list: let_vardecl_item_list comma_or_semi constraint_item */ + { (yyval.expression_v)=(yyvsp[-2].expression_v); + if ((yyval.expression_v) && (yyvsp[0].item)) { + ConstraintI* ce = (yyvsp[0].item)->cast(); + (yyval.expression_v)->push_back(ce->e()); + ce->e(NULL); + } + } + break; + + case 284: /* let_vardecl_item: ti_expr_and_id annotations */ + { (yyval.vardeclexpr) = (yyvsp[-1].vardeclexpr); + if ((yyval.vardeclexpr)) (yyval.vardeclexpr)->toplevel(false); + if ((yyval.vardeclexpr) && (yyvsp[0].expression_v)) (yyval.vardeclexpr)->addAnnotations(*(yyvsp[0].expression_v)); + delete (yyvsp[0].expression_v); + } + break; + + case 285: /* let_vardecl_item: ti_expr_and_id annotations "=" expr */ + { if ((yyvsp[-3].vardeclexpr)) (yyvsp[-3].vardeclexpr)->e((yyvsp[0].expression)); + (yyval.vardeclexpr) = (yyvsp[-3].vardeclexpr); + if ((yyval.vardeclexpr)) (yyval.vardeclexpr)->loc((yyloc)); + if ((yyval.vardeclexpr)) (yyval.vardeclexpr)->toplevel(false); + if ((yyval.vardeclexpr) && (yyvsp[-2].expression_v)) (yyval.vardeclexpr)->addAnnotations(*(yyvsp[-2].expression_v)); + delete (yyvsp[-2].expression_v); + } + break; + + case 286: /* annotations: %empty */ + { (yyval.expression_v)=NULL; } + break; + + case 288: /* annotation_expr: expr_atom_head_nonstring */ + { (yyval.expression) = (yyvsp[0].expression); } + break; + + case 289: /* annotation_expr: string_expr */ + { (yyval.expression) = new Call((yylsp[0]), ASTString("mzn_expression_name"), {(yyvsp[0].expression)}); } + break; + + case 290: /* ne_annotations: "::" annotation_expr */ + { (yyval.expression_v)=new std::vector(1); + (*(yyval.expression_v))[0] = (yyvsp[0].expression); + } + break; + + case 291: /* ne_annotations: ne_annotations "::" annotation_expr */ + { (yyval.expression_v)=(yyvsp[-2].expression_v); if ((yyval.expression_v)) (yyval.expression_v)->push_back((yyvsp[0].expression)); } + break; + + case 292: /* id_or_quoted_op: "identifier" */ + { (yyval.sValue)=(yyvsp[0].sValue); } + break; + + case 293: /* id_or_quoted_op: "'<->'" */ + { (yyval.sValue)=strdup("'<->'"); } + break; + + case 294: /* id_or_quoted_op: "'->'" */ + { (yyval.sValue)=strdup("'->'"); } + break; + + case 295: /* id_or_quoted_op: "'<-'" */ + { (yyval.sValue)=strdup("'<-'"); } + break; + + case 296: /* id_or_quoted_op: "'\\/'" */ + { (yyval.sValue)=strdup("'\\/'"); } + break; + + case 297: /* id_or_quoted_op: "'xor'" */ + { (yyval.sValue)=strdup("'xor'"); } + break; + + case 298: /* id_or_quoted_op: "'/\\'" */ + { (yyval.sValue)=strdup("'/\\'"); } + break; + + case 299: /* id_or_quoted_op: "'<'" */ + { (yyval.sValue)=strdup("'<'"); } + break; + + case 300: /* id_or_quoted_op: "'>'" */ + { (yyval.sValue)=strdup("'>'"); } + break; + + case 301: /* id_or_quoted_op: "'<='" */ + { (yyval.sValue)=strdup("'<='"); } + break; + + case 302: /* id_or_quoted_op: "'>='" */ + { (yyval.sValue)=strdup("'>='"); } + break; + + case 303: /* id_or_quoted_op: "'='" */ + { (yyval.sValue)=strdup("'='"); } + break; + + case 304: /* id_or_quoted_op: "'!='" */ + { (yyval.sValue)=strdup("'!='"); } + break; + + case 305: /* id_or_quoted_op: "'in'" */ + { (yyval.sValue)=strdup("'in'"); } + break; + + case 306: /* id_or_quoted_op: "'subset'" */ + { (yyval.sValue)=strdup("'subset'"); } + break; + + case 307: /* id_or_quoted_op: "'superset'" */ + { (yyval.sValue)=strdup("'superset'"); } + break; + + case 308: /* id_or_quoted_op: "'union'" */ + { (yyval.sValue)=strdup("'union'"); } + break; + + case 309: /* id_or_quoted_op: "'diff'" */ + { (yyval.sValue)=strdup("'diff'"); } + break; + + case 310: /* id_or_quoted_op: "'symdiff'" */ + { (yyval.sValue)=strdup("'symdiff'"); } + break; + + case 311: /* id_or_quoted_op: "'..'" */ + { (yyval.sValue)=strdup("'..'"); } + break; + + case 312: /* id_or_quoted_op: "'+'" */ + { (yyval.sValue)=strdup("'+'"); } + break; + + case 313: /* id_or_quoted_op: "'-'" */ + { (yyval.sValue)=strdup("'-'"); } + break; + + case 314: /* id_or_quoted_op: "'*'" */ + { (yyval.sValue)=strdup("'*'"); } + break; + + case 315: /* id_or_quoted_op: "'^'" */ + { (yyval.sValue)=strdup("'^'"); } + break; + + case 316: /* id_or_quoted_op: "'/'" */ + { (yyval.sValue)=strdup("'/'"); } + break; + + case 317: /* id_or_quoted_op: "'div'" */ + { (yyval.sValue)=strdup("'div'"); } + break; + + case 318: /* id_or_quoted_op: "'mod'" */ + { (yyval.sValue)=strdup("'mod'"); } + break; + + case 319: /* id_or_quoted_op: "'intersect'" */ + { (yyval.sValue)=strdup("'intersect'"); } + break; + + case 320: /* id_or_quoted_op: "'not'" */ + { (yyval.sValue)=strdup("'not'"); } + break; + + case 321: /* id_or_quoted_op: "'++'" */ + { (yyval.sValue)=strdup("'++'"); } + break; + + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + *++yylsp = yyloc; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken, &yylloc}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (&yylloc, parm, yymsgp); + if (yysyntax_error_status == YYENOMEM) + goto yyexhaustedlab; + } + } + + yyerror_range[1] = yylloc; + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= END) + { + /* Return failure if at end of input. */ + if (yychar == END) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, parm); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp, parm); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + ++yylsp; + YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if 1 +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, parm, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, parm); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, parm); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + diff --git a/lib/cached/regex_lexer.yy.cpp b/lib/cached/regex_lexer.yy.cpp new file mode 100644 index 0000000..f8f5f38 --- /dev/null +++ b/lib/cached/regex_lexer.yy.cpp @@ -0,0 +1,2089 @@ + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define yy_create_buffer regex_yy_create_buffer +#define yy_delete_buffer regex_yy_delete_buffer +#define yy_scan_buffer regex_yy_scan_buffer +#define yy_scan_string regex_yy_scan_string +#define yy_scan_bytes regex_yy_scan_bytes +#define yy_init_buffer regex_yy_init_buffer +#define yy_flush_buffer regex_yy_flush_buffer +#define yy_load_buffer_state regex_yy_load_buffer_state +#define yy_switch_to_buffer regex_yy_switch_to_buffer +#define yypush_buffer_state regex_yypush_buffer_state +#define yypop_buffer_state regex_yypop_buffer_state +#define yyensure_buffer_stack regex_yyensure_buffer_stack +#define yy_flex_debug regex_yy_flex_debug +#define yyin regex_yyin +#define yyleng regex_yyleng +#define yylex regex_yylex +#define yylineno regex_yylineno +#define yyout regex_yyout +#define yyrestart regex_yyrestart +#define yytext regex_yytext +#define yywrap regex_yywrap +#define yyalloc regex_yyalloc +#define yyrealloc regex_yyrealloc +#define yyfree regex_yyfree + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +#ifdef yy_create_buffer +#define regex_yy_create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer regex_yy_create_buffer +#endif + +#ifdef yy_delete_buffer +#define regex_yy_delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer regex_yy_delete_buffer +#endif + +#ifdef yy_scan_buffer +#define regex_yy_scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer regex_yy_scan_buffer +#endif + +#ifdef yy_scan_string +#define regex_yy_scan_string_ALREADY_DEFINED +#else +#define yy_scan_string regex_yy_scan_string +#endif + +#ifdef yy_scan_bytes +#define regex_yy_scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes regex_yy_scan_bytes +#endif + +#ifdef yy_init_buffer +#define regex_yy_init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer regex_yy_init_buffer +#endif + +#ifdef yy_flush_buffer +#define regex_yy_flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer regex_yy_flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define regex_yy_load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state regex_yy_load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define regex_yy_switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer regex_yy_switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define regex_yypush_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state regex_yypush_buffer_state +#endif + +#ifdef yypop_buffer_state +#define regex_yypop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state regex_yypop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define regex_yyensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack regex_yyensure_buffer_stack +#endif + +#ifdef yylex +#define regex_yylex_ALREADY_DEFINED +#else +#define yylex regex_yylex +#endif + +#ifdef yyrestart +#define regex_yyrestart_ALREADY_DEFINED +#else +#define yyrestart regex_yyrestart +#endif + +#ifdef yylex_init +#define regex_yylex_init_ALREADY_DEFINED +#else +#define yylex_init regex_yylex_init +#endif + +#ifdef yylex_init_extra +#define regex_yylex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra regex_yylex_init_extra +#endif + +#ifdef yylex_destroy +#define regex_yylex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy regex_yylex_destroy +#endif + +#ifdef yyget_debug +#define regex_yyget_debug_ALREADY_DEFINED +#else +#define yyget_debug regex_yyget_debug +#endif + +#ifdef yyset_debug +#define regex_yyset_debug_ALREADY_DEFINED +#else +#define yyset_debug regex_yyset_debug +#endif + +#ifdef yyget_extra +#define regex_yyget_extra_ALREADY_DEFINED +#else +#define yyget_extra regex_yyget_extra +#endif + +#ifdef yyset_extra +#define regex_yyset_extra_ALREADY_DEFINED +#else +#define yyset_extra regex_yyset_extra +#endif + +#ifdef yyget_in +#define regex_yyget_in_ALREADY_DEFINED +#else +#define yyget_in regex_yyget_in +#endif + +#ifdef yyset_in +#define regex_yyset_in_ALREADY_DEFINED +#else +#define yyset_in regex_yyset_in +#endif + +#ifdef yyget_out +#define regex_yyget_out_ALREADY_DEFINED +#else +#define yyget_out regex_yyget_out +#endif + +#ifdef yyset_out +#define regex_yyset_out_ALREADY_DEFINED +#else +#define yyset_out regex_yyset_out +#endif + +#ifdef yyget_leng +#define regex_yyget_leng_ALREADY_DEFINED +#else +#define yyget_leng regex_yyget_leng +#endif + +#ifdef yyget_text +#define regex_yyget_text_ALREADY_DEFINED +#else +#define yyget_text regex_yyget_text +#endif + +#ifdef yyget_lineno +#define regex_yyget_lineno_ALREADY_DEFINED +#else +#define yyget_lineno regex_yyget_lineno +#endif + +#ifdef yyset_lineno +#define regex_yyset_lineno_ALREADY_DEFINED +#else +#define yyset_lineno regex_yyset_lineno +#endif + +#ifdef yywrap +#define regex_yywrap_ALREADY_DEFINED +#else +#define yywrap regex_yywrap +#endif + +#ifdef yyalloc +#define regex_yyalloc_ALREADY_DEFINED +#else +#define yyalloc regex_yyalloc +#endif + +#ifdef yyrealloc +#define regex_yyrealloc_ALREADY_DEFINED +#else +#define yyrealloc regex_yyrealloc +#endif + +#ifdef yyfree +#define regex_yyfree_ALREADY_DEFINED +#else +#define yyfree regex_yyfree +#endif + +#ifdef yytext +#define regex_yytext_ALREADY_DEFINED +#else +#define yytext regex_yytext +#endif + +#ifdef yyleng +#define regex_yyleng_ALREADY_DEFINED +#else +#define yyleng regex_yyleng +#endif + +#ifdef yyin +#define regex_yyin_ALREADY_DEFINED +#else +#define yyin regex_yyin +#endif + +#ifdef yyout +#define regex_yyout_ALREADY_DEFINED +#else +#define yyout regex_yyout +#endif + +#ifdef yy_flex_debug +#define regex_yy_flex_debug_ALREADY_DEFINED +#else +#define yy_flex_debug regex_yy_flex_debug +#endif + +#ifdef yylineno +#define regex_yylineno_ALREADY_DEFINED +#else +#define yylineno regex_yylineno +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* begin standard C++ headers. */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +extern int yyleng; + +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + #define YY_LINENO_REWIND_TO(ptr) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, (yytext_ptr) ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = NULL; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart ( FILE *input_file ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size ); +void yy_delete_buffer ( YY_BUFFER_STATE b ); +void yy_flush_buffer ( YY_BUFFER_STATE b ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer ); +void yypop_buffer_state ( void ); + +static void yyensure_buffer_stack ( void ); +static void yy_load_buffer_state ( void ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len ); + +void *yyalloc ( yy_size_t ); +void *yyrealloc ( void *, yy_size_t ); +void yyfree ( void * ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define regex_yywrap() (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP +typedef flex_uint8_t YY_CHAR; + +FILE *yyin = NULL, *yyout = NULL; + +typedef int yy_state_type; + +extern int yylineno; +int yylineno = 1; + +extern char *yytext; +#ifdef yytext_ptr +#undef yytext_ptr +#endif +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state ( void ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state ); +static int yy_get_next_buffer ( void ); +static void yynoreturn yy_fatal_error ( const char* msg ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; +#define YY_NUM_RULES 20 +#define YY_END_OF_BUFFER 21 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[29] = + { 0, + 0, 0, 21, 19, 1, 1, 19, 6, 7, 5, + 4, 11, 15, 12, 2, 8, 17, 13, 14, 16, + 9, 3, 10, 0, 18, 2, 17, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 5, 6, + 7, 8, 9, 10, 11, 12, 1, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, + 1, 1, 14, 1, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 16, 4, 17, 18, 19, 1, 15, 15, 15, 15, + + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 20, 21, 22, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[23] = + { 0, + 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1 + } ; + +static const flex_int16_t yy_base[30] = + { 0, + 0, 0, 34, 35, 35, 35, 28, 35, 35, 35, + 35, 35, 35, 35, 19, 35, 10, 35, 35, 35, + 35, 35, 35, 26, 35, 15, 11, 35, 26 + } ; + +static const flex_int16_t yy_def[30] = + { 0, + 28, 1, 28, 28, 28, 28, 29, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 29, 28, 28, 28, 0, 28 + } ; + +static const flex_int16_t yy_nxt[58] = + { 0, + 4, 5, 6, 4, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 4, 21, + 22, 23, 27, 27, 27, 27, 24, 26, 27, 27, + 25, 26, 25, 28, 3, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28 + } ; + +static const flex_int16_t yy_chk[58] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 17, 27, 17, 27, 29, 26, 17, 27, + 24, 15, 7, 3, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int yy_flex_debug; +int yy_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ +/* + * Main authors: + * Jip J. Dekker + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#define YY_DECL int yylex() + +#include + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals ( void ); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( void ); + +int yyget_debug ( void ); + +void yyset_debug ( int debug_flag ); + +YY_EXTRA_TYPE yyget_extra ( void ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined ); + +FILE *yyget_in ( void ); + +void yyset_in ( FILE * _in_str ); + +FILE *yyget_out ( void ); + +void yyset_out ( FILE * _out_str ); + + int yyget_leng ( void ); + +char *yyget_text ( void ); + +int yyget_lineno ( void ); + +void yyset_lineno ( int _line_number ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( void ); +#else +extern int yywrap ( void ); +#endif +#endif + +#ifndef YY_NO_UNPUT + + static void yyunput ( int c, char *buf_ptr ); + +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * ); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput ( void ); +#else +static int input ( void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int yylex (void); + +#define YY_DECL int yylex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_load_buffer_state( ); + } + + { + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of yytext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 35 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +/* rule 1 can match eol */ +YY_RULE_SETUP +{ /* ignore white space */ } + YY_BREAK +case 2: +YY_RULE_SETUP +{ regex_yylval.iValue = std::atoi(regex_yytext); return R_INTEGER; } + YY_BREAK +case 3: +YY_RULE_SETUP +{ return R_UNION; } + YY_BREAK +case 4: +YY_RULE_SETUP +{ return R_PLUS; } + YY_BREAK +case 5: +YY_RULE_SETUP +{ return R_STAR; } + YY_BREAK +case 6: +YY_RULE_SETUP +{ return R_GROUP_OPEN; } + YY_BREAK +case 7: +YY_RULE_SETUP +{ return R_GROUP_CLOSE; } + YY_BREAK +case 8: +YY_RULE_SETUP +{ return R_OPTIONAL; } + YY_BREAK +case 9: +YY_RULE_SETUP +{ return R_QUANT_OPEN; } + YY_BREAK +case 10: +YY_RULE_SETUP +{ return R_QUANT_CLOSE; } + YY_BREAK +case 11: +YY_RULE_SETUP +{ return R_COMMA; } + YY_BREAK +case 12: +YY_RULE_SETUP +{ return R_ANY; } + YY_BREAK +case 13: +YY_RULE_SETUP +{ return R_CLASS_OPEN; } + YY_BREAK +case 14: +YY_RULE_SETUP +{ return R_CLASS_CLOSE; } + YY_BREAK +case 15: +YY_RULE_SETUP +{ return R_CLASS_RANGE; } + YY_BREAK +case 16: +YY_RULE_SETUP +{ return R_CLASS_NEG; } + YY_BREAK +case 17: +YY_RULE_SETUP +{ regex_yylval.sValue = strdup(yytext); return R_IDENTIFIER; } + YY_BREAK +case 18: +YY_RULE_SETUP +{ regex_yylval.sValue = strdup(yytext); return R_IDENTIFIER; } + YY_BREAK +case 19: +YY_RULE_SETUP +{ + /* Catch all */ + throw std::runtime_error("Illegal token in regular expression: '" + std::string(regex_yytext) + "'"); +} + YY_BREAK +case 20: +YY_RULE_SETUP +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( yywrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = (yytext_ptr); + int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + yy_state_type yy_current_state; + char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 4); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + int yy_is_jam; + char *yy_cp = (yy_c_buf_p); + + YY_CHAR yy_c = 4; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 29 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 28); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT + + static void yyunput (int c, char * yy_bp ) +{ + char *yy_cp; + + yy_cp = (yy_c_buf_p); + + /* undo effects of setting up yytext */ + *yy_cp = (yy_hold_char); + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + int number_to_move = (yy_n_chars) + 2; + char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + (yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + (yytext_ptr) = yy_bp; + (yy_hold_char) = *yy_cp; + (yy_c_buf_p) = yy_cp; +} + +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (int) ((yy_c_buf_p) - (yytext_ptr)); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve yytext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void yyrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE ); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file ); + yy_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void yy_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * + */ + void yy_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf ); + + yyfree( (void *) b ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void yy_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void yypop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void yyensure_buffer_stack (void) +{ + yy_size_t num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + if ( ! (yy_buffer_stack) ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr ) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yynoreturn yy_fatal_error (const char* msg ) +{ + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = (yy_hold_char); \ + (yy_c_buf_p) = yytext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int yyget_lineno (void) +{ + + return yylineno; +} + +/** Get the input stream. + * + */ +FILE *yyget_in (void) +{ + return yyin; +} + +/** Get the output stream. + * + */ +FILE *yyget_out (void) +{ + return yyout; +} + +/** Get the length of the current token. + * + */ +int yyget_leng (void) +{ + return yyleng; +} + +/** Get the current token. + * + */ + +char *yyget_text (void) +{ + return yytext; +} + +/** Set the current line number. + * @param _line_number line number + * + */ +void yyset_lineno (int _line_number ) +{ + + yylineno = _line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str ) +{ + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str ) +{ + yyout = _out_str ; +} + +int yyget_debug (void) +{ + return yy_flex_debug; +} + +void yyset_debug (int _bdebug ) +{ + yy_flex_debug = _bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = NULL; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = NULL; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} + +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(); + } + + /* Destroy the stack itself. */ + yyfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n ) +{ + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s ) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size ) +{ + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size ) +{ + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr ) +{ + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + diff --git a/lib/cached/regex_parser.tab.cpp b/lib/cached/regex_parser.tab.cpp new file mode 100644 index 0000000..9cc9dcd --- /dev/null +++ b/lib/cached/regex_parser.tab.cpp @@ -0,0 +1,1495 @@ +/* A Bison parser, made by GNU Bison 3.7.6. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30706 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.6" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse regex_yyparse +#define yylex regex_yylex +#define yyerror regex_yyerror +#define yydebug regex_yydebug +#define yynerrs regex_yynerrs +#define yylval regex_yylval +#define yychar regex_yychar + +/* First part of user prologue. */ + +#include +#include +#include +#include +#include + +#include +#include +using namespace Gecode; +using namespace MiniZinc; + +typedef struct yy_buffer_state *YY_BUFFER_STATE; +YY_BUFFER_STATE regex_yy_scan_string ( const char* yy_str ); + +extern int yylex(); +extern FILE* yyin; + +typedef struct REContext{ + REG* expr; + const IntSetVal& dom; + const std::unordered_map& idMap; +} REContext; + +void yyerror(REContext& ctx, const char* s); + + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_R_INTEGER = 3, /* R_INTEGER */ + YYSYMBOL_R_GROUP_OPEN = 4, /* "(" */ + YYSYMBOL_R_GROUP_CLOSE = 5, /* ")" */ + YYSYMBOL_R_STAR = 6, /* "*" */ + YYSYMBOL_R_PLUS = 7, /* "+" */ + YYSYMBOL_R_ANY = 8, /* "." */ + YYSYMBOL_R_UNION = 9, /* "|" */ + YYSYMBOL_R_OPTIONAL = 10, /* "?" */ + YYSYMBOL_R_QUANT_OPEN = 11, /* "{" */ + YYSYMBOL_R_QUANT_CLOSE = 12, /* "}" */ + YYSYMBOL_R_COMMA = 13, /* "," */ + YYSYMBOL_R_CLASS_OPEN = 14, /* "[" */ + YYSYMBOL_R_CLASS_CLOSE = 15, /* "]" */ + YYSYMBOL_R_CLASS_RANGE = 16, /* "-" */ + YYSYMBOL_R_CLASS_NEG = 17, /* "^" */ + YYSYMBOL_R_IDENTIFIER = 18, /* R_IDENTIFIER */ + YYSYMBOL_YYACCEPT = 19, /* $accept */ + YYSYMBOL_regex = 20, /* regex */ + YYSYMBOL_expression = 21, /* expression */ + YYSYMBOL_term = 22, /* term */ + YYSYMBOL_factor = 23, /* factor */ + YYSYMBOL_atom = 24, /* atom */ + YYSYMBOL_set_items = 25, /* set_items */ + YYSYMBOL_set_item = 26, /* set_item */ + YYSYMBOL_literal = 27 /* literal */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 17 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 34 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 19 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 9 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 24 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 38 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 273 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 75, 75, 82, 83, 91, 92, 100, 101, 106, + 111, 116, 121, 126, 133, 135, 140, 150, 165, 169, + 170, 180, 184, 198, 199 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "R_INTEGER", "\"(\"", + "\")\"", "\"*\"", "\"+\"", "\".\"", "\"|\"", "\"?\"", "\"{\"", "\"}\"", + "\",\"", "\"[\"", "\"]\"", "\"-\"", "\"^\"", "R_IDENTIFIER", "$accept", + "regex", "expression", "term", "factor", "atom", "set_items", "set_item", + "literal", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273 +}; +#endif + +#define YYPACT_NINF (-8) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 0, -8, 0, -8, 2, -8, 13, -8, -7, 0, + 16, -8, 10, 3, 18, 3, 9, -8, 0, -8, + -8, -8, -8, 25, -8, 19, -8, -8, 3, -8, + 17, -8, -8, -8, 4, 20, -8, -8 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 0, 23, 0, 15, 0, 24, 0, 2, 3, 5, + 7, 14, 0, 0, 0, 19, 21, 1, 0, 6, + 8, 9, 10, 0, 18, 0, 16, 20, 0, 4, + 0, 17, 22, 11, 0, 0, 12, 13 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -8, -8, -1, 22, -8, -8, -3, -8, -4 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 6, 7, 8, 9, 10, 14, 15, 11 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 16, 12, 18, 1, 2, 1, 1, 35, 3, 16, + 25, 16, 27, 17, 4, 24, 36, 29, 5, 13, + 5, 5, 20, 21, 32, 28, 22, 23, 30, 33, + 34, 19, 37, 26, 31 +}; + +static const yytype_int8 yycheck[] = +{ + 4, 2, 9, 3, 4, 3, 3, 3, 8, 13, + 13, 15, 15, 0, 14, 5, 12, 18, 18, 17, + 18, 18, 6, 7, 28, 16, 10, 11, 3, 12, + 13, 9, 12, 15, 15 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 3, 4, 8, 14, 18, 20, 21, 22, 23, + 24, 27, 21, 17, 25, 26, 27, 0, 9, 22, + 6, 7, 10, 11, 5, 25, 15, 25, 16, 21, + 3, 15, 27, 12, 13, 3, 12, 12 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int8 yyr1[] = +{ + 0, 19, 20, 21, 21, 22, 22, 23, 23, 23, + 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, + 25, 26, 26, 27, 27 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 3, 1, 2, 1, 2, 2, + 2, 4, 5, 6, 1, 1, 3, 4, 3, 1, + 2, 1, 3, 1, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (ctx, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, ctx); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, REContext& ctx) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (ctx); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, REContext& ctx) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, ctx); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, REContext& ctx) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], ctx); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, ctx); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, REContext& ctx) +{ + YY_USE (yyvaluep); + YY_USE (ctx); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (REContext& ctx) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* regex: expression */ + { + *ctx.expr = (*(yyvsp[0].rValue)); + delete (yyvsp[0].rValue); + } + break; + + case 4: /* expression: term "|" expression */ + { + *(yyvsp[-2].rValue) = *(yyvsp[-2].rValue) | *(yyvsp[0].rValue); + delete (yyvsp[0].rValue); + (yyval.rValue) = (yyvsp[-2].rValue); + } + break; + + case 6: /* term: factor term */ + { + *(yyvsp[-1].rValue) = *(yyvsp[-1].rValue) + *(yyvsp[0].rValue); + delete (yyvsp[0].rValue); + (yyval.rValue) = (yyvsp[-1].rValue); + } + break; + + case 8: /* factor: atom "*" */ + { + *(yyvsp[-1].rValue) = *(*(yyvsp[-1].rValue)); + (yyval.rValue) = (yyvsp[-1].rValue); + } + break; + + case 9: /* factor: atom "+" */ + { + *(yyvsp[-1].rValue) = +(*(yyvsp[-1].rValue)); + (yyval.rValue) = (yyvsp[-1].rValue); + } + break; + + case 10: /* factor: atom "?" */ + { + *(yyvsp[-1].rValue) = (*(yyvsp[-1].rValue))(0, 1); + (yyval.rValue) = (yyvsp[-1].rValue); + } + break; + + case 11: /* factor: atom "{" R_INTEGER "}" */ + { + *(yyvsp[-3].rValue) = (*(yyvsp[-3].rValue))((yyvsp[-1].iValue), (yyvsp[-1].iValue)); + (yyval.rValue) = (yyvsp[-3].rValue); + } + break; + + case 12: /* factor: atom "{" R_INTEGER "," "}" */ + { + *(yyvsp[-4].rValue) = (*(yyvsp[-4].rValue))((yyvsp[-2].iValue)); + (yyval.rValue) = (yyvsp[-4].rValue); + } + break; + + case 13: /* factor: atom "{" R_INTEGER "," R_INTEGER "}" */ + { + *(yyvsp[-5].rValue) = (*(yyvsp[-5].rValue))((yyvsp[-3].iValue), (yyvsp[-1].iValue)); + (yyval.rValue) = (yyvsp[-5].rValue); + } + break; + + case 14: /* atom: literal */ + { (yyval.rValue) = new REG((yyvsp[0].iValue)); } + break; + + case 15: /* atom: "." */ + { + IntArgs range = IntArgs::create(ctx.dom.max().toInt() - ctx.dom.min().toInt() + 1, ctx.dom.min().toInt()); + (yyval.rValue) = new REG(range); + } + break; + + case 16: /* atom: "[" set_items "]" */ + { + std::vector v; + v.reserve((yyvsp[-1].setValue)->size()); + for(auto i : *(yyvsp[-1].setValue)) { + v.push_back(i); + } + delete (yyvsp[-1].setValue); + (yyval.rValue) = new REG(IntArgs(v)); + } + break; + + case 17: /* atom: "[" "^" set_items "]" */ + { + std::vector diff; + std::unordered_set domain; + for(int i = ctx.dom.min().toInt(); i<=ctx.dom.max().toInt(); ++i) { + domain.insert(i); + } + std::set_difference( + domain.begin(), domain.end(), + (yyvsp[-1].setValue)->begin(), (yyvsp[-1].setValue)->end(), + std::inserter(diff, diff.begin()) + ); + delete (yyvsp[-1].setValue); + (yyval.rValue) = new REG(IntArgs(diff)); + } + break; + + case 18: /* atom: "(" expression ")" */ + { (yyval.rValue) = (yyvsp[-1].rValue); } + break; + + case 20: /* set_items: set_item set_items */ + { + (yyval.setValue) = (yyvsp[-1].setValue); + for (auto i : *(yyvsp[0].setValue)) { + (yyvsp[-1].setValue)->insert(i); + } + delete (yyvsp[0].setValue); + } + break; + + case 21: /* set_item: literal */ + { + (yyval.setValue) = new std::unordered_set({(yyvsp[0].iValue)}); + } + break; + + case 22: /* set_item: literal "-" literal */ + { + int from = (yyvsp[-2].iValue); + int to = (yyvsp[0].iValue); + if (to < from) { + std::swap(from,to); + } + (yyval.setValue) = new std::unordered_set; + for(int i = from; i<=to; ++i) { + (yyval.setValue)->insert(i); + } + } + break; + + case 24: /* literal: R_IDENTIFIER */ + { + std::string s((yyvsp[0].sValue)); + auto find = ctx.idMap.find(s); + if (find == ctx.idMap.end()) { + throw std::runtime_error("Unknown identifier: " + s); + } + (yyval.iValue) = find->second; + } + break; + + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (ctx, YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, ctx); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, ctx); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (ctx, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, ctx); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, ctx); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + + + +void yyerror(REContext& ctx, const char* s) { + // TODO: Add error location + throw std::runtime_error("Cannot parse regular expression: " + std::string(s)); +} + +std::unique_ptr regex_from_string(const std::string& regex_str, const IntSetVal& domain, const std::unordered_map& identifiers) { + REG* expr = new REG(); + regex_yy_scan_string(regex_str.c_str()); + REContext rctx = REContext{expr, domain, identifiers}; + int err = yyparse(rctx); + if (err != 0) { + throw std::runtime_error("Cannot parse regular expression, error code " + std::to_string(err)); + } + return std::unique_ptr(expr); +} diff --git a/lib/cdecode.c b/lib/cdecode.c new file mode 100644 index 0000000..a316093 --- /dev/null +++ b/lib/cdecode.c @@ -0,0 +1,83 @@ +/* +cdecoder.c - c source to a base64 decoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include + +int base64_decode_value(char value_in) { + static const char decoding[] = {62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, + -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; + static const char decoding_size = sizeof(decoding); + value_in -= 43; + if (value_in < 0 || value_in >= decoding_size) return -1; + return decoding[(int)value_in]; +} + +void base64_init_decodestate(base64_decodestate* state_in) { + state_in->step = step_a; + state_in->plainchar = 0; +} + +int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, + base64_decodestate* state_in) { + const char* codechar = code_in; + char* plainchar = plaintext_out; + char fragment; + + *plainchar = state_in->plainchar; + + switch (state_in->step) { + while (1) { + case step_a: + do { + if (codechar == code_in + length_in) { + state_in->step = step_a; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar = (fragment & 0x03f) << 2; + case step_b: + do { + if (codechar == code_in + length_in) { + state_in->step = step_b; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x030) >> 4; + *plainchar = (fragment & 0x00f) << 4; + case step_c: + do { + if (codechar == code_in + length_in) { + state_in->step = step_c; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03c) >> 2; + *plainchar = (fragment & 0x003) << 6; + case step_d: + do { + if (codechar == code_in + length_in) { + state_in->step = step_d; + state_in->plainchar = *plainchar; + return plainchar - plaintext_out; + } + fragment = (char)base64_decode_value(*codechar++); + } while (fragment < 0); + *plainchar++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return plainchar - plaintext_out; +} diff --git a/lib/cencode.c b/lib/cencode.c new file mode 100644 index 0000000..ddb76a4 --- /dev/null +++ b/lib/cencode.c @@ -0,0 +1,98 @@ +/* +cencoder.c - c source to a base64 encoding algorithm implementation + +This is part of the libb64 project, and has been placed in the public domain. +For details, see http://sourceforge.net/projects/libb64 +*/ + +#include + +const int CHARS_PER_LINE = 72; + +void base64_init_encodestate(base64_encodestate* state_in) { + state_in->step = step_A; + state_in->result = 0; + state_in->stepcount = 0; +} + +char base64_encode_value(char value_in) { + static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (value_in > 63) return '='; + return encoding[(int)value_in]; +} + +int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, + base64_encodestate* state_in) { + const char* plainchar = plaintext_in; + const char* const plaintextend = plaintext_in + length_in; + char* codechar = code_out; + char result; + char fragment; + + result = state_in->result; + + switch (state_in->step) { + while (1) { + case step_A: + if (plainchar == plaintextend) { + state_in->result = result; + state_in->step = step_A; + return codechar - code_out; + } + fragment = *plainchar++; + result = (fragment & 0x0fc) >> 2; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (plainchar == plaintextend) { + state_in->result = result; + state_in->step = step_B; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0f0) >> 4; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (plainchar == plaintextend) { + state_in->result = result; + state_in->step = step_C; + return codechar - code_out; + } + fragment = *plainchar++; + result |= (fragment & 0x0c0) >> 6; + *codechar++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *codechar++ = base64_encode_value(result); + + ++(state_in->stepcount); + if (state_in->stepcount == CHARS_PER_LINE / 4) { + *codechar++ = '\n'; + state_in->stepcount = 0; + } + } + } + /* control should not reach here */ + return codechar - code_out; +} + +int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { + char* codechar = code_out; + + switch (state_in->step) { + case step_B: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + *codechar++ = '='; + break; + case step_C: + *codechar++ = base64_encode_value(state_in->result); + *codechar++ = '='; + break; + case step_A: + break; + } + *codechar++ = '\n'; + + return codechar - code_out; +} diff --git a/lib/chain_compressor.cpp b/lib/chain_compressor.cpp new file mode 100644 index 0000000..558ea96 --- /dev/null +++ b/lib/chain_compressor.cpp @@ -0,0 +1,531 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include + +namespace MiniZinc { + +void ChainCompressor::removeItem(Item* i) { + CollectDecls cd(env.vo, deletedVarDecls, i); + if (auto ci = i->dyn_cast()) { + topDown(cd, ci->e()); + } else if (auto vdi = i->dyn_cast()) { + topDown(cd, vdi->e()); + } else { + assert(false); // CURRENTLY NOT SUPPORTED + } + env.flat_removeItem(i); +} + +int ChainCompressor::addItem(Item* i) { + env.flat_addItem(i); + int item_idx = env.flat()->size() - 1; + trackItem(i); + return item_idx; +} + +void ChainCompressor::updateCount() { + for (auto it = this->items.begin(); it != items.end();) { + if (it->second->removed()) { + it = this->items.erase(it); + } else { + ++it; + } + } +} + +void ChainCompressor::replaceCallArgument(Item* i, Call* c, unsigned int n, Expression* e) { + CollectDecls cd(env.vo, deletedVarDecls, i); + topDown(cd, c->arg(n)); + c->arg(n, e); + CollectOccurrencesE ce(env.vo, i); + topDown(ce, e); +} + +bool ImpCompressor::trackItem(Item* i) { + if (i->removed()) { + return false; + } + if (auto ci = i->dyn_cast()) { + if (auto c = ci->e()->dyn_cast()) { + // clause([y], [x]); i.e. x -> y + if (c->id() == constants().ids.clause) { + auto positive = c->arg(0)->cast(); + auto negative = c->arg(1)->cast(); + if (positive->length() == 1 && negative->length() == 1) { + auto var = (*negative)[0]->cast(); + storeItem(var->decl(), i); + return true; + } + } else if (c->id() == "mzn_reverse_map_var") { + auto control = c->arg(0)->cast(); + assert(control->type().isvarbool()); + storeItem(control->decl(), i); + return true; + // pred_imp(..., b); i.e. b -> pred(...) + } else if (c->id().endsWith("_imp")) { + auto control = c->arg(c->n_args() - 1)->cast(); + assert(control->type().isvarbool()); + storeItem(control->decl(), i); + return true; + } + } + } else if (auto vdi = i->dyn_cast()) { + if (vdi->e()->type().isvarbool() && vdi->e() && vdi->e()->e()) { + if (auto c = vdi->e()->e()->dyn_cast()) { + // x = forall([y,z,...]); potentially: x -> (y /\ z /\ ...) + if (c->id() == constants().ids.forall) { + storeItem(vdi->e(), i); + return true; + // x ::ctx_pos = pred(...); potentially: pred_imp(..., x); i.e. x -> pred(...) + } else if (env.fopts.enable_imp && vdi->e()->ann().contains(constants().ctx.pos)) { + GCLock lock; + auto cid = env.halfReifyId(c->id()); + std::vector args; + args.reserve(c->n_args() + 1); + for (int j = 0; j < c->n_args(); ++j) { + args.push_back(c->arg(j)->type()); + } + args.push_back(Type::varbool()); + FunctionI* decl = env.model->matchFn(env, cid, args, false); + + if (decl) { + storeItem(vdi->e(), i); + return true; + } + } + } + } + } + return false; +} + +void ImpCompressor::compress() { + for (auto it = items.begin(); it != items.end();) { + VarDecl* lhs = nullptr; + VarDecl* rhs = nullptr; + // Check if compression is possible + if (auto ci = it->second->dyn_cast()) { + auto c = ci->e()->cast(); + if (c->id() == constants().ids.clause) { + auto positive = c->arg(0)->cast(); + auto var = (*positive)[0]->cast(); + int occurrences = env.vo.usages(var->decl()); + unsigned long lhs_occurences = count(var->decl()); + bool output_var = var->decl()->ann().contains(constants().ann.output_var); + + // Compress if: + // - There is one occurrence on the RHS of a clause and the others are on the LHS of a + // clause + // - There is one occurrence on the RHS of a clause, that Id is a reified forall that has no + // other occurrences + // - There is one occurrence on the RHS of a clause, that Id is a reification in a positive + // context, and all other occurrences are on the LHS of a clause + bool compress = !output_var && lhs_occurences > 0; + if (var->decl()->e() && var->decl()->e()->dyn_cast()) { + auto call = var->decl()->e()->cast(); + if (call->id() == constants().ids.forall) { + compress = compress && (occurrences == 1 && lhs_occurences == 1); + } else { + compress = compress && (occurrences == lhs_occurences); + } + } else { + compress = compress && (occurrences == lhs_occurences + 1); + } + if (compress) { + rhs = var->decl(); + auto negative = c->arg(1)->cast(); + lhs = (*negative)[0]->cast()->decl(); + if (lhs == rhs) { + continue; + } + } + // TODO: Detect equivalences for output variables. + } + } + + if (lhs && rhs) { + assert(count(rhs) > 0); + + auto range = find(rhs); + for (auto match = range.first; match != range.second;) { + bool succes = compressItem(match->second, lhs); + assert(succes); + env.n_imp_del++; + match = items.erase(match); + } + + assert(!rhs->ann().contains(constants().ann.output_var)); + removeItem(it->second); + it = items.erase(it); + } else { + ++it; + } + } +} + +bool ImpCompressor::compressItem(Item* i, VarDecl* newLHS) { + GCLock lock; + if (auto ci = i->dyn_cast()) { + auto c = ci->e()->cast(); + // Given (x -> y) /\ (y -> z), produce x -> z + if (c->id() == constants().ids.clause) { + auto positive = c->arg(0)->cast(); + auto rhs = (*positive)[0]->cast(); + if (rhs->decl() != newLHS) { + ConstraintI* nci = constructClause(positive, newLHS->id()); + boolConstraints.push_back(addItem(nci)); + } + removeItem(i); + return true; + // Given (x -> y) /\ (y -> pred(...)), produce x -> pred(...) + } else if (c->id() == "mzn_reverse_map_var") { + return true; + } else if (c->id().endsWith("_imp")) { + replaceCallArgument(i, c, c->n_args() - 1, newLHS->id()); + trackItem(i); + return true; + } + } else if (auto vdi = i->dyn_cast()) { + auto c = vdi->e()->e()->dyn_cast(); + // Given: (x -> y) /\ (y -> (a /\ b /\ ...)), produce (x -> a) /\ (x -> b) /\ ... + if (c->id() == constants().ids.forall) { + auto exprs = c->arg(0)->cast(); + for (int j = 0; j < exprs->size(); ++j) { + auto rhs = (*exprs)[j]->cast(); + if (rhs->decl() != newLHS) { + ConstraintI* nci = constructClause(rhs, newLHS->id()); + boolConstraints.push_back(addItem(nci)); + } + } + return true; + // x ::ctx_pos = pred(...); potentially: pred_imp(..., x); i.e. x -> pred(...) + } else if (vdi->e()->ann().contains(constants().ctx.pos)) { + ConstraintI* nci = constructHalfReif(c, newLHS->id()); + assert(nci); + addItem(nci); + return true; + } + } + return false; +} + +ConstraintI* ImpCompressor::constructClause(Expression* pos, Expression* neg) { + assert(GC::locked()); + std::vector args(2); + if (pos->dyn_cast()) { + args[0] = pos; + } else { + assert(neg->type().isbool()); + std::vector eVec(1); + eVec[0] = pos; + args[0] = new ArrayLit(pos->loc().introduce(), eVec); + args[0]->type(Type::varbool(1)); + } + if (neg->dyn_cast()) { + args[1] = neg; + } else { + assert(neg->type().isbool()); + std::vector eVec(1); + eVec[0] = neg; + args[1] = new ArrayLit(neg->loc().introduce(), eVec); + args[1]->type(Type::varbool(1)); + } + // NEVER CREATE (a -> a) + assert((*args[0]->dyn_cast())[0]->dyn_cast()->decl() != + (*args[1]->dyn_cast())[0]->dyn_cast()->decl()); + auto nc = new Call(MiniZinc::Location().introduce(), constants().ids.clause, args); + nc->type(Type::varbool()); + nc->decl(env.model->matchFn(env, nc, false)); + assert(nc->decl()); + + return new ConstraintI(MiniZinc::Location().introduce(), nc); +} + +ConstraintI* ImpCompressor::constructHalfReif(Call* call, Id* control) { + assert(env.fopts.enable_imp); + assert(GC::locked()); + auto cid = env.halfReifyId(call->id()); + std::vector args(call->n_args()); + for (int i = 0; i < call->n_args(); ++i) { + args[i] = call->arg(i); + } + args.push_back(control); + FunctionI* decl = env.model->matchFn(env, cid, args, false); + if (decl) { + auto nc = new Call(call->loc().introduce(), cid, args); + nc->decl(decl); + nc->type(Type::varbool()); + return new ConstraintI(call->loc().introduce(), nc); + } + return nullptr; +} + +bool LECompressor::trackItem(Item* i) { + if (i->removed()) { + return false; + } + bool added = false; + if (auto ci = i->dyn_cast()) { + if (auto call = ci->e()->dyn_cast()) { + // {int,float}_lin_le([c1,c2,...], [x, y,...], 0); + if (call->id() == constants().ids.int_.lin_le || + call->id() == constants().ids.float_.lin_le) { + auto as = follow_id(call->arg(0))->cast(); + auto bs = follow_id(call->arg(1))->cast(); + assert(as->size() == bs->size()); + + for (int j = 0; j < as->size(); ++j) { + if (as->type().isintarray()) { + if (follow_id((*as)[j])->cast()->v() > IntVal(0)) { + // Check if left hand side is a variable (could be constant) + if (auto decl = follow_id_to_decl((*bs)[j])->dyn_cast()) { + storeItem(decl, i); + added = true; + } + } + } else { + if (follow_id((*as)[j])->cast()->v() > FloatVal(0)) { + // Check if left hand side is a variable (could be constant) + if (auto decl = follow_id_to_decl((*bs)[j])->dyn_cast()) { + storeItem(decl, i); + added = true; + } + } + } + } + } + assert(call->id() != constants().ids.int2float); + } + } else if (auto vdi = i->dyn_cast()) { + assert(vdi->e()); + if (Expression* vde = vdi->e()->e()) { + if (auto call = vde->dyn_cast()) { + if (call->id() == constants().ids.int2float) { + if (auto vd = follow_id_to_decl(call->arg(0))->dyn_cast()) { + auto alias = follow_id_to_decl(vdi->e())->cast(); + aliasMap[vd] = alias; + } + } + } + } + } + return added; +} + +void LECompressor::compress() { + for (auto it = items.begin(); it != items.end();) { + VarDecl* lhs = nullptr; + VarDecl* rhs = nullptr; + VarDecl* alias = nullptr; + + // Check if compression is possible + if (auto ci = it->second->dyn_cast()) { + auto call = ci->e()->cast(); + if (call->id() == constants().ids.int_.lin_le) { + auto as = follow_id(call->arg(0))->cast(); + auto bs = follow_id(call->arg(1))->cast(); + auto c = follow_id(call->arg(2))->cast(); + + if (bs->size() == 2 && c->v() == IntVal(0)) { + auto a0 = follow_id((*as)[0])->cast()->v(); + auto a1 = follow_id((*as)[1])->cast()->v(); + if (a0 == -a1 && eqBounds((*bs)[0], (*bs)[1])) { + int i = a0 < a1 ? 0 : 1; + if (!(*bs)[i]->isa()) { + break; + } + auto neg = follow_id_to_decl((*bs)[i])->cast(); + bool output_var = neg->ann().contains(constants().ann.output_var); + + int occurrences = env.vo.usages(neg); + unsigned long lhs_occurences = count(neg); + bool compress = !output_var; + auto search = aliasMap.find(neg); + + if (search != aliasMap.end()) { + alias = search->second; + int alias_occ = env.vo.usages(alias); + unsigned long alias_lhs_occ = count(alias); + // neg is only allowed to occur: + // - once in the "implication" + // - once in the aliasing + // - on a lhs of other expressions + // alias is only allowed to occur on a lhs of an expression. + compress = compress && (lhs_occurences + alias_lhs_occ > 0) && + (occurrences == lhs_occurences + 2) && (alias_occ == alias_lhs_occ); + } else { + // neg is only allowed to occur: + // - once in the "implication" + // - on a lhs of other expressions + compress = compress && (lhs_occurences > 0) && (occurrences == lhs_occurences + 1); + } + + auto pos = follow_id_to_decl((*bs)[1 - i])->dyn_cast(); + if (pos && compress) { + rhs = neg; + lhs = pos; + assert(lhs != rhs); + } + // TODO: Detect equivalences for output variables. + } + } + } + } + + if (lhs && rhs) { + assert(count(rhs) + count(alias) > 0); + + auto range = find(rhs); + for (auto match = range.first; match != range.second;) { + LEReplaceVar(match->second, rhs, lhs); + match = items.erase(match); + } + if (alias) { + VarDecl* i2f_lhs; + + auto search = aliasMap.find(lhs); + if (search != aliasMap.end()) { + i2f_lhs = search->second; + } else { + // Create new int2float + Call* i2f = new Call(lhs->loc().introduce(), constants().ids.int2float, {lhs->id()}); + i2f->decl(env.model->matchFn(env, i2f, false)); + assert(i2f->decl()); + i2f->type(Type::varfloat()); + auto domain = new SetLit(lhs->loc().introduce(), eval_floatset(env, lhs->ti()->domain())); + auto i2f_ti = new TypeInst(lhs->loc().introduce(), Type::varfloat(), domain); + i2f_lhs = new VarDecl(lhs->loc().introduce(), i2f_ti, env.genId(), i2f); + i2f_lhs->type(Type::varfloat()); + addItem(new VarDeclI(lhs->loc().introduce(), i2f_lhs)); + } + + auto arange = find(alias); + for (auto match = arange.first; match != arange.second;) { + LEReplaceVar(match->second, alias, i2f_lhs); + match = items.erase(match); + } + } + + assert(!rhs->ann().contains(constants().ann.output_var)); + removeItem(it->second); + env.n_lin_del++; + it = items.erase(it); + } else { + ++it; + } + } +} + +template +void LECompressor::LEReplaceVar(Item* i, VarDecl* oldVar, VarDecl* newVar) { + typedef typename LinearTraits::Val Val; + GCLock lock; + + auto ci = i->cast(); + auto call = ci->e()->cast(); + assert(call->id() == constants().ids.int_.lin_le || call->id() == constants().ids.float_.lin_le); + + // Remove old occurrences + CollectDecls cd(env.vo, deletedVarDecls, i); + topDown(cd, ci->e()); + + ArrayLit* al_c = eval_array_lit(env, call->arg(0)); + std::vector coeffs(al_c->size()); + for (int j = 0; j < al_c->size(); j++) { + coeffs[j] = LinearTraits::eval(env, (*al_c)[j]); + } + ArrayLit* al_x = eval_array_lit(env, call->arg(1)); + std::vector x(al_x->size()); + for (int j = 0; j < al_x->size(); j++) { + Expression* decl = follow_id_to_decl((*al_x)[j]); + if (decl && decl->cast() == oldVar) { + x[j] = newVar->id(); + } else { + x[j] = (*al_x)[j]; + } + } + Val d = LinearTraits::eval(env, call->arg(2)); + + simplify_lin(coeffs, x, d); + if (coeffs.empty()) { + env.flat_removeItem(i); + env.n_lin_del++; + return; + } else { + std::vector coeffs_e(coeffs.size()); + std::vector x_e(coeffs.size()); + for (unsigned int j = 0; j < coeffs.size(); j++) { + coeffs_e[j] = Lit::a(coeffs[j]); + x_e[j] = x[j](); + Expression* decl = follow_id_to_decl(x_e[j]); + if (decl && decl->cast() == newVar) { + storeItem(newVar, i); + } + } + + if (auto arg0 = call->arg(0)->dyn_cast()) { + arg0->setVec(coeffs_e); + } else { + auto al_c_new = new ArrayLit(al_c->loc().introduce(), coeffs_e); + al_c_new->type(al_c->type()); + call->arg(0, al_c_new); + } + + if (auto arg1 = call->arg(1)->dyn_cast()) { + arg1->setVec(x_e); + } else { + auto al_x_new = new ArrayLit(al_x->loc().introduce(), x_e); + al_x_new->type(al_x->type()); + call->arg(1, al_x_new); + } + + call->arg(2, Lit::a(d)); + } + + // Add new occurences + CollectOccurrencesE ce(env.vo, i); + topDown(ce, ci->e()); +} + +bool LECompressor::eqBounds(Expression* a, Expression* b) { + // TODO: (To optimise) Check lb(lhs) >= lb(rhs) and enforce ub(lhs) <= ub(rhs) + IntSetVal* dom_a = nullptr; + IntSetVal* dom_b = nullptr; + + if (auto a_decl = follow_id_to_decl(a)->dyn_cast()) { + if (a_decl->ti()->domain()) { + dom_a = eval_intset(env, a_decl->ti()->domain()); + } + } else { + assert(a->dyn_cast()); + auto a_val = a->cast(); + dom_a = IntSetVal::a(a_val->v(), a_val->v()); + } + + if (auto b_decl = follow_id_to_decl(b)->dyn_cast()) { + if (b_decl->ti()->domain()) { + dom_b = eval_intset(env, b_decl->ti()->domain()); + } + } else { + assert(b->dyn_cast()); + auto b_val = b->cast(); + dom_b = IntSetVal::a(b_val->v(), b_val->v()); + } + + return (dom_a && dom_b && (dom_a->min() == dom_b->min()) && (dom_a->max() == dom_b->max())) || + (!dom_a && !dom_b); +} + +} // namespace MiniZinc diff --git a/lib/codegen.cpp b/lib/codegen.cpp new file mode 100644 index 0000000..a1064b6 --- /dev/null +++ b/lib/codegen.cpp @@ -0,0 +1,4763 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include <../lib/codegen/analysis.hpp> +#include <../lib/codegen/codegen_internal.hpp> +#include +#include + +namespace MiniZinc { + +// Known limitations: +// - Comprehensions and generator expressions are assumed to be total, so are evaluated in root +// context. Expression evaluation currently runs in two modes: +// - eval, which places the result onto the value stack on the enclosing context, or +// - locate, which places the result into a register, and returns the register. +// this is done to avoid a unnecessary push/pop sequences, when a result is already bound to +// a register, and is needed for e.g. a call. + +#define ENABLE_PLUS 1 +// MZNC_COLLECT_LEAVES should generate fewer aggregation contexts for models with par-resolved +// partiality (i.e. lots of array accesses) +#define MZNC_COLLECT_LEAVES + +using Mode = CG::Mode; + +struct builtin_t { + std::function boolean; + std::function general; +}; + +typedef std::unordered_map builtin_table; + +const char* instr_names[] = { + "ADDI", + "SUBI", + "MULI", + "DIVI", + "MODI", + "INCI", + "DECI", + + "IMMI", + "CLEAR", + "LOAD_GLOBAL", + "STORE_GLOBAL", + "MOV", + + "JMP", + "JMPIF", + "JMPIFNOT", + + "EQI", + "LTI", + "LEI", + + "AND", + "OR", + "NOT", + "XOR", + + "ISPAR", + "ISEMPTY", + "LENGTH", + "GET_VEC", + "GET_ARRAY", + + "LB", + "UB", + "DOM", + + "MAKE_SET", + "INTERSECTION", + "UNION", + "DIFF", + + "INTERSECT_DOMAIN", + + "OPEN_AGGREGATION", + "CLOSE_AGGREGATION", + "SIMPLIFY_LIN", + + "PUSH", + "POP", + "POST", + + "RET", + "CALL", + "BUILTIN", + "TCALL", + + "ITER_ARRAY", + "ITER_VEC", + "ITER_RANGE", + "ITER_NEXT", + "ITER_BREAK", + + "TRACE", + "ABORT", +}; + +const char* mode_names[] = { + "ROOT", "ROOT_NEG", "FUN", "FUN_NEG", "IMP", "IMP_NEG", "MAX_MODE", +}; + +const char* agg_names[] = {"AND", "OR", "VEC", "OTHER"}; +const char* instr_name(BytecodeStream::Instr i) { return instr_names[i]; } + +const char* agg_name(AggregationCtx::Symbol s) { return agg_names[s]; } +const char* mode_name(BytecodeProc::Mode m) { return mode_names[m]; } + +inline void TODO(void) { throw InternalError("Not yet implemented!"); } + +void CodeGen::register_builtins(void) { + // Solver Built-ins + register_builtin("mk_intvar", 1); + + // Constants + register_builtin("absent", 1); + register_builtin("infinity", 1); + register_builtin("boolean_domain", 0); + register_builtin("infinite_domain", 0); + + // Interpreter Built-ins + register_builtin("uniform", 2); + register_builtin("sol", 1); + register_builtin("sort_by", 2); + register_builtin("floor", 1); + register_builtin("ceil", 1); + register_builtin("slice_Xd", 3); + register_builtin("array_Xd", 2); + register_builtin("index_set", 2); + register_builtin("internal_sort", 1); +} + +void OPEN_AGG(CodeGen& cg, CG_Builder& frag, AggregationCtx::Symbol ctx) { + cg.env_push(); + cg.reg_trail.push_back(cg.current_reg_count); + + PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, ctx); +} +void CLOSE_AGG(CodeGen& cg, CG_Builder& frag) { + assert(cg.reg_trail.size() > 0); + int old_reg_count = cg.current_reg_count; + cg.current_reg_count = cg.reg_trail.back(); + cg.reg_trail.pop_back(); + if (cg.current_reg_count < old_reg_count) { + PUSH_INSTR(frag, BytecodeStream::CLEAR, CG::r(cg.current_reg_count), CG::r(old_reg_count - 1)); + } + PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); + cg.env_pop(); +} + +void OPEN_AND(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_AND); } +void OPEN_OR(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_OR); } +void OPEN_OTHER(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_OTHER); } +void OPEN_VEC(CodeGen& cg, CG_Builder& frag) { OPEN_AGG(cg, frag, AggregationCtx::VCTX_VEC); } + +CG_ProcID CodeGen::register_builtin(std::string s, unsigned int arity) { + auto it(_proc_map.find(s)); + if (it != _proc_map.end()) { + // throw InternalError(std::string("Builtin already registered: ") + s); + std::cerr << "WARNING: Builtin " << s << " already registered." << std::endl; + return it->second; + } + + CG_ProcID id(CG_ProcID::builtin(_builtins.size())); + _builtins.push_back(std::make_pair(s, arity)); + _proc_map.insert(std::make_pair(s, id)); + return id; +} + +CG_ProcID CodeGen::find_builtin(std::string s) { + auto it(_proc_map.find(s)); + assert(it != _proc_map.end()); + return (*it).second; +} + +int bind_cst(int x, CodeGen& cg, CG_Builder& frag); + +void bind_binop_var(CodeGen& cg, CG_Builder& frag, Mode ctx, BinOpType op, int r_lhs, int r_rhs) { + GCLock lock; + switch (op) { + // Actual builtins + case BOT_PLUS: { + auto fun = + find_call_fun(cg, {"op_plus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); + return; + } + case BOT_MINUS: { + auto fun = + find_call_fun(cg, {"op_minus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); + return; + } + case BOT_MULT: { + auto fun = + find_call_fun(cg, {"op_times"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); + return; + } + case BOT_IDIV: { + auto fun = find_call_fun(cg, {"op_int_division"}, Type::varint(), + {Type::varint(), Type::varint()}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); + return; + } + case BOT_DIV: { + auto fun = find_call_fun(cg, {"op_float_division"}, Type::varint(), + {Type::varint(), Type::varint()}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); + return; + } + case BOT_MOD: { + auto fun = + find_call_fun(cg, {"op_modulus"}, Type::varint(), {Type::varint(), Type::varint()}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_lhs), CG::r(r_rhs)); + return; + } + case BOT_DOTDOT: { + // The values in r_lhs and r_rhs had better be IMMIs. + OPEN_VEC(cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lhs)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_rhs)); + CLOSE_AGG(cg, frag); + return; + } + case BOT_PLUSPLUS: { + OPEN_VEC(cg, frag); + Foreach iter_lhs(cg, r_lhs); + iter_lhs.emit_pre(frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_lhs.val())); + iter_lhs.emit_post(frag); + + Foreach iter_rhs(cg, r_rhs); + iter_rhs.emit_pre(frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_rhs.val())); + iter_rhs.emit_post(frag); + CLOSE_AGG(cg, frag); + return; + } + default: + break; + } + throw InternalError("Unexpected fall-through in bind_binop_var."); +} + +int bind_binop_par_int(CodeGen& cg, CG_Builder& frag, BinOpType op, int r_lhs, int r_rhs) { + int r; + switch (op) { + // Actual builtins + case BOT_EQ: + case BOT_EQUIV: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_NQ: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + return r; + case BOT_LE: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_LQ: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_GR: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_rhs), CG::r(r_lhs), CG::r(r)); + return r; + case BOT_GQ: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_rhs), CG::r(r_lhs), CG::r(r)); + return r; + case BOT_XOR: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::XOR, CG::r(r_rhs), CG::r(r_lhs), CG::r(r)); + return r; + case BOT_AND: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_OR: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_IMPL: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_lhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_RIMPL: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_rhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::XOR, CG::r(r_lhs), CG::r(r), CG::r(r)); + return r; + case BOT_PLUS: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_MINUS: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_MULT: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::MULI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_IDIV: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::DIVI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_MOD: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::MODI, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_DOTDOT: + // The values in r_lhs and r_rhs had better be IMMIs. + OPEN_VEC(cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lhs)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_rhs)); + CLOSE_AGG(cg, frag); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return r; + case BOT_IN: { + int l_head(GET_LABEL(cg)); + int l_stop(GET_LABEL(cg)); + int l_exit(GET_LABEL(cg)); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::ITER_VEC, CG::r(r_rhs), CG::l(l_exit)); + PUSH_LABEL(frag, l_head); + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r)); + // Start of interval. + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r), CG::r(r_lhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_stop)); + // End of interval + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lhs), CG::r(r), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_stop)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_head)); + PUSH_LABEL(frag, l_stop); + PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(1)); + PUSH_LABEL(frag, l_exit); + return r; + } + case BOT_PLUSPLUS: { + r = GET_REG(cg); + OPEN_VEC(cg, frag); + Foreach iter_lhs(cg, r_lhs); + iter_lhs.emit_pre(frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_lhs.val())); + iter_lhs.emit_post(frag); + + Foreach iter_rhs(cg, r_rhs); + iter_rhs.emit_pre(frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(iter_rhs.val())); + iter_rhs.emit_post(frag); + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return r; + } + default: + break; + } + throw InternalError("Unexpected fall-through in bind_binop_par."); +} + +int bind_binop_par_set(CodeGen& cg, CG_Builder& frag, BinOpType op, int r_lhs, int r_rhs) { + int r; + switch (op) { + // Actual builtins + case BOT_EQ: + case BOT_EQUIV: { + r = GET_REG(cg); + int r_tmp = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(r), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_rhs), CG::r(r_lhs), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(r_tmp), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(r_tmp), CG::r(r)); + return r; + } + case BOT_NQ: { + r = bind_binop_par_set(cg, frag, BOT_EQ, r_lhs, r_rhs); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + return r; + } + case BOT_LE: + r = GET_REG(cg); + TODO(); + return r; + case BOT_LQ: + r = GET_REG(cg); + TODO(); + return r; + case BOT_GR: + r = GET_REG(cg); + TODO(); + return r; + case BOT_GQ: + r = GET_REG(cg); + TODO(); + return r; + case BOT_UNION: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_INTERSECT: + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + case BOT_SUBSET: { + // (L subset R) <-> ((L intersect R) == L) + r = GET_REG(cg); + int l_fin(GET_LABEL(cg)); + int r_inter(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(r_lhs), CG::r(r_rhs), CG::r(r_inter)); + + int r_sz(GET_REG(cg)); + int r_tmp(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r_lhs), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r_inter), CG::r(r_sz)); + PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_tmp), CG::r(r_sz), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_fin)); + + int r_idx(GET_REG(cg)); + int l_loop(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_idx)); + PUSH_LABEL(frag, l_loop); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_idx), CG::r(r_sz), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_tmp), CG::l(l_fin)); + + int r_tmp2(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_lhs), CG::r(r_idx), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_inter), CG::r(r_idx), CG::r(r_tmp2)); + PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_tmp), CG::r(r_tmp2), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_fin)); + + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_idx)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_loop)); + + PUSH_LABEL(frag, l_fin); + return r; + } + case BOT_SUPERSET: { + return bind_binop_par_set(cg, frag, BOT_SUBSET, r_rhs, r_lhs); + } + case BOT_DIFF: { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + return r; + } + case BOT_SYMDIFF: { + int r(GET_REG(cg)); + int r_tmp(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_lhs), CG::r(r_rhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r_rhs), CG::r(r_lhs), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r), CG::r(r_tmp), CG::r(r)); + return r; + } + default: + break; + } + throw InternalError("Unexpected fall-through in bind_binop_par_set."); +} + +CG_Cond::T linear_cond(CodeGen& cg, CG_Builder& frag, BinOpType op, Mode ctx, int r_lhs, + int r_rhs) { + GCLock lock; + switch (op) { + // Actual builtins + case BOT_LQ: + case BOT_LE: { + // Check bounds + int lb_lhs = GET_REG(cg); + int ub_rhs = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_lhs), CG::r(lb_lhs)); + PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_rhs), CG::r(ub_rhs)); + PUSH_INSTR(frag, (op == BOT_LE ? BytecodeStream::LTI : BytecodeStream::LEI), CG::r(lb_lhs), + CG::r(ub_rhs), CG::r(lb_lhs)); + int c = GET_REG(cg); + int x = GET_REG(cg); + int k = GET_REG(cg); + int z = (op == BOT_LE ? +1 : 0); + PUSH_INSTR(frag, BytecodeStream::SIMPLIFY_LIN, CG::r(r_lhs), CG::r(r_rhs), CG::i(z), CG::r(c), + CG::r(x), CG::r(k)); + auto linear = + CG_Cond::call({"pre_int_lin_le"}, ctx, true, + {Type::varbool(), Type::parint(1), Type::varint(1), Type::parint()}, + {CG::r(c), CG::r(x), CG::r(k)}); + return CG_Cond::forall(ctx, CG_Cond::reg(lb_lhs, true), linear); + } + case BOT_EQUIV: + case BOT_EQ: { + // Check domain + int dom_lhs = GET_REG(cg); + int dom_rhs = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_lhs), CG::r(dom_lhs)); + PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_rhs), CG::r(dom_rhs)); + PUSH_INSTR(frag, BytecodeStream::INTERSECTION, CG::r(dom_lhs), CG::r(dom_rhs), + CG::r(dom_lhs)); + PUSH_INSTR(frag, BytecodeStream::ISEMPTY, CG::r(dom_lhs), CG::r(dom_rhs)); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(dom_rhs), CG::r(dom_rhs)); + // Create linear equation + int k = GET_REG(cg); + int c = GET_REG(cg); + int x = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::SIMPLIFY_LIN, CG::r(r_lhs), CG::r(r_rhs), CG::i(0), CG::r(c), + CG::r(x), CG::r(k)); + auto linear = + CG_Cond::call({"pre_int_lin_eq"}, ctx, true, + {Type::varbool(), Type::parint(1), Type::varint(1), Type::parint()}, + {CG::r(c), CG::r(x), CG::r(k)}); + return CG_Cond::forall(ctx, CG_Cond::reg(dom_rhs, true), linear); + } + case BOT_NQ: + return ~linear_cond(cg, frag, BOT_EQ, -ctx, r_lhs, r_rhs); + case BOT_GR: + return linear_cond(cg, frag, BOT_LE, ctx, r_rhs, r_lhs); + case BOT_GQ: + return linear_cond(cg, frag, BOT_LQ, ctx, r_rhs, r_lhs); + default: + break; + } + throw InternalError("Unexpected fall-through in linear_cond."); +} + +CG_ProcID CodeGen::resolve_fun(FunctionI* fun, bool reserved_name) { + auto it(fun_bodies.find(fun)); + if (it != fun_bodies.end()) return it->second; + + GCLock lock; + + int p_idx = bytecode.size(); + CG_ProcID p_id(CG_ProcID::proc(p_idx)); + ASTExprVec params(fun->params()); + + std::stringstream ss; + if (!reserved_name && fun->e()) { + ss << "f_" << fun->id().str(); + for (auto& param : params) { + ss << "_"; + if (param->type().dim() > 0) { + ss << "d" << param->type().dim(); + } else if (param->type().dim() < 0) { + ss << "dT"; + } + if (param->type().isvar()) { + ss << "v"; + } + if (param->type().is_set()) { + ss << "s"; + } + switch (param->type().bt()) { + case Type::BT_BOOL: { + ss << "b"; + break; + } + case Type::BT_INT: { + ss << "i"; + break; + } + case Type::BT_FLOAT: { + ss << "f"; + break; + } + case Type::BT_STRING: { + ss << "s"; + break; + } + case Type::BT_ANN: { + ss << "a"; + break; + } + case Type::BT_TOP: { + ss << "t"; + break; + } + default: { + assert(false); + break; + } + } + } + } else { + ss << fun->id().str(); + } + + bytecode.emplace_back(ss.str(), params.size()); + fun_bodies.insert(std::make_pair(fun, p_id)); + return p_id; +} + +struct dispatch_node { + int label; + int level; + uint64_t sig; +}; + +std::tuple find_call_fun(CodeGen& cg, const ASTString& ident, + const Type& ret_type, + std::vector arg_types, + BytecodeProc::Mode m, + bool reserved_name) { + for (auto& arg_type : arg_types) { + arg_type.ti(Type::TI_PAR); + } + int sz = arg_types.size(); + CallSig sig(ident, arg_types); + auto it(cg.dispatch.find(sig)); + + BytecodeProc::Mode call_mode(ret_type.isbool() ? m : BytecodeProc::FUN); + BytecodeProc::Mode def_mode(ret_type.isbool() ? m : BytecodeProc::ROOT); + + if (it != cg.dispatch.end()) { + auto d_proc(it->second); + if (d_proc.first.is_builtin() || cg.bytecode[d_proc.first.id()].is_available(call_mode)) { + return {d_proc.first, call_mode, d_proc.second}; + } + } + + GCLock lock; + auto bodies = std::move(cg.fun_map.get_bodies(ident, arg_types)); + assert(!bodies.empty()); + auto neg_bodies = std::move(cg.fun_map.get_bodies(ASTString(ident.str() + "_neg"), arg_types)); + + if (ret_type.isbool() && call_mode != BytecodeProc::ROOT) { + bool valid = false; + if (cg.fun_map.defines_mode(ident, arg_types, call_mode).first) { + valid = true; + } else if (BytecodeProc::is_neg(m) && std::any_of(neg_bodies.begin(), neg_bodies.end(), + [](FunctionI* fi) { return fi->e(); })) { + valid = true; + } else { + valid = cg.fun_map.defines_mode(ident, arg_types, BytecodeProc::FUN).first; + if (valid) { + call_mode = BytecodeProc::FUN; + def_mode = BytecodeProc::FUN; + } + } + if (!valid && + std::none_of(bodies.begin(), bodies.end(), [](FunctionI* fi) { return fi->e(); })) { + throw InternalError(ident.str() + + " is used in a reified context, but no reification is available."); + } + } + + std::vector procs; + for (FunctionI* b : bodies) { + CG_ProcID body(cg.resolve_fun(b, reserved_name && bodies.size() == 1)); + // Force the body to be created + procs.push_back(body); + if (!cg.bytecode[body.id()].is_available(call_mode)) { + cg.bytecode[body.id()].body(call_mode); + cg.pending_bodies.emplace_back(b, std::make_pair(call_mode, def_mode)); + } + } + + // If there's a unique candidate, go for it. + if (procs.size() == 1) { + if (it == cg.dispatch.end()) { + cg.dispatch.insert(std::make_pair(sig, std::make_pair(procs[0], false))); + } + return {procs[0], call_mode, false}; + } + + CG_ProcID d_proc(CG_ProcID::builtin(0)); + if (it != cg.dispatch.end()) { + d_proc = it->second.first; + } else { + // Otherwise, generate the dispatch function. + int p_idx = cg.bytecode.size(); + + std::stringstream ss; + if (reserved_name) { + ss << ident.str(); + } else { + ss << "d_" << ident.str(); + for (auto& type : arg_types) { + ss << "_"; + if (type.dim() > 0) { + ss << "d" << type.dim(); + } else if (type.dim() < 0) { + ss << "dT"; + } + if (type.is_set()) { + ss << "s"; + } + switch (type.bt()) { + case Type::BT_BOOL: { + ss << "b"; + break; + } + case Type::BT_INT: { + ss << "i"; + break; + } + case Type::BT_FLOAT: { + ss << "f"; + break; + } + case Type::BT_STRING: { + ss << "s"; + break; + } + case Type::BT_ANN: { + ss << "a"; + break; + } + case Type::BT_TOP: { + ss << "t"; + break; + } + default: { + assert(false); + break; + } + } + } + } + + d_proc = CG_ProcID::proc(p_idx); + cg.bytecode.emplace_back(ss.str(), arg_types.size()); + cg.dispatch.insert(std::make_pair(sig, std::make_pair(d_proc, true))); + } + + // Now generate the dispatch body. + CG_Builder frag; + std::vector var_sig(sz); + std::vector def_sig(bodies.size()); + for (int bi = 0; bi < bodies.size(); ++bi) { + ASTExprVec params(bodies[bi]->params()); + for (int ii = 0; ii < params.size(); ++ii) { + if (params[ii]->type().isvar()) { + var_sig[ii] |= 1ull << bi; + def_sig[bi] |= 1ull << ii; + } + } + } + + std::vector> sig_table(sz + 1); + std::vector nodes; + + nodes.push_back(dispatch_node{-1, 0, (1ull << bodies.size()) - 1}); + + for (int ii = 0; ii < nodes.size(); ii++) { + dispatch_node d(nodes[ii]); + if (d.label != -1) PUSH_LABEL(frag, d.label); + if (!d.sig) { + PUSH_INSTR(frag, BytecodeStream::ABORT); + } else if (d.level == sz) { + // Find the best candidate, and emit a call. + uint64_t candidates(d.sig); + unsigned int best(find_lsb(candidates)); + uint64_t best_sig(def_sig[best]); + candidates ^= 1ull << best; + while (candidates) { + unsigned int curr(find_lsb(candidates)); + candidates ^= 1ull << curr; + uint64_t sig(def_sig[curr]); + if (!(sig & ~best_sig)) { + // At least as good as the incumbent + best = curr; + best_sig = sig; + } + } + CG_ProcID p_id(procs[best]); + PUSH_INSTR(frag, BytecodeStream::TCALL, call_mode, p_id, + CG::i(bodies[best]->ti()->type().isvar())); + } else { + int par_label; + auto p_it(sig_table[d.level + 1].find(d.sig)); + if (p_it != sig_table[d.level + 1].end()) { + par_label = p_it->second; + } else { + par_label = GET_LABEL(cg); + // int idx = nodes.size(); + sig_table[d.level + 1].insert(std::make_pair(d.sig, par_label)); + nodes.push_back(dispatch_node{par_label, d.level + 1, d.sig}); + } + uint64_t v_sig(d.sig & var_sig[d.level]); + if (v_sig == d.sig) { + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(par_label)); + } else { + int var_label; + auto v_it(sig_table[d.level + 1].find(v_sig)); + if (v_it != sig_table[d.level + 1].end()) { + var_label = v_it->second; + } else { + var_label = GET_LABEL(cg); + // int idx = nodes.size(); + sig_table[d.level + 1].insert(std::make_pair(v_sig, var_label)); + nodes.push_back(dispatch_node{var_label, d.level + 1, v_sig}); + } + + PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(d.level), CG::r(sz)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(sz), CG::l(par_label)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(var_label)); + } + } + } + + cg.append(d_proc.id(), call_mode, frag); + + return {d_proc, call_mode, false}; +} + +std::tuple find_call_fun(CodeGen& cg, Call* call, + BytecodeProc::Mode m, + bool reserved_name) { + std::vector arg_types; + int sz = call->n_args(); + for (int ii = 0; ii < sz; ++ii) { + Type t(call->arg(ii)->type()); + arg_types.push_back(t); + } + return find_call_fun(cg, call->id(), call->type(), arg_types, m, reserved_name); +} +/* +CG_ProcID find_call_pred(CodeGen& cg, Call* c) { + return CG_ProcID::proc(0xbead); + +} +*/ + +// Analyse an expression (and sub-expressions) for partiality +#if 0 +struct ClearFlags : public EVisitor { + bool enter(Expression* e) { + if(!e->isUnboxedVal() && e->user_flag0()) { + e->user_flag0(0); + e->user_flag1(0); + return true; + } + return false; + } + // FIXME: We need to identify call bodies. + void vCall(const Call&) {} + + static void clear(Expression* e) { + ClearFlags cf; + TopDownIterator td(cf); + td.run(e); + } +}; + +struct Partiality { + Partiality(CodeGen& _cg) : cg(_cg) { } + + // We use _flag_3 to track whether something is already on the + // stack, and _flag_4 to track whether it is eliminated as true. + bool is_partial(Expression* e) { + if(e->isUnboxedVal()) + return false; + // Either on the call stack and still open, or completed. + // In either case, check whether the partiality-flag is set. + if(e->user_flag0()) + return e->user_flag1(); + // Otherwise, mark it as pending, and enter it. + e->user_flag0(1); + bool p = _is_partial(e); + // Record the result, and return. + e->user_flag1(p); + return p; + } + + bool _is_partial(Expression* e) { + // First, Boolean expressions are always total. + if(e->type().isbool()) + return false; + // Otherwise, look at the + switch(e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_SETLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ID: + return false; + case Expression::E_ARRAYLIT: { + ArrayLit* a(e->template cast()); + int sz(a->size()); + for(int ii = 0; ii < sz; ++ii) { + if(is_partial((*a)[ii])) + return true; + } + return true; + } + case Expression::E_ARRAYACCESS: { + /* + ArrayAccess* a(e->template cast()); + if(is_partial(a->v())) + return true; + ASTExprVec idx(a->idx()); + int sz(idx.size()); + for(int ii = 0; ii < sz; ++ii) { + if(is_partial(idx[ii])) + return true; + } + break; + */ + // FIXME: Needs an analysis to determine whether + // dom(a->v) subseteq index_set(A). + return true; + } + case Expression::E_COMP: { + // A comprehension is total if all its generators + // and its body are total. + // Don't need to look in the where clauses, because + // they're Boolean, and therefore total. + Comprehension* c(e->template cast()); + int sz = c->n_generators(); + for(int g = 0; g < c->n_generators(); ++g) { + if(is_partial(c->in(g))) + return true; + } + return is_partial(c->e()); + } + case Expression::E_ITE: { + // The conditions are Boolean, so must be total. + // Look at the values. + ITE* ite(e->template cast()); + int sz(ite->size()); + if(is_partial(ite->e_else())) + return false; + for(int ii = 0; ii < sz; ++ii) { + if(is_partial(ite->e_then(ii))) + return false; + } + return true; + } + case Expression::E_BINOP: { + BinOp* b(e->template cast()); + // First, check if the op is itself partial. + // TODO: (Eventually) add a pass to determine whether we can exclude + // 0 from the domain of b->rhs(). + if(b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) + return true; + return is_partial(b->lhs()) || is_partial(b->rhs()); + } + case Expression::E_UNOP: + return is_partial(e->template cast()->e()); + + case Expression::E_CALL: { + Call* call(e->template cast()); + int sz = call->n_args(); + // Check if any of its arguments are partial. + for(int ii = 0; ii < sz; ++ii) { + if(is_partial(call->arg(ii))) + return true; + } + // FIXME: Identify the relevant call body, recursively + // check for partiality. + // return false; + for(FunctionI* b : cg.fun_map.get_bodies(call)) { + // Boolean-typed values are always total + if(b->ti()->type().isbool()) + continue; + if(!b->e()) + return true; + } + return false; + } + case Expression::E_LET: { + Let* let(e->template cast()); + + // Check if any of the expressions are partial. + ASTExprVec bindings(let->let()); + for(Expression* item : bindings) { + if (VarDecl* vd = e->dyn_cast()) { + if(vd->e()) { + // If both a domain and a definition are given, + // the domain might be constraining. + if (vd->ti()->domain()) + return true; + if(is_partial(vd->e())) + return true; + } + } else { + // If there's some item that isn't a binding, it must be a constriant + return true; + } + } + return is_partial(let->in()); + } + case Expression::E_ANON: + case Expression::E_VARDECL: + case Expression::E_TI: + case Expression::E_TIID: + throw InternalError("Bytecode generator encountered unexpected expression type."); + } + } + + void reset_flags(Expression* e) { + /* + if(!e->isUnboxedVal()) { + if(e->_flag_3) { + e->_flag_3 = e->_flag_4 = 0; + } + } + */ + } + + CodeGen& cg; + +}; +#endif + +ASTStSet CodeGen::scope(Expression* e) { + // Is it already cached? + auto it(_exp_scope.find(e)); + if (it != _exp_scope.end()) return (*it).second; + + // Otherwise, dispatch on the type. + ASTStSet r; + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_SETLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + break; + case Expression::E_ID: { + Id* id(e->template cast()); + r.insert(id->v()); + break; + } + case Expression::E_ARRAYLIT: { + ArrayLit* a(e->template cast()); + int sz(a->size()); + for (int ii = 0; ii < sz; ++ii) { + ASTStSet rr(scope((*a)[ii])); + r.insert(rr.begin(), rr.end()); + } + break; + } + case Expression::E_ARRAYACCESS: { + ArrayAccess* a(e->template cast()); + r = scope(a->v()); + + ASTExprVec idx(a->idx()); + int sz(idx.size()); + for (int ii = 0; ii < sz; ++ii) { + ASTStSet rr(scope(idx[ii])); + r.insert(rr.begin(), rr.end()); + } + break; + } + case Expression::E_COMP: { + // Work from the inner expression out. + Comprehension* c(e->template cast()); + r = scope(c->e()); + int sz = c->n_generators(); + for (int g = sz - 1; g >= 0; --g) { + ASTStSet r_in(scope(c->in(g))); + + if (c->where(g)) { + ASTStSet r_where(scope(c->where(g))); + r.insert(r_where.begin(), r_where.end()); + r.insert(r_in.begin(), r_in.end()); + } + + // Now remove the variables that were bound. + for (int d = 0; d < c->n_decls(g); ++d) { + VarDecl* vd(c->decl(g, d)); + r.erase(vd->id()->str()); + } + } + break; + } + case Expression::E_ITE: { + ITE* ite(e->template cast()); + int sz(ite->size()); + r = scope(ite->e_else()); + for (int ii = 0; ii < sz; ++ii) { + ASTStSet rr(scope(ite->e_if(ii))); + r.insert(rr.begin(), rr.end()); + rr = scope(ite->e_then(ii)); + r.insert(rr.begin(), rr.end()); + } + break; + } + case Expression::E_BINOP: { + BinOp* b(e->template cast()); + r = scope(b->lhs()); + ASTStSet rr(scope(b->rhs())); + r.insert(rr.begin(), rr.end()); + break; + } + case Expression::E_UNOP: + r = scope(e->template cast()->e()); + break; + case Expression::E_CALL: { + Call* call(e->template cast()); + int sz = call->n_args(); + for (int ii = 0; ii < sz; ++ii) { + ASTStSet rr(scope(call->arg(ii))); + r.insert(rr.begin(), rr.end()); + } + break; + } + case Expression::E_LET: { + Let* let(e->template cast()); + ASTExprVec bindings(let->let()); + int sz(bindings.size()); + r = scope(let->in()); + for (int ii = sz - 1; ii >= 0; --ii) { + if (VarDecl* vd = bindings[ii]->dyn_cast()) { + r.erase(vd->id()->str()); + if (vd->e()) { + ASTStSet r_d(scope(vd->e())); + r.insert(r_d.begin(), r_d.end()); + } + } else { + ASTStSet r_e(scope(bindings[ii])); + r.insert(r_e.begin(), r_e.end()); + } + } + break; + } + case Expression::E_ANON: + case Expression::E_VARDECL: + case Expression::E_TI: + case Expression::E_TIID: + throw InternalError("Bytecode generator encountered unexpected expression type."); + } + + _exp_scope.insert(std::make_pair(e, r)); + return r; +} + +CodeGen::Binding CodeGen::cache_lookup(Expression* e) { return env().cache_lookup(e, scope(e)); } +void CodeGen::cache_store(Expression* e, CodeGen::Binding l) { env().cache_store(e, scope(e), l); } + +void _debugcond(CG_Cond::T c) { + CG_Cond::_T* p(c.get()); + if (c.sign()) std::cerr << "~"; + if (!p) { + std::cerr << "T"; + } else { + switch (p->kind()) { + case CG_Cond::CC_Reg: + std::cerr << "R" << p->reg[0].reg; + break; + case CG_Cond::CC_Call: + std::cerr << ""; + break; + case CG_Cond::CC_And: + std::cerr << "(and"; + for (CG_Cond::T child : static_cast(p)->children) { + std::cerr << " "; + _debugcond(child); + } + break; + } + } +} +void debugcond(CG_Cond::T c) { + _debugcond(c); + std::cerr << std::endl; +} + +int bind_cst(int x, CodeGen& cg, CG_Builder& frag) { + CG::Binding b; + if (cg.env().cache_lookup_cst(x, b)) return b.first; + int r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(x), CG::r(r)); + cg.env().cache_store_cst(x, std::make_pair(r, CG_Cond::ttt())); + return r; +} + +// WARNING ON THE USE OF CG_Conds: register caching assumes that CG_Conds dont escape an +// aggregation context, and will be available on all paths. If the cond is forced conditionally +// (e.g. shortcutting), cg.push_env() should be called before the force() call, and cg.pop_env() +// called where control flow rejoins. + +// Given CG_Cond cond, collect the disjuncts having positive or negative values. +// Returns false if the conjunction is a contradiction. +bool collect_prod(std::vector& pos, std::vector& neg, + std::vector& delayed, CG_Cond::T cond, CodeGen& cg, + CG_Builder& frag) { + assert(cond.get()); + CG_Cond::_T* p(cond.get()); + bool sign(cond.sign()); + + if (p->reg[1 - sign].is_seen || p->reg[1 - sign].is_root) return false; + if (p->reg[sign].is_seen || p->reg[sign].is_root) return true; + p->reg[sign].is_seen = true; + + if (p->reg[sign].has_reg()) { + pos.push_back(p->reg[sign].reg); + } else if (p->reg[1 - sign].has_reg()) { + neg.push_back(p->reg[1 - sign].reg); + } else if (p->kind() == CG_Cond::CC_And && !sign) { + // Recursively collect the children. + std::vector& children(static_cast(p)->children); + for (CG_Cond::T c : children) { + if (!collect_prod(pos, neg, delayed, c, cg, frag)) return false; + } + } else if (p->kind() == CG_Cond::CC_And) { + // How do we decide which way to compile the remaining conditions? + delayed.push_back(static_cast(p)); + } else { + assert(p->kind() == CG_Cond::CC_Call); + CG_Cond::C_Call* call(static_cast(p)); + std::vector ty(call->ty.begin() + 1, call->ty.end()); + auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->cse), call->params); + int r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { + if (call->ty[0].ispar()) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + } + if (!sign) + pos.push_back(r); + else + neg.push_back(r); + } + return true; +} + +// Given CG_Cond cond, collect the disjuncts having positive or negative values. +// Returns true if the disjunction is a tautology. +bool collect_disj(std::vector& pos, std::vector& neg, + std::vector& delayed, CG_Cond::T cond, CodeGen& cg, + CG_Builder& frag) { + assert(cond.get()); + CG_Cond::_T* p(cond.get()); + bool sign(cond.sign()); + + if (p->reg[1 - sign].is_seen || p->reg[sign].is_root) return true; + if (p->reg[sign].is_seen || p->reg[1 - sign].is_root) return false; + p->reg[sign].is_seen = true; + + if (p->reg[sign].has_reg()) { + pos.push_back(p->reg[sign].reg); + } else if (p->reg[1 - sign].has_reg()) { + neg.push_back(p->reg[1 - sign].reg); + } else if (p->kind() == CG_Cond::CC_And && sign) { + // Recursively collect the children. + std::vector& children(static_cast(p)->children); + for (CG_Cond::T c : children) { + if (collect_disj(pos, neg, delayed, ~c, cg, frag)) return true; + } + } else if (p->kind() == CG_Cond::CC_And) { + delayed.push_back(static_cast(p)); + } else { + CG_Cond::C_Call* call(static_cast(p)); + std::vector ty(call->ty.begin() + 1, call->ty.end()); + auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->cse), call->params); + int r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { + if (call->ty[0].ispar()) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + } + if (!sign) + pos.push_back(r); + else + neg.push_back(r); + } + return false; +} + +std::pair _force_cond(CG_Cond::T cond, CodeGen& cg, CG_Builder& frag); + +void collect_and_leaves(std::vector& par_leaves, std::vector& var_leaves, + CG_Cond::T child, CodeGen& cg, CG_Builder& frag) { + assert(child.get()); + CG_Cond::_T* p(child.get()); + bool sign(child.sign()); + auto push = [&](CG_Cond::T r, bool is_par) { + if (is_par) { + par_leaves.push_back(r); + } else { + var_leaves.push_back(r); + } + }; + + if (p->reg[sign].has_reg()) { + push(child, p->reg[sign].is_par); + return; + } + if (p->reg[1 - sign].has_reg()) { + push(child, p->reg[1 - sign].is_par); + } else if (p->kind() == CG_Cond::CC_And && !sign) { + std::vector& children(static_cast(p)->children); + for (CG_Cond::T c : children) { + collect_and_leaves(par_leaves, var_leaves, c, cg, frag); + } + } else { + push(child, /* Check if this is par. */ false); + } +} +void collect_or_leaves(std::vector& par_leaves, std::vector& var_leaves, + CG_Cond::T child, CodeGen& cg, CG_Builder& frag) { + assert(child.get()); + CG_Cond::_T* p(child.get()); + bool sign(child.sign()); + auto push = [&](CG_Cond::T r, bool is_par) { + if (is_par) { + par_leaves.push_back(r); + } else { + var_leaves.push_back(r); + } + }; + + if (p->reg[1 - sign].has_reg()) { + push(~child, p->reg[1 - sign].is_par); + return; + } + if (p->reg[sign].has_reg()) { + push(~child, p->reg[sign].is_par); + } else if (p->kind() == CG_Cond::CC_And && !sign) { + std::vector& children(static_cast(p)->children); + for (CG_Cond::T c : children) { + collect_or_leaves(par_leaves, var_leaves, c, cg, frag); + } + } else { + push(~child, /* Check if this is par. */ false); + } +} + +void force_and_leaves(std::vector& var_leaves, std::vector& par_leaves, CG_Cond::T child, + CodeGen& cg, CG_Builder& frag) { + assert(child.get()); + CG_Cond::_T* p(child.get()); + bool sign(child.sign()); + auto push = [&](int r, bool is_par) { + if (is_par) { + par_leaves.push_back(r); + } else { + var_leaves.push_back(r); + } + }; + if (p->reg[sign].has_reg()) { + push(p->reg[sign].reg, p->reg[sign].is_par); + return; + } + cg.env().record_cached_cond(child); + if (p->reg[1 - sign].has_reg()) { + // Create the negation + int r(GET_REG(cg)); + if (p->reg[1 - sign].is_par) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r)); + } else { + GCLock lock; + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par); + push(p->reg[sign].reg, p->reg[sign].is_par); + } else if (p->kind() == CG_Cond::CC_And && !sign) { + std::vector& children(static_cast(p)->children); + for (CG_Cond::T c : children) { + force_and_leaves(var_leaves, par_leaves, c, cg, frag); + } + } else { + auto forced = _force_cond(child, cg, frag); + push(forced.first, forced.second); + } +} + +// Pushing the _negation_ of child. +void force_or_leaves(std::vector& var_leaves, std::vector& par_leaves, CG_Cond::T child, + CodeGen& cg, CG_Builder& frag) { + assert(child.get()); + CG_Cond::_T* p(child.get()); + bool sign(child.sign()); + auto push = [&](int r, bool is_par) { + if (is_par) { + par_leaves.push_back(r); + } else { + var_leaves.push_back(r); + } + }; + if (p->reg[1 - sign].has_reg()) { + push(p->reg[1 - sign].reg, p->reg[1 - sign].is_par); + return; + } + cg.env().record_cached_cond(~child); + if (p->reg[sign].has_reg()) { + // Create the negation + int r(GET_REG(cg)); + if (p->reg[sign].is_par) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[sign].reg), CG::r(r)); + } else { + GCLock lock; + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(p->reg[sign].reg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + p->reg[1 - sign] = CG_Cond::cond_reg(r, p->reg[sign].is_par); + push(p->reg[1 - sign].reg, p->reg[1 - sign].is_par); + } else if (p->kind() == CG_Cond::CC_And && !sign) { + std::vector& children(static_cast(p)->children); + for (CG_Cond::T c : children) { + force_or_leaves(var_leaves, par_leaves, c, cg, frag); + } + } else { + auto forced = _force_cond(~child, cg, frag); + push(forced.first, forced.second); + } +} + +std::pair _force_cond(CG_Cond::T cond, CodeGen& cg, CG_Builder& frag) { + CG_Cond::_T* p(cond.get()); + bool negated(cond.sign()); + if (!p) { + // Either true or false. + // return CG::locate_immi(1 - cond.sign(), cg, frag); + return {bind_cst(1 - cond.sign(), cg, frag), true}; + } + cg.env().record_cached_cond(cond); + if (p->kind() == CG_Cond::CC_Reg) { + throw InternalError("_force_cond called on value in register."); + } else if (p->kind() == CG_Cond::CC_Call) { + CG_Cond::C_Call* call(static_cast(p)); + std::vector ty(call->ty.begin() + 1, call->ty.end()); + int r; + Mode m(call->m); + if (m.strength() != CG::Mode::Root) { + Mode call_m(m.strength(), negated); + auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->cse), call->params); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { + if (call->ty[0].ispar()) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + } + return {r, call->ty[0].ispar()}; + } + Mode call_m(m.strength(), negated); + assert(m == call_m); + auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call->m); + if (call_m == std::get<1>(fun)) { + PUSH_INSTR(frag, BytecodeStream::CALL, call_m, std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->cse), call->params); + } else { + assert(std::get<1>(fun) == BytecodeProc::FUN); + std::cerr << "Warning: emitting reification for " << call->ident + << " because no ROOT_NEG version is available\n"; + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), call->params); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { + if (call->ty[0].ispar()) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + } + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r)); + } + return {bind_cst(1, cg, frag), true}; + } else if (p->kind() == CG_Cond::CC_And && !cond.sign()) { +#ifndef MZNC_COLLECT_LEAVES + std::vector var_leaves; + std::vector par_leaves; + int r = GET_REG(cg); + // I don't think these are necessary + force_and_leaves(var_leaves, par_leaves, cond, cg, frag); + if (var_leaves.empty()) { + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); + for (int i = 1; i < par_leaves.size(); ++i) { + PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); + } + } else { + OPEN_AND(cg, frag); + for (int r_c : par_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + for (int r_c : var_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + return {r, var_leaves.empty()}; +#else + std::vector par_leaves; + std::vector var_leaves; + collect_and_leaves(par_leaves, var_leaves, cond, cg, frag); + + // If we had a conjunction, there should be at least two leaves. + assert(var_leaves.size() + par_leaves.size() > 1); + + // If there is at least one par child, we're going to do shortcutting + // -- so we need a jump, and to push a fresh env. + int r = GET_REG(cg); + int lblE = 0xdeadbeef; + if (par_leaves.size() > 0) { + lblE = GET_LABEL(cg); + cg.env_push(); + + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); + for (int ii = 0; ii < par_leaves.size() - 1; ii++) { + CG_Cond::T c(par_leaves[ii]); + int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(lblE)); + } + CG_Cond::T c(par_leaves.back()); + int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); + if (var_leaves.size() > 0) { + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(lblE)); + } else { + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); + } + } + // Excludes the 0 case, because it's just been handled. + if (var_leaves.size() == 1) { + // Exactly one var. Don't open a context; + // we return either the result register + // or the forced cond. + int r_c = CG::force(var_leaves[0], BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); + } else if (var_leaves.size() > 1) { + // In this case, we need to open a context. + OPEN_AND(cg, frag); + for (CG_Cond::T c : var_leaves) { + int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + if (par_leaves.size() > 0) { // We did some shortcutting. + PUSH_LABEL(frag, lblE); + cg.env_pop(); + } + return {r, var_leaves.empty()}; +#endif + } else { + assert(p->kind() == CG_Cond::CC_And && cond.sign()); +#ifndef MZNC_COLLECT_LEAVES + std::vector var_leaves; + std::vector par_leaves; + int r = GET_REG(cg); + // I don't think these are necessary + force_or_leaves(var_leaves, par_leaves, ~cond, cg, frag); + if (var_leaves.empty()) { + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); + for (int i = 1; i < par_leaves.size(); ++i) { + PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); + } + } else { + OPEN_OR(cg, frag); + for (int r_c : par_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + for (int r_c : var_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + return {r, var_leaves.empty()}; +#else + std::vector par_leaves; + std::vector var_leaves; + collect_or_leaves(par_leaves, var_leaves, ~cond, cg, frag); + + // If we had a conjunction, there should be at least two leaves. + assert(var_leaves.size() + par_leaves.size() > 1); + + // If there is at least one par child, we're going to do shortcutting + // -- so we need a jump, and to push a fresh env. + int r = GET_REG(cg); + int lblE = 0xdeadbeef; + if (par_leaves.size() > 0) { + lblE = GET_LABEL(cg); + cg.env_push(); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r)); + + for (int ii = 0; ii < par_leaves.size() - 1; ii++) { + CG_Cond::T c(par_leaves[ii]); + int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_c), CG::l(lblE)); + } + CG_Cond::T c(par_leaves.back()); + int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); + if (var_leaves.size() > 0) { + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_c), CG::l(lblE)); + } else { + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); + } + } + // Excludes the 0 case, because it's just been handled. + if (var_leaves.size() == 1) { + // Exactly one var. Don't open a context; + // we return either the result register + // or the forced cond. + int r_c = CG::force(var_leaves[0], BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_c), CG::r(r)); + } else if (var_leaves.size() > 1) { + // In this case, we need to open a context. + OPEN_OR(cg, frag); + for (CG_Cond::T c : var_leaves) { + int r_c = CG::force(c, BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + if (par_leaves.size() > 0) { // We did some shortcutting. + PUSH_LABEL(frag, lblE); + cg.env_pop(); + } + return {r, var_leaves.empty()}; +#endif + } +} +int CG::force(CG_Cond::T _cond, Mode ctx, CodeGen& cg, CG_Builder& frag) { + GCLock lock; + // Negate condition if forced in negated context + CG_Cond::T cond = ctx.is_neg() ? ~_cond : _cond; + // Check if the condition is already forced. + CG_Cond::_T* p(cond.get()); + bool sign(cond.sign()); + if (!p) { + return bind_cst(!sign, cg, frag); + } + if (p->reg[sign].has_reg()) { + return p->reg[sign].reg; + } + if (p->reg[1 - sign].has_reg()) { + // Emit the negation + int r(GET_REG(cg)); + if (p->reg[1 - sign].is_par) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par); + return r; + } + auto forced = _force_cond(cond, cg, frag); + p->reg[sign] = CG_Cond::cond_reg(forced.first, forced.second); + return forced.first; +} + +CG::Binding CG::force_or_bind(Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag) { + if (e->type().isbool()) { + return {CG::force(CG::compile(e, cg, frag), ctx, cg, frag), CG_Cond::ttt()}; + } + return CG::bind(e, cg, frag); +} + +int CG::force_or_bind(Expression* e, Mode ctx, std::vector& cond, CodeGen& cg, + CG_Builder& frag) { + if (e->type().isbool()) { + return CG::force(CG::compile(e, cg, frag), ctx, cg, frag); + } else { + CG::Binding b(CG::bind(e, cg, frag)); + if (b.second.get()) { + cond.push_back(b.second); + } + return b.first; + } +} + +class EnvInit : public ItemVisitor { +private: + friend class ItemIter; + + EnvInit(CodeGen& _cg, Model& _m) : cg(_cg), m(_m) {} + + /// Enter model + bool enterModel(Model* m) { return true; } + /// Enter item + bool enter(Item* m) { return true; } + /// Visit variable declaration + void vVarDeclI(VarDeclI* vdi) { + VarDecl* vd(vdi->e()); + if (!vd->type().isann()) { + if (vd->type().ispar()) { + if (!vd->e()) { + // cg.env().bind(vd->id()->v(), Loc::global(cg.num_globals)); + int g = cg.add_global(vd, true); + } + } else { + // If it's a var with a body, feed it into the mode analyser. + // TODO: Because mode analysis is not interprocedural, we have to + // assume global params may be used in any context. + modes.def(vd, BytecodeProc::ROOT); + modes.use(vd, BytecodeProc::FUN); + } + } + } + void vConstraintI(ConstraintI* c) { modes.use(c->e(), BytecodeProc::ROOT); } + /// Visit assign item + void vAssignI(AssignI* ass) {} + + void vFunctionI(FunctionI* f) { cg.register_function(f); } + + void vSolveI(SolveI* si) { + GCLock lock; + ASTString ident("solve_this"); + int mode; + Expression* objective; + switch (si->st()) { + case SolveI::ST_SAT: + mode = 0; + objective = IntLit::a(0); + break; + case SolveI::ST_MIN: + mode = 1; + objective = si->e(); + break; + case SolveI::ST_MAX: + mode = 2; + objective = si->e(); + break; + } + Expression* search_a; + int search_var = 0; + int search_val = 0; + Call* c; + if (Call* ann = si->ann().getCall(ASTString("int_search"))) { + search_a = ann->arg(0); + Id* varsel = ann->arg(1)->cast(); + if (varsel->idn() == -1 && varsel->v() == ASTString("input_order")) { + search_var = 1; + } else if (varsel->idn() == -1 && varsel->v() == ASTString("first_fail")) { + search_var = 2; + } + Id* valsel = ann->arg(2)->cast(); + if (valsel->idn() == -1 && valsel->v() == ASTString("indomain_min")) { + search_val = 1; + } else if (valsel->idn() == -1 && valsel->v() == ASTString("indomain_max")) { + search_val = 2; + } + c = new Call( + si->loc(), ident, + {IntLit::a(mode), objective, search_a, IntLit::a(search_var), IntLit::a(search_val)}); + } else { + c = new Call(si->loc(), ident, {IntLit::a(mode), objective}); + } + c->type(Type::varbool()); + m.addItem(new ConstraintI(Location().introduce(), c)); + } + + CodeGen& cg; + Model& m; + ModeAnalysis modes; + +public: + static void run(CodeGen& cg, Model* m) { + EnvInit eb(cg, *m); + iterItems(eb, m); + cg.mode_map = std::move(eb.modes.extract()); + } +}; + +struct ShowVal { + ShowVal(CodeGen& _cg, CG_Value _v) : cg(_cg), v(_v) {} + + CodeGen& cg; + CG_Value v; +}; +std::ostream& operator<<(std::ostream& o, ShowVal s) { + switch (s.v.kind) { + case CG_Value::V_Immi: + o << s.v.value; + break; + case CG_Value::V_Global: + o << s.v.value; + break; + case CG_Value::V_Reg: + o << "R" << s.v.value; + break; + case CG_Value::V_Proc: + o << "p" << s.v.value; + break; + case CG_Value::V_Label: + o << "l" << s.v.value; + } + return o; +} + +template +void show_frag(O& out, CodeGen& cg, std::vector& frag) { + auto show = [&cg](CG_Value v) { return ShowVal(cg, v); }; + + int num_agg = 0; + for (CG_Instr& i : frag) { + auto op(static_cast(i.tag >> 1)); + if (op == BytecodeStream::CLOSE_AGGREGATION) { + num_agg--; + } + for (int j = 0; j < num_agg; ++j) { + out << " "; + } + if (i.tag & 1) { + out << "l" << (i.tag >> 1) << ": "; + continue; + } + + out << instr_name(op); + switch (op) { + case BytecodeStream::OPEN_AGGREGATION: + out << " " << agg_name((AggregationCtx::Symbol)i.params[0].value); + num_agg++; + break; + case BytecodeStream::BUILTIN: { + CG_ProcID p(CG_ProcID::of_val(i.params[0])); + assert(p.is_builtin()); + out << " " << cg._builtins[p.id()].first; + for (int ii = 1; ii < i.params.size(); ++ii) { + out << " " << show(i.params[ii]); + } + break; + } + case BytecodeStream::CALL: { + // Mode + out << " " << mode_name((BytecodeProc::Mode)i.params[0].value); + // Procedure + CG_ProcID p(CG_ProcID::of_val(i.params[1])); + if (p.is_builtin()) { + out << " " << cg._builtins[p.id()].first; + } else { + out << " " << cg.bytecode[p.id()].ident; + } + // CSE flag + out << " " << show(i.params[2]); + // Arguments + for (int ii = 3; ii < i.params.size(); ++ii) { + out << " " << show(i.params[ii]); + } + break; + } + case BytecodeStream::TCALL: { + // Mode + out << " " << mode_name((BytecodeProc::Mode)i.params[0].value); + // Procedure + CG_ProcID p(CG_ProcID::of_val(i.params[1])); + if (p.is_builtin()) { + out << " " << cg._builtins[p.id()].first; + } else { + out << " " << cg.bytecode[p.id()].ident; + } + // CSE flag + out << " " << show(i.params[2]); + break; + } + default: + for (CG_Value p : i.params) out << " " << show(p); + } + out << std::endl; + } +} + +template +void show(O& out, CodeGen& cg) { + { + GCLock lock; + Model m; + for (auto g : cg.globals_env) { + if (g.second.second) { + TypeInst* ti = g.first->ti(); + // TODO: This removes information from the origin model. Could be reverted after printing + if (ti->isarray()) { + std::vector ranges(ti->ranges().size()); + for (int i = 0; i < ranges.size(); ++i) { + ranges[i] = new TypeInst(Location().introduce(), Type::parint()); + } + auto nti = new TypeInst(Location().introduce(), ti->type(), ranges); + g.first->ti(nti); + } else if (ti->domain()) { + auto nti = new TypeInst(Location().introduce(), ti->type()); + g.first->ti(nti); + } + g.first->ann().add(new Call(Location().introduce(), constants().ann.global_register, + {IntLit::a(g.second.first)})); + m.addItem(new VarDeclI(Location().introduce(), g.first)); + } + } + for (auto fun : cg.req_solver_predicates) { + m.addItem(fun); + } + MiniZinc::Printer p(out, 0); + p.print(&m); + } + out << "@@@@@@@@@@" << std::endl; + for (auto b : cg._builtins) { + out << ":" << b.first << ": " << b.second << std::endl; + } + for (auto& p : cg.bytecode) { + for (BytecodeProc::Mode m : p) { + out << ":" << p.ident << ":" << mode_name(m) << " " << p.arity << std::endl; + show_frag(out, cg, p.body(m)); + } + } +} + +/* +void post_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) { + if(!cond.get()) { + assert(!cond.sign()); + return; + } + + std::vector leaves; + force_and_leaves(leaves, cond, cg, frag); + for(int r_c : leaves) + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r_c)); +} +*/ +void post_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) { + GCLock lock; + if (!cond.get()) { + if (cond.sign()) PUSH_INSTR(frag, BytecodeStream::POST, CG::r(bind_cst(0, cg, frag))); + return; + } + CG_Cond::_T* p(cond.get()); + bool sign(cond.sign()); + if (p->reg[sign].is_root) return; + if (p->reg[1 - sign].is_root) { + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(bind_cst(0, cg, frag))); + return; + } + + if (p->reg[sign].has_reg()) { + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(p->reg[sign].reg)); + } else if (p->reg[1 - sign].has_reg()) { + int r(GET_REG(cg)); + if (p->reg[1 - sign].is_par) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(p->reg[1 - sign].reg), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(p->reg[1 - sign].reg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r)); + p->reg[sign] = CG_Cond::cond_reg(r, p->reg[1 - sign].is_par); + } else if (p->kind() == CG_Cond::CC_Call) { + CG_Cond::C_Call* call(static_cast(p)); + CG::Mode call_m(CG::Mode::Root, sign); + std::vector ty(call->ty.begin() + 1, call->ty.end()); + auto fun = find_call_fun(cg, call->ident, call->ty[0], ty, call_m); + if (call_m == std::get<1>(fun)) { + PUSH_INSTR(frag, BytecodeStream::CALL, call_m, std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->cse), call->params); + } else { + assert(std::get<1>(fun) == BytecodeProc::FUN); + std::cerr << "Warning: emitting reification for " << call->ident + << " because no ROOT_NEG version is available\n"; + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->cse), call->params); + int r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (BytecodeProc::is_neg(call->m) != BytecodeProc::is_neg(std::get<1>(fun))) { + if (call->ty[0].ispar()) { + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r), CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + } + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(r)); + } + } else { + assert(p->kind() == CG_Cond::CC_And); + CG_Cond::C_And* conj(reinterpret_cast(p)); + if (!sign) { + // Recurse. + for (CG_Cond::T child : conj->children) post_cond(cg, frag, child); + } else { + // FIXME: Check force context. + PUSH_INSTR(frag, BytecodeStream::POST, CG::r(CG::force(cond, BytecodeProc::FUN, cg, frag))); + } + } + p->reg[sign].is_root = true; +} + +// FIXME: This always forces calls outside the aggregation. +void aggregate_cond(CodeGen& cg, CG_Builder& frag, CG_Cond::T cond) { + CG_Cond::_T* p(cond.get()); + bool sign(cond.sign()); + if (!p) { + // int r_val(CG::locate_immi(1 - sign, cg, frag)); + int r_val(bind_cst(1 - sign, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_val)); + return; + } + if (p->reg[sign].has_reg()) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(p->reg[sign].reg)); + return; + } else if (p->reg[1 - sign].has_reg()) { + int r_neg(p->reg[1 - sign].reg); + if (p->reg[1 - sign].is_par) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_neg), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + } else { + auto fun = + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_neg)); + } + return; + } + std::vector var_leaves; + std::vector par_leaves; + if (p->kind() == CG_Cond::CC_And) { + if (!sign) { + force_and_leaves(var_leaves, par_leaves, cond, cg, frag); + int r; + if (var_leaves.empty()) { + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); + for (int i = 1; i < par_leaves.size(); ++i) { + PUSH_INSTR(frag, BytecodeStream::AND, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + } else { + OPEN_AND(cg, frag); + for (int r_c : par_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + for (int r_c : var_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + CLOSE_AGG(cg, frag); + } + } else { + force_or_leaves(var_leaves, par_leaves, ~cond, cg, frag); + int r; + if (var_leaves.empty()) { + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(par_leaves[0]), CG::r(r)); + for (int i = 1; i < par_leaves.size(); ++i) { + PUSH_INSTR(frag, BytecodeStream::OR, CG::r(r), CG::r(par_leaves[i]), CG::r(r)); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + } else { + OPEN_OR(cg, frag); + for (int r_c : par_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + for (int r_c : var_leaves) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + CLOSE_AGG(cg, frag); + } + } + } else { + // FIXME: Check force context + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(CG::force(cond, BytecodeProc::FUN, cg, frag))); + } +} + +int locate_range(int l, int u, CodeGen& cg, CG_Builder& frag) { + CG::Binding b; + if (cg.env().cache_lookup_range(l, u, b)) { + return b.first; + } + if (l == 0 && u == 1) { + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("boolean_domain")); + int r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + cg.env().cache_store_range(l, u, std::make_pair(r, CG_Cond::ttt())); + return r; + } + + OPEN_VEC(cg, frag); + int r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(l), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(u), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + CLOSE_AGG(cg, frag); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + cg.env().cache_store_range(l, u, std::make_pair(r, CG_Cond::ttt())); + return r; +} + +CG::Binding bind_domain(VarDecl* vd, CodeGen& cg, CG_Builder& frag) { + if (vd->type().bt() == Type::BT_BOOL) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("boolean_domain")); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return {r, CG_Cond::ttt()}; + } + if (Expression* d = vd->ti()->domain()) { + // Ignoring partiality here. + return CG::bind(d, cg, frag); + } + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinite_domain")); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return {r, CG_Cond::ttt()}; +} + +class Compile : public ItemVisitor { +private: + friend class ItemIter; + + Compile(CodeGen& _cg) : cg(_cg) /*, bool_dom(-1) */ { + // Force the compilation of predicate definitions that can be generated by the compiler + find_call_fun(cg, {"op_not"}, Type::varbool(), {Type::varbool()}, BytecodeProc::FUN); + find_call_fun(cg, {"bool_clause"}, Type::varbool(), {Type::varbool(1), Type::varbool(1)}, + BytecodeProc::ROOT, true); + find_call_fun(cg, {"bool_clause_reif"}, Type::varbool(), + {Type::varbool(1), Type::varbool(1), Type::varbool()}, BytecodeProc::ROOT, true); + find_call_fun(cg, {"int_lin_eq"}, Type::varbool(), + {Type::parint(1), Type::varint(1), Type::parint()}, BytecodeProc::ROOT, true); + } + + /// Enter model + bool enterModel(Model* m) { return true; } + /// Enter item + bool enter(Item* m) { return true; } + /// Visit variable declaration + void vVarDeclI(VarDeclI* vdi) { + VarDecl* vd(vdi->e()); + if (vd->type().isann()) return; + if (vd->type().isopt()) return; + if (vd->type().isvar()) { + // In whatever case, we're going to create something, + // and dump it in a register. + int r_var; + if (vd->e()) { + // Defined + // Evaluate the definition in root context, + // add it to a register + // r_var = CG::locate(vd->e(), BytecodeProc::ROOT, cg, root_frag); + CG::Binding b_var(CG::bind(vd->e(), cg, root_frag)); + r_var = b_var.first; + // TODO: Special case for root stuff. + post_cond(cg, root_frag, b_var.second); + + if (Expression* d = vd->ti()->domain()) { + if (!vd->ti()->isarray()) { + CG::Binding b_d = CG::bind(d, cg, root_frag); + int r_dp = GET_REG(cg); + PUSH_INSTR(root_frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(r_var), CG::r(b_d.first), + CG::r(r_dp)); + post_cond(cg, root_frag, b_d.second); + } else { + // Iterate over the elements of the variable we just created, and + // bind the corresponding domain. + CG::Binding b_d = CG::bind(d, cg, root_frag); + int r_dp = GET_REG(cg); + ITER_ARRAY(cg, root_frag, r_var, [b_d, r_dp](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(r_elt), CG::r(b_d.first), + CG::r(r_dp)); + }); + post_cond(cg, root_frag, b_d.second); + } + } + } else { + CG::Binding b_d(bind_domain(vd, cg, root_frag)); + post_cond(cg, root_frag, b_d.second); + int r_d = b_d.first; + + r_var = GET_REG(cg); + if (vd->ti()->isarray()) { + // Open nested iterators + int rTMP(GET_REG(cg)); + std::vector r_regs; + for (Expression* r : vd->ti()->ranges()) { + Expression* dim(r->template cast()->domain()); + CG::Binding b_reg(CG::bind(dim, cg, root_frag)); + post_cond(cg, root_frag, b_reg.second); + r_regs.push_back(b_reg.first); + } + std::vector nesting; + OPEN_VEC(cg, root_frag); + for (int r_r : r_regs) { + Forset iter(cg, r_r); + nesting.push_back(iter); + iter.emit_pre(root_frag); + } + PUSH_INSTR(root_frag, BytecodeStream::CALL, BytecodeProc::ROOT, + cg.find_builtin("mk_intvar"), CG::i(0), CG::r(r_d)); + for (int r_i = r_regs.size() - 1; r_i >= 0; --r_i) { + nesting[r_i].emit_post(root_frag); + } + CLOSE_AGG(cg, root_frag); + + PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(r_var)); + OPEN_VEC(cg, root_frag); + for (int r_r : r_regs) { + PUSH_INSTR(root_frag, BytecodeStream::GET_VEC, CG::r(r_r), + CG::r(bind_cst(1, cg, root_frag)), CG::r(rTMP)); + PUSH_INSTR(root_frag, BytecodeStream::PUSH, CG::r(rTMP)); + PUSH_INSTR(root_frag, BytecodeStream::GET_VEC, CG::r(r_r), + CG::r(bind_cst(2, cg, root_frag)), CG::r(rTMP)); + PUSH_INSTR(root_frag, BytecodeStream::PUSH, CG::r(rTMP)); + } + CLOSE_AGG(cg, root_frag); + PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(rTMP)); + + PUSH_INSTR(root_frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r_var), + CG::r(rTMP)); + } else { + PUSH_INSTR(root_frag, BytecodeStream::CALL, BytecodeProc::ROOT, + cg.find_builtin("mk_intvar"), CG::i(0), CG::r(r_d)); + } + PUSH_INSTR(root_frag, BytecodeStream::POP, CG::r(r_var)); + } + // Now copy it into a global, and add it to the env. + int g = cg.add_global(vd, false); + PUSH_INSTR(root_frag, BytecodeStream::STORE_GLOBAL, CG::r(r_var), CG::g(g)); + + // Since it's still in a register, add it to the current env as well. + cg.env().bind(vd->id()->str(), CG::Binding(r_var, CG_Cond::ttt())); + } else { + // For par identifiers with definitions, we evaluate them. + if (vd->e() && !vd->type().isann()) { + // Evaluate the definition. + // int r = vd->type().ispar() ? CG::locate_par(vd->e(), cg, root_frag) : CG::locate(vd->e(), + // BytecodeProc::ROOT, cg, root_frag); + int r; + if (vd->type().isbool()) { + r = CG::force(CG::compile(vd->e(), cg, root_frag), BytecodeProc::ROOT, cg, root_frag); + } else { + // Par expressions may still introduce constraints. + CG::Binding b_d = CG::bind(vd->e(), cg, root_frag); + post_cond(cg, root_frag, b_d.second); + r = b_d.first; + } + int g = cg.add_global(vd, false); + PUSH_INSTR(root_frag, BytecodeStream::STORE_GLOBAL, CG::r(r), CG::g(g)); + + cg.env().bind(vd->id()->str(), CG::Binding(r, CG_Cond::ttt())); + } + } + } + + /// Visit assign item + void vAssignI(AssignI* ass) {} + + void vConstraintI(ConstraintI* c) { + // CG::eval(c->e(), BytecodeProc::ROOT, cg, root_frag); + post_cond(cg, root_frag, CG::compile(c->e(), cg, root_frag)); + } + + void vFunctionI(FunctionI* f) { + GCLock l; + if (f->ann().contains(constants().ann._export)) { + CG_ProcID body(cg.resolve_fun(f)); + // Force the body to be created + if (!body.is_builtin()) { + assert(body.id() < cg.bytecode.size()); + if (!cg.bytecode[body.id()].is_available(BytecodeProc::ROOT)) { + cg.bytecode[body.id()].body(BytecodeProc::ROOT); + cg.pending_bodies.push_back( + std::make_pair(f, std::make_pair(BytecodeProc::ROOT, BytecodeProc::ROOT))); + } + } + } + } + + // FIXME: This method of saving the CodeGen state is pretty icky. + // CodeGen should probably be split into two objects. + void compile_fun(CG_Builder& frag, const ASTExprVec& params, Expression* e) { + // Save the codegen state. + auto saved_env = cg.current_env; + cg.current_env = CG_Env::spawn(nullptr); + int saved_regs = cg.current_reg_count; + + cg.current_reg_count = params.size(); + + cg._exp_scope.clear(); + cg.mode_map.clear(); + + // Rerun mode analysis + ModeAnalysis modes; + // FIXME: Currently assuming all functions are total. + modes.def(e, BytecodeProc::ROOT); + modes.use(e, BytecodeProc::ROOT); + cg.mode_map = std::move(modes.extract()); + + // Set up the new env. + { + GCLock l; + for (int ii = 0; ii < params.size(); ++ii) { + cg.env().bind(params[ii]->id()->str(), CG::Binding(ii, CG_Cond::ttt())); + } + } + + // Now compile the result. + CG::Binding b_res = CG::bind(e, cg, frag); + if (b_res.second.p) { + // FIXME: What actually happens with this forced result? + // FIXME: Check force context + CG::force(b_res.second, BytecodeProc::FUN, cg, frag); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_res.first)); + PUSH_INSTR(frag, BytecodeStream::RET); + + // now restore everything + cg.current_reg_count = saved_regs; + + delete cg.current_env; + cg.current_env = saved_env; + } + + void compile_pred(CG_Builder& frag, const ASTExprVec& params, Mode m, Expression* e) { + // Save the codegen state. + auto saved_env = cg.current_env; + cg.current_env = CG_Env::spawn(nullptr); + int saved_regs = cg.current_reg_count; + + cg.current_reg_count = params.size(); + + cg._exp_scope.clear(); + cg.mode_map.clear(); + + // Rerun mode analysis + ModeAnalysis modes; + modes.use(e, m); + cg.mode_map = std::move(modes.extract()); + + // Set up the new env. + { + GCLock l; + for (int ii = 0; ii < params.size(); ++ii) { + cg.env().bind(params[ii]->id()->str(), CG::Binding(ii, CG_Cond::ttt())); + } + } + + // Now compile the result. + CG_Cond::T cond(CG::compile(e, cg, frag)); + if (m.strength() == Mode::Root) { + post_cond(cg, frag, m.is_neg() ? ~cond : cond); + } else { + aggregate_cond(cg, frag, m.is_neg() ? ~cond : cond); + } + PUSH_INSTR(frag, BytecodeStream::RET); + + cg.current_reg_count = saved_regs; + // then restore everything. + delete cg.current_env; + cg.current_env = saved_env; + } + CodeGen& cg; + CG_Builder root_frag; + + // int bool_dom; +public: + static void run(CodeGen& cg, Model* m) { + Compile c(cg); + // Compile the main model + OPEN_OTHER(cg, c.root_frag); + iterItems(c, m); + { + int old_reg_count = cg.current_reg_count; + cg.current_reg_count = cg.reg_trail.back(); + cg.reg_trail.pop_back(); + PUSH_INSTR(c.root_frag, BytecodeStream::CLEAR, CG::r(cg.current_reg_count), + CG::r(old_reg_count - 1)); + cg.env_pop(); + } + PUSH_INSTR(c.root_frag, BytecodeStream::RET); + + // Now generate procedures for any necessary function/predicate bodies. + while (!cg.pending_bodies.empty()) { + GCLock lock; + auto p(cg.pending_bodies.back()); + cg.pending_bodies.pop_back(); + + FunctionI* fun(p.first); + Mode call_mode(p.second.first); + Mode def_mode(p.second.second); + annotate_total(fun); + // Building structures + CG_ProcID proc(cg.resolve_fun(fun)); + CG_Builder frag; + // Find the body. + std::vector arg_types(fun->params().size()); + for (int j = 0; j < arg_types.size(); ++j) { + arg_types[j] = fun->params()[j]->type(); + } + + // Introduce reification if necessary + if (call_mode == BytecodeProc::IMP || call_mode == BytecodeProc::FUN || + call_mode == BytecodeProc::IMP_NEG || call_mode == BytecodeProc::FUN_NEG) { + auto redef = cg.fun_map.defines_mode(fun->id(), arg_types, call_mode); + if (redef.first) { + std::vector args; + args.reserve(fun->params().size() + 1); + for (int i = 0; i < fun->params().size(); ++i) { + VarDecl* vd = fun->params()[i]; + args.emplace_back(vd->id()); + } + TypeInst var_bool(Location().introduce(), Type::varbool()); + VarDecl new_var(Location().introduce(), &var_bool, "b"); + args.emplace_back(new_var.id()); + Call call(Location().introduce(), redef.second, args); + call.type(Type::varbool()); + Let let(Location().introduce(), {&new_var, &call}, new_var.id()); + let.type(Type::varbool()); + let.addAnnotation(constants().ann.promise_total); + c.compile_pred(frag, fun->params(), call_mode, &let); + cg.append(proc.id(), call_mode, frag); + continue; + } + } + if (BytecodeProc::is_neg(call_mode)) { + GCLock lock; + ASTString ident(fun->id().str() + "_neg"); + auto neg_bodies = std::move(cg.fun_map.get_bodies(ident, arg_types)); + if (std::any_of(neg_bodies.begin(), neg_bodies.end(), + [](FunctionI* fi) { return fi->e(); })) { + std::vector args; + args.reserve(fun->params().size()); + for (int i = 0; i < fun->params().size(); ++i) { + VarDecl* vd = fun->params()[i]; + args.emplace_back(vd->id()); + } + Call call(Location().introduce(), ident, args); + call.type(Type::varbool()); + // Call negated constraint in positive context (implementation should deal with the + // negation). + c.compile_pred(frag, fun->params(), BytecodeProc::negate(call_mode), &call); + cg.append(proc.id(), call_mode, frag); + continue; + } + } + + if (fun->e()) { + if (fun->e()->type().isbool()) { + c.compile_pred(frag, fun->params(), def_mode, fun->e()); + } else { + assert(def_mode == BytecodeProc::ROOT); + c.compile_fun(frag, fun->params(), fun->e()); + } + } else { + assert(call_mode == BytecodeProc::ROOT); + cg.req_solver_predicates.push_back(fun); + } + cg.append(proc.id(), call_mode, frag); + } + + // And finally, add the entry function. + int main_proc = cg.bytecode.size(); + cg.bytecode.emplace_back("main", 0); + cg.append(main_proc, BytecodeProc::ROOT, c.root_frag); + + show(std::cout, cg); + } +}; + +Mode open_conj(Mode ctx, CG_Builder& frag) { + if (ctx == BytecodeProc::ROOT) return ctx; + + PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, + ctx.is_neg() ? AggregationCtx::VCTX_OR : AggregationCtx::VCTX_AND); + return +ctx; +} +void close_conj(Mode ctx, CG_Builder& frag) { + if (ctx != BytecodeProc::ROOT) PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); +} +Mode open_disj(Mode ctx, CG_Builder& frag) { + if (ctx == BytecodeProc::ROOT_NEG) return ctx; + + PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, + ctx.is_neg() ? AggregationCtx::VCTX_AND : AggregationCtx::VCTX_OR); + return +ctx; +} +void close_disj(Mode ctx, CG_Builder& frag) { + if (ctx != BytecodeProc::ROOT_NEG) PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); +} + +Mode left_child_ctx(Mode ctx, BinOpType b) { + switch (b) { + // Only Boolean operators change the mode. + case BOT_AND: + return ctx.is_neg() ? +ctx : ctx; + case BOT_OR: + case BOT_RIMPL: + return ctx.is_neg() ? ctx : +ctx; + case BOT_IMPL: + return ctx.is_neg() ? -ctx : -(+ctx); + case BOT_EQUIV: + case BOT_XOR: + return *ctx; + default: + return ctx; + } +} +Mode right_child_ctx(Mode ctx, BinOpType b) { + switch (b) { + // Only Boolean operators change the mode. + case BOT_AND: + return ctx.is_neg() ? +ctx : ctx; + case BOT_OR: + case BOT_IMPL: + return ctx.is_neg() ? ctx : +ctx; + case BOT_RIMPL: + return ctx.is_neg() ? -ctx : -(+ctx); + case BOT_EQUIV: + case BOT_XOR: + return *ctx; + default: + return ctx; + } +} +Mode child_ctx(Mode ctx, UnOpType u) { + switch (u) { + case UOT_NOT: + return -ctx; + default: + return ctx; + } +} + +void CG::run(CodeGen& cg, Model* m) { + // First, collect the model parameters, and assign them + // global slots. + EnvInit::run(cg, m); + Compile::run(cg, m); +} + +// For a non-Boolean value, place it in a register and collect its partiality. +std::pair _bind(Expression* e, CodeGen& cg, CG_Builder& frag) { + // Look up the mode we need to compile e in. + // FIXME: Figure out why some expressions don't have a mode attached. + CG::Mode ctx(BytecodeProc::FUN); + try { + ctx = cg.mode_map.at(e); + } catch (const std::out_of_range& exn) { + } + + switch (e->eid()) { + case Expression::E_INTLIT: { + // return std::make_pair(CG::locate_immi(e->template cast()->v().toInt(), cg, frag), + // CG_Cond::ttt()); + IntVal i = e->cast()->v(); + if (!i.isFinite()) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(i.isPlusInfinity(), cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return {r, CG_Cond::ttt()}; + } + return {bind_cst(i.toInt(), cg, frag), CG_Cond::ttt()}; + } + case Expression::E_FLOATLIT: + // return std::make_pair(CG::locate_par(e->template cast(), cg, frag), nullptr); + // NOT YET IMPLEMENTED + return CG::Binding(bind_cst(911911, cg, frag), CG_Cond::ttt()); + case Expression::E_SETLIT: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_BOOLLIT: + throw InternalError("bind called on Boolean expression."); + case Expression::E_STRINGLIT: + // NOT YET IMPLEMENTED + return CG::Binding(bind_cst(911911, cg, frag), CG_Cond::ttt()); + case Expression::E_ID: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_ANON: + throw InternalError("bind reached unexpected expression type: E_ANON."); + case Expression::E_ARRAYLIT: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_ARRAYACCESS: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_COMP: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_ITE: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_BINOP: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_UNOP: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_CALL: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_LET: + return CG::bind(e->template cast(), ctx, cg, frag); + case Expression::E_VARDECL: + case Expression::E_TI: + case Expression::E_TIID: + throw InternalError("Bytecode generator encountered unexpected expression type in bind."); + } +} + +CG::Binding CG::bind(Expression* e, CodeGen& cg, CG_Builder& frag) { + try { + return cg.cache_lookup(e); + } catch (const CG_Env::NotFound& exn) { + CG::Binding b(_bind(e, cg, frag)); + cg.cache_store(e, b); + return b; + } + // return _bind(e, cg, frag); // FIXME +} + +// +int CG::locate_immi(int x, CodeGen& cg, CG_Builder& frag) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(x), CG::r(r)); + return r; +} + +// Modified version of execute-comprehension, executing an arbitrary callable on the innter +// expression. +template +void execute_comprehension_generic(Comprehension* c, Mode ctx, CodeGen& cg, CG_Builder& frag, F f) { + // Build up the object to build the generator. + std::vector nesting; + + int depth = 0; + + int g = c->n_generators(); + int n_gen = c->n_generators(); + for (int g = 0; g < n_gen; ++g) { + // Bind the in-expression to a register. + // assert(c->in(g)->type().ispar()); + Expression* in(c->in(g)); + // std::cout << "Binding R" << r << " to: "; debugprint(in); + // Open the bindings. + cg.env_push(); + if (in->type().is_set()) { + int r(CG::bind(in, cg, frag).first); + assert(in->type().ispar()); + for (int d = 0; d < c->n_decls(g); ++d) { + Forset* iter(new Forset(cg, r)); + depth += 2; // Forset first iterates over the range, then values in the range. + nesting.push_back(iter); + iter->emit_pre(frag); + // Bind vd->id() to iter.val() + VarDecl* vd(c->decl(g, d)); + ASTString id(vd->id()->str()); + // cg.env().bind(id, Loc::reg(iter.val())); + cg.env().bind(id, CodeGen::Binding(iter->val(), CG_Cond::ttt())); + } + } else { + assert(in->type().isboolarray() || in->type().isintarray() || in->type().isintsetarray()); + int r_arr(CG::bind(in, cg, frag).first); + for (int d = 0; d < c->n_decls(g); ++d) { + Foreach* iter(new Foreach(cg, r_arr)); + nesting.push_back(iter); + iter->emit_pre(frag); + depth += 1; + + VarDecl* vd(c->decl(g, d)); + ASTString id(vd->id()->str()); + cg.env().bind(id, CodeGen::Binding(iter->val(), CG_Cond::ttt())); + } + } + // Now emit the where-clause + Expression* where(c->where(g)); + if (where) { + int lblCont(nesting.back()->cont()); + assert(where->type().ispar()); + int rC = CG::force(CG::compile(where, cg, frag), BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblCont)); + } + } + // We're now in the deepest scope. Generate code for the body. + f(c->e(), ctx, cg, frag, nesting.back()->cont(), depth); + + // Now close the iterators _in reverse order_, and restore the environment. + for (int ii = nesting.size() - 1; ii >= 0; --ii) { + nesting[ii]->emit_post(frag); + delete nesting[ii]; + } + for (int ii = 0; ii < n_gen; ++ii) cg.env_pop(); +} + +// Specialized version when we're binding and pushing the body. +void execute_comprehension_bind(Comprehension* c, Mode ctx, CodeGen& cg, CG_Builder& frag) { + execute_comprehension_generic( + c, ctx, cg, frag, + [](Expression* e, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { + // Bind and push everything in the comprehension. + CG::Binding b_e = CG::force_or_bind(e, ctx, cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_e.first)); // FIXME: Discarding partiality + }); +} + +// Special case implementation of folds where body is a generator. +CG_Cond::T eval_forall(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + Expression* param = call->arg(0); + // Now check the expression's type. If it's an array literal or + // comprehension, we generate code directly, rather than generating + // a concrete vector. + switch (param->eid()) { + case Expression::E_ARRAYLIT: { + std::vector conj; + ArrayLit* a(param->cast()); + int sz(a->size()); + for (int ii = 0; ii < sz; ++ii) conj.push_back(CG::compile((*a)[ii], cg, frag)); + return CG_Cond::forall(ctx, conj); + } break; + case Expression::E_COMP: { + Comprehension* c(param->cast()); + // Special cases: if we're in root, just post everything. + if (ctx == BytecodeProc::ROOT) { + execute_comprehension_generic( + param->cast(), ctx, cg, frag, + [](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { + CG_Cond::T c = CG::compile(elt, cg, frag); + post_cond(cg, frag, c); + }); + ; + return CG_Cond::T::ttt(); + } + // If it's par, do shortcutting. + if (c->e()->type().ispar()) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r)); + execute_comprehension_generic( + c, ctx, cg, frag, + [r](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { + int r_cond = CG::force(CG::compile(elt, cg, frag), BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(lbl_cont)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(depth)); + }); + ; + return CG_Cond::reg(r, true); + } else { + // General case, just aggregate the values. + OPEN_AND(cg, frag); + execute_comprehension_generic( + c, ctx, cg, frag, + [](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { + int r_cond = + CG::force(CG::compile(elt, cg, frag), CG::Mode(ctx.strength(), false), cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_cond)); + }); + CLOSE_AGG(cg, frag); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return CG_Cond::reg(r, false); + } + } + default: { + int r(GET_REG(cg)); + CG::Binding b_param(CG::bind(param, cg, frag)); + int r_A(b_param.first); + std::vector p_A; + if (!b_param.second.get()) { + if (b_param.second.sign()) return CG_Cond::T::fff(); + } else { + // TODO: It's probably not quicker to split par/var since we need the AND Aggregation anyway + force_and_leaves(p_A, p_A, b_param.second, cg, frag); + } + OPEN_AND(cg, frag); + // First, push the constraints attached to A. + for (int r_c : p_A) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c)); + } + ITER_ARRAY(cg, frag, r_A, [](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); + }); + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return CG_Cond::reg(r, call->type().ispar()); + } + } +} +// Same as forall, but producing an or-context. +CG_Cond::T eval_exists(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + Expression* param = call->arg(0); + + // Now check the expression's type. If it's an array literal or + // comprehension, we generate code directly, rather than generating + // a concrete vector. + switch (param->eid()) { + case Expression::E_ARRAYLIT: { + std::vector disj; + ArrayLit* a(param->cast()); + int sz(a->size()); + for (int ii = 0; ii < sz; ++ii) { + CG_Cond::T elt(CG::compile((*a)[ii], cg, frag)); + disj.push_back(elt); + } + return CG_Cond::exists(ctx, disj); + } break; + /* + case Expression::E_COMP: { + Comprehension* c(param->cast()); + execute_comprehension(c, c_ctx, cg, frag); + } + break; + */ +#if 1 + case Expression::E_COMP: { + Comprehension* c(param->cast()); + if (c->e()->type().ispar()) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); + execute_comprehension_generic( + c, ctx, cg, frag, + [r](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { + int r_cond = CG::force(CG::compile(elt, cg, frag), BytecodeProc::FUN, cg, frag); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_cond), CG::l(lbl_cont)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::ITER_BREAK, CG::i(depth)); + }); + return CG_Cond::reg(r, true); + } else { + OPEN_OR(cg, frag); + execute_comprehension_generic( + c, ctx, cg, frag, + [](Expression* elt, Mode ctx, CodeGen& cg, CG_Builder& frag, int lbl_cont, int depth) { + int r_cond = + CG::force(CG::compile(elt, cg, frag), CG::Mode(ctx.strength(), false), cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_cond)); + }); + CLOSE_AGG(cg, frag); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return CG_Cond::reg(r, false); + } + } // Fallthrough +#endif + default: { + int r(GET_REG(cg)); + // Otherwise, get the result into a register... + CG::Binding b_A(CG::bind(param, cg, frag)); + int r_A(b_A.first); + // and push every element. + OPEN_OR(cg, frag); + ITER_ARRAY(cg, frag, r_A, [](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); + }); + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return CG_Cond::forall(ctx, b_A.second, CG_Cond::reg(r, call->type().ispar())); + } + } +} + +CG_Cond::T eval_isfixed_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + Expression* e(call->arg(0)); + + if (e->type().ispar()) return CG_Cond::T::ttt(); + + int r_e; + if (e->type().isbool()) { + CG_Cond::T b_e(CG::compile(e, cg, frag)); + if (!b_e.get()) { + return CG_Cond::T::ttt(); + } + r_e = CG::force(b_e, ctx, cg, frag); + } else { + CG::Binding b(CG::bind(e, cg, frag)); + r_e = b.first; + } + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(r_e), CG::r(r)); + return CG_Cond::reg(r, true); +} + +CG::Binding bind_fix(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + + Expression* arg(call->arg(0)); + CG::Binding b_arg(CG::bind(arg, cg, frag)); + if (arg->type().ispar()) { + return b_arg; + } else if (arg->type().isintarray() || arg->type().isboolarray()) { + int r_cond(GET_REG(cg)); + // Init the condition + PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(b_arg.first), CG::r(r_cond)); + + // Gather elements + OPEN_VEC(cg, frag); + ITER_ARRAY(cg, frag, b_arg.first, [](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_elt)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); + }); + CLOSE_AGG(cg, frag); + int r_elts(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_elts)); + + // Ensure resulting array has the same index set + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first), + CG::r(bind_cst(0, cg, frag))); + int r_indxs(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_indxs)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r_elts), + CG::r(r_indxs)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_elts)); + + return {r_elts, CG_Cond::forall(ctx, b_arg.second, CG_Cond::reg(r_cond, true))}; + } else { + int r(GET_REG(cg)); + int r_cond(GET_REG(cg)); + + PUSH_INSTR(frag, BytecodeStream::ISPAR, CG::r(b_arg.first), CG::r(r_cond)); + PUSH_INSTR(frag, BytecodeStream::LB, CG::r(b_arg.first), CG::r(r)); + + return {r, CG_Cond::forall(ctx, b_arg.second, CG_Cond::reg(r_cond, true))}; + } +} + +CG_Cond::T eval_fix(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + + // Should we check that the argument is actually fixed?? + int r = CG::force(CG::compile(call->arg(0), cg, frag), ctx, cg, frag); + return CG_Cond::reg(r, true); +} + +CG_Cond::T eval_error_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + throw InternalError("Call should only appear in general context."); + return CG_Cond::ttt(); +} +CG::Binding bind_error_g(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + throw InternalError("Call should only appear in Boolean context."); +} +CG::Binding bind_sum(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + GCLock lock; + assert(call->n_args() == 1); + Expression* e = call->arg(0); + // Components of the sum may be partial. + // TODO: Specialise for literals and comprehensions. + CG::Binding b_elts(CG::bind(e, cg, frag)); + int r_one(bind_cst(1, cg, frag)); + + if (e->type().ispar()) { + int r_sum(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_sum)); + ITER_ARRAY(cg, frag, b_elts.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_sum), CG::r(r_elt), CG::r(r_sum)); + }); + return CG::Binding(r_sum, b_elts.second); + } + + int r_sz(GET_REG(cg)); + int r_ret(GET_REG(cg)); + + int l_eq0(GET_LABEL(cg)); + int l_eq1(GET_LABEL(cg)); + int l_end(GET_LABEL(cg)); + + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_elts.first), CG::r(r_sz)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sz), CG::l(l_eq0)); + PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(r_one), CG::r(r_sz), CG::r(r_ret)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_ret), CG::l(l_eq1)); + + // Sum n arguments + auto fun = find_call_fun(cg, {"sum_cc"}, Type::varint(), {Type::varint(1)}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(b_elts.first)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); + + // Sum zero arguments (result must be 0) + PUSH_LABEL(frag, l_eq0); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_ret)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); + + // Sum 1 arguments (result == a[1]) + PUSH_LABEL(frag, l_eq1); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_elts.first), CG::r(r_one), CG::r(r_ret)); + + // End of sum + PUSH_LABEL(frag, l_end); + return CG::Binding(r_ret, b_elts.second); +} + +CG::Binding bind_set2array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + // Bind set + Expression* e = call->arg(0); + CG::Binding b_elts(CG::bind(e, cg, frag)); + + // Create array + + OPEN_VEC(cg, frag); + ITER_SET(cg, frag, b_elts.first, [](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); + }); + CLOSE_AGG(cg, frag); + int r_ret(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); + + return {r_ret, b_elts.second}; +} + +CG::Binding bind_dom_bounds_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + + // Open the context here, so we can recover all the registers after. + OPEN_VEC(cg, frag); + + int r_lb(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_lb)); + + int r_ub(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ub)); + + // Do the iteration + int r_test(GET_REG(cg)); + int r_bound(GET_REG(cg)); + + int l_lb(GET_LABEL(cg)); + int l_ub(GET_LABEL(cg)); + // Body + ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_bound)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_lb), CG::r(r_bound), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_test), CG::l(l_lb)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_bound), CG::r(r_lb)); + PUSH_LABEL(frag, l_lb); + PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_elt), CG::r(r_bound)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_bound), CG::r(r_ub), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_test), CG::l(l_ub)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_bound), CG::r(r_ub)); + PUSH_LABEL(frag, l_ub); + }); + + // Now collect the elements + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_lb)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_ub)); + CLOSE_AGG(cg, frag); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return CG::Binding(r, b_A.second); +} + +CG_Cond::T eval_assert_b(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->arg(0)->type().ispar()); + // Evaluate the assertion, abort if it fails. + /* + int r_cond = CG::force(CG::compile(call->arg(0), cg, frag), cg, frag); + int l_okay(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(l_okay)); + PUSH_INSTR(frag, BytecodeStream::ABORT); + PUSH_LABEL(frag, l_okay); + */ + // Otherwise, evaluate as usual. + if (call->n_args() == 2) { + return CG_Cond::ttt(); + } else { + assert(call->n_args() == 3); + return CG::compile(call->arg(2), cg, frag); + } +} + +CG::Binding bind_assert_g(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 3); + assert(call->arg(0)->type().ispar()); + /* + int r_cond = CG::force(CG::compile(call->arg(0), cg, frag), cg, frag); + int l_okay(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_cond), CG::l(l_okay)); + PUSH_INSTR(frag, BytecodeStream::ABORT); + PUSH_LABEL(frag, l_okay); + */ + return CG::bind(call->arg(2), cg, frag); +} + +CG::Binding bind_arrayXd(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 2); + CG::Binding orig(CG::bind(call->arg(0), cg, frag)); + CG::Binding target(CG::bind(call->arg(1), cg, frag)); + + // Get index sets from orig + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(orig.first), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + // Create new array + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(target.first), + CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + return {r, CG_Cond::forall(ctx, orig.second, target.second)}; +} + +template +CG::Binding bind_arrayNd(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == N + 1); + std::vector cond; + + // Bind all index sets + std::vector r_index(N); + for (int ii = 0; ii < N; ++ii) { + CG::Binding b(CG::bind(call->arg(ii), cg, frag)); + r_index[ii] = b.first; + cond.push_back(b.second); + } + + // Bind array expression + CG::Binding arr = CG::bind(call->arg(N), cg, frag); + + int rI(GET_REG(cg)); + OPEN_VEC(cg, frag); + for (int ii = 0; ii < N; ++ii) { + // TODO: Ensure the index set is a range (2-elements) + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_index[ii]), CG::r(bind_cst(1, cg, frag)), + CG::r(rI)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rI)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_index[ii]), CG::r(bind_cst(2, cg, frag)), + CG::r(rI)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rI)); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(arr.first), + CG::r(rI)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI)); + + return {rI, CG_Cond::forall(ctx, cond)}; +} + +CG::Binding bind_array1d(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + if (call->n_args() == 1) { + int rA(GET_REG(cg)); + CG::Binding arr = CG::bind(call->arg(0), cg, frag); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(arr.first), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rA)); + + return {rA, arr.second}; + } else { + // Index set given by the user + assert(call->n_args() == 2); + return bind_arrayNd<1>(call, ctx, cg, frag); + } +} + +CG::Binding bind_array_union(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + Expression* e = call->arg(0); + // Components of the sum may be partial. + // TODO: Specialise for literals and comprehensions. + CG::Binding b_elts(CG::bind(e, cg, frag)); + + assert(e->type().ispar()); // TODO: var case + int r_union(GET_REG(cg)); + OPEN_VEC(cg, frag); + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_union)); + + ITER_ARRAY(cg, frag, b_elts.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_union), CG::r(r_elt), CG::r(r_union)); + }); + + return {r_union, b_elts.second}; +} + +CG::Binding bind_length(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b.first), CG::r(r)); + return {r, b.second}; +} + +CG::Binding bind_lb(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LB, CG::r(b.first), CG::r(r)); + return CG::Binding(r, b.second); +} +CG::Binding bind_ub(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::UB, CG::r(b.first), CG::r(r)); + return CG::Binding(r, b.second); +} +CG::Binding bind_dom(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(b.first), CG::r(r)); + return CG::Binding(r, b.second); +} +CG::Binding bind_lb_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + + int r_agg(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg)); + + int r_tmp(GET_REG(cg)); + int r_test(GET_REG(cg)); + int l_end(GET_LABEL(cg)); + ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::LB, CG::r(r_elt), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_tmp), CG::r(r_agg), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_tmp), CG::r(r_agg)); + PUSH_LABEL(frag, l_end); + }); + + return CG::Binding(r_agg, b_A.second); +} + +CG::Binding bind_ub_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + + int r_agg(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg)); + + int r_tmp(GET_REG(cg)); + int r_test(GET_REG(cg)); + int l_end(GET_LABEL(cg)); + ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::UB, CG::r(r_elt), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_agg), CG::r(r_tmp), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_tmp), CG::r(r_agg)); + PUSH_LABEL(frag, l_end); + }); + + return CG::Binding(r_agg, b_A.second); +} + +CG::Binding bind_dom_array(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + + int r_agg(GET_REG(cg)); + OPEN_VEC(cg, frag); + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_agg)); + + int r_tmp(GET_REG(cg)); + ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(r_elt), CG::r(r_tmp)); + PUSH_INSTR(frag, BytecodeStream::UNION, CG::r(r_agg), CG::r(r_tmp), CG::r(r_agg)); + }); + + return {r_agg, b_A.second}; +} + +CG::Binding bind_index_set(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + assert(call->arg(0)->type().dim() == 1); + CG::Binding b_arg(CG::bind(call->arg(0), cg, frag)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first), + CG::r(bind_cst(1, cg, frag))); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + return {r, b_arg.second}; +} + +template +CG::Binding bind_index_set_XofY(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_arg(CG::bind(call->arg(0), cg, frag)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_arg.first), + CG::r(bind_cst(X, cg, frag))); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + return {r, b_arg.second}; +} + +CG::Binding bind_bool2int(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + return CG::force_or_bind(call->arg(0), BytecodeProc::FUN, cg, frag); +} +CG::Binding bind_int2float(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + return CG::bind(call->arg(0), cg, frag); +} + +CG::Binding bind_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag); +CG_Cond::T compile_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag); + +CG::Binding bind_set_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->type().ispar()); + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + int r_sz(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz)); + int l_fst(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(bind_cst(0, cg, frag)), CG::r(r_sz), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_fst)); + PUSH_INSTR(frag, BytecodeStream::ABORT); + PUSH_LABEL(frag, l_fst); + + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_sz), CG::r(r)); + + // TODO: Also should not be allowed on empty sets + return CG::Binding(r, b_A.second); +}; + +CG::Binding bind_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->type().ispar()); + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + int r_test(GET_REG(cg)); + int l_end(GET_LABEL(cg)); + ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r), CG::r(r_elt), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r)); + PUSH_LABEL(frag, l_end); + }); + + // TODO: Fail if array size < 1 + return CG::Binding(r, CG_Cond::ttt()); +} + +CG::Binding bind_arg_max(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + int r_elt(GET_REG(cg)); + int r_v(GET_REG(cg)); + int r_test(GET_REG(cg)); + int r_index(GET_REG(cg)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_A.first), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_index)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_v)); + + int l_end(GET_LABEL(cg)); + ITER_SET(cg, frag, r_index, [&](CodeGen& cg, CG_Builder& frag, int r_i) { + // Array access should be valid since we are using the actual index set. + PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(1), CG::r(b_A.first), CG::r(r_i), + CG::r(r_elt), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_v), CG::r(r_elt), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r_v)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_i), CG::r(r)); + PUSH_LABEL(frag, l_end); + }); + + // TODO: Fail if array size < 1 + return CG::Binding(r, CG_Cond::ttt()); +} + +CG::Binding bind_arg_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + int r_elt(GET_REG(cg)); + int r_v(GET_REG(cg)); + int r_test(GET_REG(cg)); + int r_index(GET_REG(cg)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("index_set"), CG::r(b_A.first), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_index)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_v)); + + int l_end(GET_LABEL(cg)); + ITER_SET(cg, frag, r_index, [&](CodeGen& cg, CG_Builder& frag, int r_i) { + // Array access should be valid since we are using the actual index set. + PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(1), CG::r(b_A.first), CG::r(r_i), + CG::r(r_elt), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_elt), CG::r(r_v), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r_v)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_i), CG::r(r)); + PUSH_LABEL(frag, l_end); + }); + + // TODO: Fail if array size < 1 + return CG::Binding(r, CG_Cond::ttt()); +} + +CG::Binding bind_set_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->type().ispar()); + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + int r_sz(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz)); + int l_fst(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(bind_cst(1, cg, frag)), CG::r(r_sz), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_fst)); + PUSH_INSTR(frag, BytecodeStream::ABORT); + + PUSH_LABEL(frag, l_fst); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(bind_cst(1, cg, frag)), + CG::r(r)); + + return CG::Binding(r, CG_Cond::ttt()); +} + +CG::Binding bind_min(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->type().ispar()); + assert(call->n_args() == 1); + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + int r_test(GET_REG(cg)); + int l_end(GET_LABEL(cg)); + ITER_ARRAY(cg, frag, b_A.first, [&](CodeGen& cg, CG_Builder& frag, int r_elt) { + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_elt), CG::r(r), CG::r(r_test)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_test), CG::l(l_end)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_elt), CG::r(r)); + PUSH_LABEL(frag, l_end); + }); + + return CG::Binding(r, CG_Cond::ttt()); +} + +CG::Binding bind_card(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(call->type().ispar()); + assert(call->n_args() == 1); + + CG::Binding b_A(CG::bind(call->arg(0), cg, frag)); + int r(GET_REG(cg)); + int r_i(GET_REG(cg)); + int r_sz(GET_REG(cg)); + int r_e(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); + + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_i)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(b_A.first), CG::r(r_sz)); + int l_hd(GET_LABEL(cg)); + int l_tl(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_i), CG::r(r_sz), CG::r(r_e)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_e), CG::l(l_tl)); + + PUSH_LABEL(frag, l_hd); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_i), CG::r(r_e)); + PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r), CG::r(r_e), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_i)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(b_A.first), CG::r(r_i), CG::r(r_e)); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(r_i)); + PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r), CG::r(r_e), CG::r(r)); + + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(r_i), CG::r(r_sz), CG::r(r_e)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_e), CG::l(l_hd)); + PUSH_LABEL(frag, l_tl); + return CG::Binding(r, b_A.second); +} + +CG::Binding bind_internal(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + std::string name = call->decl()->id().str(); + CG_ProcID proc = cg.find_builtin(name); + int r_res(GET_REG(cg)); + + std::vector r_args(call->n_args()); + std::vector p_args(call->n_args()); + for (int i = 0; i < call->n_args(); ++i) { + CG::Binding b_arg(CG::bind(call->arg(i), cg, frag)); + r_args[i] = CG::r(b_arg.first); + p_args[i] = b_arg.second; + } + + // Push BUILTIN instruction with the correct id + PUSH_INSTR(frag, BytecodeStream::BUILTIN, proc); + // Append instruction with register arguments + CG_Instr& i = frag.instrs.back(); + PUSH_INSTR_OPERAND(i, r_args); + + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_res)); + + return {r_res, CG_Cond::forall(ctx, p_args)}; +} + +CG_Cond::T eval_context_is_root(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // TODO: I'm not sure this is actually correct.... + assert(call->n_args() == 1); + CG::Mode arg_ctx(BytecodeProc::FUN); + try { + arg_ctx = cg.mode_map.at(call->arg(0)); + } catch (const std::out_of_range& exn) { + } + + if (arg_ctx == BytecodeProc::ROOT) { + return CG_Cond::ttt(); + } else { + return CG_Cond::fff(); + } +} + +CG_Cond::T eval_has_bounds(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // TODO: Actual implementation! + + return CG_Cond::ttt(); +} + +CG::Binding bind_TODO(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + PUSH_INSTR(frag, BytecodeStream::ABORT); + return {0, CG_Cond::fff()}; +} +CG_Cond::T eval_TODO(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + PUSH_INSTR(frag, BytecodeStream::ABORT); + return CG_Cond::fff(); +} + +template +CG_Cond::T eval_argX_only(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + return CG::compile(call->arg(X - 1), cg, frag); +} + +builtin_table init_builtins(void) { + builtin_table tbl; + Constants& c(constants()); + tbl.insert(std::make_pair(c.ids.sum.str(), builtin_t{eval_error_b, bind_sum})); + tbl.insert(std::make_pair(c.ids.exists.str(), builtin_t{eval_exists, bind_error_g})); + tbl.insert(std::make_pair(c.ids.forall.str(), builtin_t{eval_forall, bind_error_g})); + tbl.insert(std::make_pair(c.ids.assert.str(), builtin_t{eval_assert_b, bind_assert_g})); + tbl.insert(std::make_pair("arrayXd", builtin_t{eval_error_b, bind_arrayXd})); + tbl.insert(std::make_pair("array1d", builtin_t{eval_error_b, bind_array1d})); + tbl.insert(std::make_pair("array2d", builtin_t{eval_error_b, bind_arrayNd<2>})); + tbl.insert(std::make_pair("array3d", builtin_t{eval_error_b, bind_arrayNd<3>})); + tbl.insert(std::make_pair("array4d", builtin_t{eval_error_b, bind_arrayNd<4>})); + tbl.insert(std::make_pair("array5d", builtin_t{eval_error_b, bind_arrayNd<5>})); + tbl.insert(std::make_pair("array6d", builtin_t{eval_error_b, bind_arrayNd<6>})); + tbl.insert(std::make_pair("array7d", builtin_t{eval_error_b, bind_arrayNd<7>})); + tbl.insert(std::make_pair("array8d", builtin_t{eval_error_b, bind_arrayNd<8>})); + tbl.insert(std::make_pair("array9d", builtin_t{eval_error_b, bind_arrayNd<9>})); + tbl.insert(std::make_pair("array_union", builtin_t{eval_error_b, bind_array_union})); + tbl.insert(std::make_pair("index_set", builtin_t{eval_error_b, bind_index_set})); + tbl.insert(std::make_pair("index_set_1of2", builtin_t{eval_error_b, bind_index_set_XofY<1, 2>})); + tbl.insert(std::make_pair("index_set_2of2", builtin_t{eval_error_b, bind_index_set_XofY<2, 2>})); + tbl.insert(std::make_pair("index_set_1of3", builtin_t{eval_error_b, bind_index_set_XofY<1, 3>})); + tbl.insert(std::make_pair("index_set_2of3", builtin_t{eval_error_b, bind_index_set_XofY<2, 3>})); + tbl.insert(std::make_pair("index_set_3of3", builtin_t{eval_error_b, bind_index_set_XofY<3, 3>})); + tbl.insert(std::make_pair("index_set_1of4", builtin_t{eval_error_b, bind_index_set_XofY<1, 4>})); + tbl.insert(std::make_pair("index_set_2of4", builtin_t{eval_error_b, bind_index_set_XofY<2, 4>})); + tbl.insert(std::make_pair("index_set_3of4", builtin_t{eval_error_b, bind_index_set_XofY<3, 4>})); + tbl.insert(std::make_pair("index_set_4of4", builtin_t{eval_error_b, bind_index_set_XofY<4, 4>})); + tbl.insert(std::make_pair("length", builtin_t{eval_error_b, bind_length})); + tbl.insert(std::make_pair("lb", builtin_t{eval_error_b, bind_lb})); + tbl.insert(std::make_pair("ub", builtin_t{eval_error_b, bind_ub})); + tbl.insert(std::make_pair("dom", builtin_t{eval_error_b, bind_dom})); + tbl.insert(std::make_pair("lb_array", builtin_t{eval_error_b, bind_lb_array})); + tbl.insert(std::make_pair("ub_array", builtin_t{eval_error_b, bind_ub_array})); + tbl.insert(std::make_pair("dom_array", builtin_t{eval_error_b, bind_dom_array})); + tbl.insert(std::make_pair("set2array", builtin_t{eval_error_b, bind_set2array})); + tbl.insert(std::make_pair("dom_bounds_array", builtin_t{eval_error_b, bind_dom_bounds_array})); + tbl.insert(std::make_pair("arg_max", builtin_t{eval_error_b, bind_arg_max})); + tbl.insert(std::make_pair("arg_min", builtin_t{eval_error_b, bind_arg_min})); + tbl.insert(std::make_pair("card", builtin_t{eval_error_b, bind_card})); + tbl.insert(std::make_pair(c.ids.bool2int.str(), builtin_t{eval_error_b, bind_bool2int})); + tbl.insert(std::make_pair(c.ids.int2float.str(), builtin_t{eval_error_b, bind_bool2int})); + tbl.insert(std::make_pair("uniform", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("sol", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("sort_by", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("floor", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("ceil", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("mzn_in_root_context", builtin_t{eval_context_is_root, bind_error_g})); + tbl.insert(std::make_pair("has_bounds", builtin_t{eval_has_bounds, bind_error_g})); + tbl.insert(std::make_pair("is_fixed", builtin_t{eval_isfixed_b, bind_error_g})); + tbl.insert(std::make_pair("fix", builtin_t{eval_fix, bind_fix})); + tbl.insert(std::make_pair("array_Xd", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("slice_Xd", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("internal_sort", builtin_t{eval_error_b, bind_internal})); + tbl.insert(std::make_pair("internal_max", builtin_t{eval_error_b, bind_max})); + tbl.insert(std::make_pair("internal_set_max", builtin_t{eval_error_b, bind_set_max})); + tbl.insert(std::make_pair("internal_min", builtin_t{eval_error_b, bind_min})); + tbl.insert(std::make_pair("internal_set_min", builtin_t{eval_error_b, bind_set_min})); + tbl.insert( + std::make_pair("symmetry_breaking_constraint", builtin_t{eval_argX_only<1>, bind_error_g})); + tbl.insert(std::make_pair("redundant_constraint", builtin_t{eval_argX_only<1>, bind_error_g})); + // OPTIONAL TYPE INTERNALS + tbl.insert(std::make_pair("reverse_map", builtin_t{eval_error_b, bind_TODO})); + tbl.insert(std::make_pair("occurs", builtin_t{eval_TODO, bind_error_g})); + tbl.insert(std::make_pair("absent", builtin_t{eval_TODO, bind_error_g})); + tbl.insert(std::make_pair("deopt", builtin_t{eval_error_b, bind_TODO})); + return tbl; +} +builtin_table& builtins(void) { + static builtin_table tbl(init_builtins()); + return tbl; +} + +CG::Binding CG::bind(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag) { + try { + return CG::Binding(cg.env().lookup(x->v()).first, CG_Cond::ttt()); + } catch (const CG_Env::NotFound& exn) { + VarDecl* vd = follow_id_to_decl(x)->cast(); + int g = cg.find_global(vd); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LOAD_GLOBAL, CG::g(g), CG::r(r)); + return CG::Binding(r, CG_Cond::ttt()); + } +} + +CG::Binding CG::bind(SetLit* sl, Mode ctx, CodeGen& cg, CG_Builder& frag) { + bool is_vec(false); + OPEN_VEC(cg, frag); + if (IntSetVal* s = sl->isv()) { + int r_t(GET_REG(cg)); + for (int ii = 0; ii < s->size(); ++ii) { + IntVal l(s->min(ii)); + IntVal u(s->max(ii)); + if (l.isFinite()) { + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(l.toInt()), CG::r(r_t)); + } else { + assert(l.isMinusInfinity()); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(0, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_t)); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); + if (u.isFinite()) { + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(u.toInt()), CG::r(r_t)); + } else { + assert(u.isPlusInfinity()); + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("infinity"), + CG::r(bind_cst(1, cg, frag))); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_t)); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); + } + } else { + is_vec = true; + int sz = sl->v().size(); + int r_t(GET_REG(cg)); + for (int ii = 0; ii < sz; ii++) { + // Assumes the set is sorted. + /* + IntLit* k(l->v()[ii]->template cast()); + assert(k); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(k->v().toInt()), CG::r(r_t)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_t)); + */ + CG::Binding b(CG::bind(sl->v()[ii], cg, frag)); // Ignores any partiality. + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b.first)); + } + } + CLOSE_AGG(cg, frag); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (is_vec) PUSH_INSTR(frag, BytecodeStream::MAKE_SET, CG::r(r), CG::r(r)); + return CG::Binding(r, CG_Cond::ttt()); +} + +CG::Binding CG::bind(ArrayLit* a, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // Build up the array. + std::vector> r_vec; + std::vector p_vec; + + int sz(a->size()); + for (int ii = 0; ii < sz; ++ii) { + if ((*a)[ii]->type().isbool()) { + CG_Cond::T c(CG::compile((*a)[ii], cg, frag)); + if (!c.get()) { + if (!c.sign()) { + r_vec.emplace_back(nullptr, bind_cst(1, cg, frag)); + } else { + r_vec.emplace_back(nullptr, bind_cst(0, cg, frag)); + } + } else { + r_vec.emplace_back(nullptr, CG::force(c, ctx, cg, frag)); + } + } else if (auto il = (*a)[ii]->dyn_cast()) { + // Avoid lookup cost when constructing large array literals + r_vec.emplace_back(il, 0xdeadbeef); + } else { + Binding b_ii(CG::bind((*a)[ii], cg, frag)); + r_vec.emplace_back(nullptr, b_ii.first); + p_vec.push_back(b_ii.second); + } + } + + // Build Array + int r(GET_REG(cg)); + OPEN_VEC(cg, frag); + for (auto r_c : r_vec) { + if (r_c.first) { + assert(r_c.first->v().isFinite()); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(r_c.first->v().toInt()), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + } else { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_c.second)); + } + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + // Add index set if required + if (!(a->dims() == 1 && a->min(0))) { + int rI; + if (a->dims() == 1) { + rI = (locate_range(a->min(0), a->max(0), cg, frag)); + } else { + rI = GET_REG(cg); + OPEN_VEC(cg, frag); + for (int ii = 0; ii < a->dims(); ++ii) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(a->min(ii), cg, frag))); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(a->max(ii), cg, frag))); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rI)); + } + + // Combine array and index sets + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), CG::r(r), CG::r(rI)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } + + return {r, CG_Cond::forall(ctx, p_vec)}; +} + +std::pair execute_array_access(int r_A, std::vector r_idxs, CodeGen& cg, + CG_Builder& frag) { + int sz = r_idxs.size(); + int r(GET_REG(cg)); + int r_cond(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::GET_ARRAY, CG::i(sz), CG::r(r_A)); + std::vector r_args(sz + 2); + for (int ii = 0; ii < sz; ++ii) { + r_args[ii] = CG::r(r_idxs[ii]); + } + r_args[sz] = CG::r(r); + r_args[sz + 1] = CG::r(r_cond); + CG_Instr& i = frag.instrs.back(); + PUSH_INSTR_OPERAND(i, r_args); + return {r, CG_Cond::reg(r_cond, true)}; +} + +CG::Binding CG::bind(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag) { + GCLock lock; + // If the array elements are Boolean, we need to check for partiality + // in the indices, and that the accesses are within-range. + ASTExprVec idx(a->idx()); + Expression* A(a->v()); + int sz(idx.size()); + + bool is_var = false; + for (int ii = 0; ii < sz; ++ii) { + is_var = is_var || idx[ii]->type().isvar(); + } + + // Evaluate the indices, put them in registers. + // Collect partiality of the expression. + std::vector cond; + + // Now evaluate the array body, and emit the indices. + std::vector r_idxs(sz); + for (int ii = 0; ii < sz; ++ii) { + CG::Binding b(CG::bind(idx[ii], cg, frag)); + r_idxs[ii] = b.first; + cond.push_back(b.second); + } + + Binding b_A(CG::bind(A, cg, frag)); + int r_A(b_A.first); + cond.push_back(b_A.second); + + if (is_var) { + int r = GET_REG(cg); + + std::vector types(sz + 1, Type::varint()); + types[sz] = Type::varint(sz); + auto fun = find_call_fun(cg, {"element"}, Type::varint(), types, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + + std::vector r_args(sz + 1); + for (int ii = 0; ii < sz; ++ii) { + r_args[ii] = CG::r(r_idxs[ii]); + } + r_args[sz] = CG::r(r_A); + + // Push CALL instruction with the correct id + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun))); + // Append instruction with register arguments + CG_Instr& i = frag.instrs.back(); + PUSH_INSTR_OPERAND(i, r_args); + + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + return {r, CG_Cond::forall(ctx, cond)}; + } else { + // Just read the vector, and get the appropriate element. + int r; + CG_Cond::T ncond; + std::tie(r, ncond) = execute_array_access(r_A, r_idxs, cg, frag); + cond.push_back(ncond); + return {r, CG_Cond::forall(ctx, cond)}; + } +} + +int make_vec(CodeGen& cg, CG_Builder& frag, const std::vector& regs) { + OPEN_VEC(cg, frag); + for (int r : regs) PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + CLOSE_AGG(cg, frag); + int r_vec(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_vec)); + return r_vec; +} + +void call_clause(CodeGen& cg, CG_Builder& frag, Mode ctx, const std::vector& pos, + const std::vector& neg) { + GCLock lock; + // Build the arguments vectors. + int r_pos(make_vec(cg, frag, pos)); + int r_neg(make_vec(cg, frag, neg)); + auto fun = + find_call_fun(cg, {"clause"}, Type::varbool(), {Type::varbool(1), Type::varbool(1)}, ctx); + assert(BytecodeProc::is_neg(ctx) == BytecodeProc::is_neg(std::get<1>(fun))); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_pos), CG::r(r_neg)); +} + +int deinterlace(CodeGen& cg, CG_Builder& frag, int vec, int width, int offset) { + int r_sz(GET_REG(cg)); + int r_slice(GET_REG(cg)); + + OPEN_VEC(cg, frag); + // int r_i(CG::locate_immi(1 + offset, cg, frag)); + // int r_step(CG::locate_immi(width, cg, frag)); + int r_i(bind_cst(1 + offset, cg, frag)); + int r_step(bind_cst(width, cg, frag)); + int r_elt(GET_REG(cg)); + + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(vec), CG::r(r_sz)); + int l_hd(GET_LABEL(cg)); + int l_tl(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_i), CG::r(r_sz), CG::r(r_elt)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_elt), CG::l(l_tl)); + PUSH_LABEL(frag, l_hd); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(vec), CG::r(r_i), CG::r(r_elt)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_elt)); + PUSH_INSTR(frag, BytecodeStream::ADDI, CG::r(r_i), CG::r(r_step), CG::r(r_i)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(r_i), CG::r(r_sz), CG::r(r_elt)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r_elt), CG::l(l_hd)); + PUSH_LABEL(frag, l_tl); + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_slice)); + + return r_slice; +} + +/* +inline int reg_k(int k, CodeGen& cg, CG_Builder& frag, int& r) { + if(r == -1) + r = CG::locate_immi(1, cg, frag); + return r; +} +*/ + +CG::Binding CG::bind(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag) { + int sz(ite->size()); + int r_one(bind_cst(1, cg, frag)); + + bool all_par = true; + for (int ii = 0; ii < sz; ++ii) { + if (!ite->e_if(ii)->type().ispar()) { + all_par = false; + break; + } + } + + if (all_par) { + int l_end(GET_LABEL(cg)); + int r_ret(GET_REG(cg)); + int r_cond(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(r_cond)); + + bool is_total = true; + // We need to interfere with the env, here, because stuff may not be available. + // Except, ite->e_if(0) will always be available. + for (int ii = 0; ii < sz; ++ii) { + int r_sel = CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag); + int l_cont(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sel), CG::l(l_cont)); + cg.env_push(); + if (!ite->e_then(ii)->type().ispar()) all_par = false; + CG::Binding b_res(CG::bind(ite->e_then(ii), cg, frag)); + if (b_res.second.get()) { + is_total = false; + int r_condii(CG::force(b_res.second, ctx, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_condii), CG::r(r_cond)); + } else if (!b_res.second.sign()) { + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_cond)); + } + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(b_res.first), CG::r(r_ret)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); + cg.env_pop(); + cg.env_push(); + PUSH_LABEL(frag, l_cont); + } + // Else case. + if (!ite->e_else()->type().ispar()) all_par = false; + + CG::Binding b_res(CG::bind(ite->e_else(), cg, frag)); + if (b_res.second.get()) { + is_total = false; + int r_condii(CG::force(b_res.second, ctx, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_condii), CG::r(r_cond)); + } else if (!b_res.second.sign()) { + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_cond)); + } + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(b_res.first), CG::r(r_ret)); + PUSH_LABEL(frag, l_end); + // Now kill the availability of all the expressions. + for (int ii = 0; ii < sz; ++ii) { + cg.env_pop(); + } + return {r_ret, is_total ? CG_Cond::ttt() : CG_Cond::reg(r_cond, all_par)}; + } else { + GCLock lock; + std::vector r_cond; + std::vector p_res; + std::vector r_res; + + bool is_total = true; + for (int ii = 0; ii < sz; ++ii) { + r_cond.push_back( + CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag)); + CG::Binding b_res(CG::bind(ite->e_then(ii), cg, frag)); + r_res.push_back(b_res.first); + // Make sure b_res.second is evaluated _outside_ the aggregation. + if (b_res.second.get()) { + is_total = false; + p_res.push_back(CG::force(b_res.second, ctx, cg, frag)); + } else { + if (b_res.second.sign()) { + p_res.push_back(bind_cst(0, cg, frag)); + } else { + p_res.push_back(r_one); + } + } + } + Binding b_final = CG::bind(ite->e_else(), cg, frag); + r_cond.push_back(r_one); + r_res.push_back(b_final.first); + if (b_final.second.get()) { + is_total = false; + p_res.push_back(CG::force(b_final.second, ctx, cg, frag)); + } else { + if (b_final.second.sign()) { + p_res.push_back(bind_cst(0, cg, frag)); + } else { + p_res.push_back(r_one); + } + } + + int r_vec_cond(vec2array(r_cond, cg, frag)); + + int r_part; + if (!is_total) { + int r_vec_part(vec2array(p_res, cg, frag)); + + auto fun = find_call_fun(cg, {"if_then_else_partiality"}, Type::varbool(), + {Type::varbool(1), Type::varbool(1)}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_vec_cond), CG::r(r_vec_part)); + r_part = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_part)); + } + + int r_vec_res(vec2array(r_res, cg, frag)); + + auto fun = find_call_fun(cg, {"if_then_else"}, Type::varint(), + {Type::varbool(1), Type::varint(1)}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + + int r_ret(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::CALL, std::get<1>(fun), std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(r_vec_cond), CG::r(r_vec_res)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); + + return {r_ret, is_total ? CG_Cond::ttt() : CG_Cond::reg(r_part, false)}; + } +} + +CG::Binding CG::bind(BinOp* b, Mode ctx, CodeGen& cg, CG_Builder& frag) { + if (b->type().ispar()) { + CG::Binding b_lhs(CG::force_or_bind(b->lhs(), ctx, cg, frag)); + CG::Binding b_rhs(CG::force_or_bind(b->rhs(), ctx, cg, frag)); + if (b->lhs()->type().isintset()) { + int r = bind_binop_par_set(cg, frag, b->op(), b_lhs.first, b_rhs.first); + return {r, CG_Cond::ttt()}; + } + std::vector cond = {b_lhs.second, b_rhs.second}; + + int l_skip; + int r_ret; + if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) { + l_skip = GET_LABEL(cg); + int r_cond = GET_REG(cg); + r_ret = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r_ret)); + cond.push_back(CG_Cond::reg(r_cond, true)); + PUSH_INSTR(frag, BytecodeStream::EQI, CG::r(b_rhs.first), CG::r(r_ret), CG::r(r_cond)); + PUSH_INSTR(frag, BytecodeStream::NOT, CG::r(r_cond), CG::r(r_cond)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_cond), CG::l(l_skip)); + } + int r = bind_binop_par_int(cg, frag, b->op(), b_lhs.first, b_rhs.first); + if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) { + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r), CG::r(r_ret)); + r = r_ret; + PUSH_LABEL(frag, l_skip); + } + + return {r, CG_Cond::forall(ctx, cond)}; + } else { + std::vector partial; + int r_lhs = CG::force_or_bind(b->lhs(), ctx, partial, cg, frag); + int r_rhs = CG::force_or_bind(b->rhs(), ctx, partial, cg, frag); + + int r = GET_REG(cg); + bind_binop_var(cg, frag, BytecodeProc::FUN, b->op(), r_lhs, r_rhs); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + + if (b->op() == BOT_DIV || b->op() == BOT_IDIV || b->op() == BOT_MOD) { + // These all require rhs() != 0. Which means we need to evaluate the rhs, + // and put it in a register. + int r_zero(bind_cst(0, cg, frag)); + partial.push_back(~linear_cond(cg, frag, BOT_EQ, -ctx, r_rhs, r_zero)); + } + return {r, CG_Cond::forall(ctx, partial)}; + } +} + +CG::Binding CG::bind(UnOp* u, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // Unop is easy. + switch (u->op()) { + case UOT_NOT: + throw InternalError("Non-Boolean bind called on boolean operator."); + case UOT_PLUS: + return CG::bind(u->e(), cg, frag); + case UOT_MINUS: { + Binding b_e(CG::bind(u->e(), cg, frag)); + int r; + if (u->type().isvar()) { + GCLock lock; + auto fun = + find_call_fun(cg, {"op_minus"}, Type::varint(), {Type::varint()}, BytecodeProc::FUN); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i(!std::get<2>(fun)), CG::r(b_e.first)); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + } else { + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::SUBI, CG::r(r), CG::r(b_e.first), CG::r(r)); + } + return {r, b_e.second}; + } + } +} + +CG::Binding bind_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // Collects the partiality of the arguments + int sz = call->n_args(); + std::vector r_arg(sz); + std::vector p_arg(sz); + for (int ii = 0; ii < sz; ++ii) { + CG::Binding r_bind(CG::force_or_bind(call->arg(ii), BytecodeProc::FUN, cg, frag)); + r_arg[ii] = CG::r(r_bind.first); + p_arg[ii] = r_bind.second; + } + + // Bind the value part. + auto fun = find_call_fun(cg, call, BytecodeProc::ROOT); + assert(std::get<1>(fun) == BytecodeProc::FUN); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::FUN, std::get<0>(fun), + CG::i((!std::get<2>(fun)) && call->type().isvar()), r_arg); + + int r_ret(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_ret)); + + return std::make_pair(r_ret, CG_Cond::forall(ctx, p_arg)); +} + +CG::Binding CG::bind(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + { + GCLock gc; + auto it(builtins().find(call->id().str())); + if (it != builtins().end()) { + return (*it).second.general(call, ctx, cg, frag); + } + } + + return bind_call(call, ctx, cg, frag); +} + +CG::Binding CG::bind(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag) { + std::vector partial; + + cg.env_push(); + + if (let->ann().contains(constants().ann.promise_total)) { + OPEN_OTHER(cg, frag); + eval_let_body(let, BytecodeProc::ROOT, cg, frag, partial); + + CG::Binding b_in(CG::bind(let->in(), cg, frag)); + partial.push_back(b_in.second); + post_cond(cg, frag, CG_Cond::forall(BytecodeProc::ROOT, partial)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(b_in.first)); + CLOSE_AGG(cg, frag); + cg.env_pop(); + + int ret = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(ret)); + + return {ret, CG_Cond::forall(ctx, partial)}; + } + + eval_let_body(let, BytecodeProc::ROOT, cg, frag, partial); + CG::Binding b_in(CG::bind(let->in(), cg, frag)); + partial.push_back(b_in.second); + + cg.env_pop(); + return {b_in.first, CG_Cond::forall(ctx, partial)}; +} + +CG::Binding CG::bind(Comprehension* comp, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // Lift out the outer-most comprehension, since it will always + // be executed. + CG::bind(comp->in(0), cg, frag); + + cg.env_push(); + OPEN_VEC(cg, frag); + execute_comprehension_bind(comp, ctx, cg, frag); + CLOSE_AGG(cg, frag); + cg.env_pop(); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + if (comp->type().is_set()) { + PUSH_INSTR(frag, BytecodeStream::MAKE_SET, CG::r(r), CG::r(r)); + } + return CG::Binding(r, CG_Cond::ttt()); +} + +CG_Cond::T _compile(Expression* e, CodeGen& cg, CG_Builder& frag) { + // Look up the mode we need to compile e in. + CG::Mode ctx(BytecodeProc::FUN); + try { + ctx = cg.mode_map.at(e); + } catch (const std::out_of_range& exn) { + std::cerr << "%% Missing mode for: "; + debugprint(e); + } + + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_SETLIT: + case Expression::E_STRINGLIT: + case Expression::E_ARRAYLIT: + case Expression::E_COMP: + throw InternalError("compile called on non-Boolean expression."); + case Expression::E_BOOLLIT: + return (e->template cast()->v()) ? CG_Cond::ttt() : CG_Cond::fff(); + case Expression::E_ID: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_ANON: + throw InternalError("compile reached unexpected expression type: E_ANON."); + case Expression::E_ARRAYACCESS: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_ITE: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_BINOP: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_UNOP: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_CALL: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_LET: + return CG::compile(e->template cast(), ctx, cg, frag); + case Expression::E_VARDECL: + case Expression::E_TI: + case Expression::E_TIID: + throw InternalError("Bytecode generator encountered unexpected expression type in compile."); + } +} + +CG_Cond::T CG::compile(Expression* e, CodeGen& cg, CG_Builder& frag) { + assert(e->type().isbool()); + try { + CG_Cond::T r(cg.cache_lookup(e).second); + return r; + } catch (const CG_Env::NotFound& exn) { + CG_Cond::T cond(_compile(e, cg, frag)); + CG::Binding b(0xdeadbeef, cond); + cg.cache_store(e, b); + return cond; + } + // return _compile(e, cg, frag); // FIXME: Update env representation. +} + +CG_Cond::T CG::compile(Id* x, Mode ctx, CodeGen& cg, CG_Builder& frag) { + try { + CG::Binding b(cg.env().lookup(x->v())); + // Check if there is a complex condition + if (b.second.get()) { + // If so then the register is a placeholder and the condition holds the value + assert(b.first == 0xdeadbeef); + return b.second; + } else { + // Otherwise the value should be in the register + return CG_Cond::reg(b.first, x->type().ispar()); + } + } catch (const CG_Env::NotFound& exn) { + VarDecl* vd = follow_id_to_decl(x)->cast(); + int g = cg.find_global(vd); + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::LOAD_GLOBAL, CG::g(g), CG::r(r)); + return CG_Cond::reg(r, x->type().ispar()); + } +} + +CG_Cond::T CG::compile(ArrayAccess* a, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // If the array elements are Boolean, we need to check for partiality + // in the indices, and that the accesses are within-range. + ASTExprVec idx(a->idx()); + Expression* A(a->v()); + int sz(idx.size()); + + bool is_var = false; + for (int ii = 0; ii < sz; ++ii) { + is_var = is_var || idx[ii]->type().isvar(); + } + + // Evaluate the indices, put them in registers. + // Collect partiality of the expression. + std::vector cond; + + // Now evaluate the array body, and emit the indices. + std::vector r_idxs(sz); + for (int ii = 0; ii < sz; ++ii) { + CG::Binding b(CG::bind(idx[ii], cg, frag)); + r_idxs[ii] = b.first; + cond.push_back(b.second); + } + + CG::Binding b_A(CG::bind(A, cg, frag)); + int r_A(b_A.first); + cond.push_back(b_A.second); + + if (is_var) { + GCLock lock; + std::vector types(sz + 2, Type::varint()); + types[0] = Type::varbool(); + types[sz + 1] = Type::varbool(sz); + + std::vector r_args(sz + 1); + for (int ii = 0; ii < sz; ++ii) { + r_args[ii] = CG::r(r_idxs[ii]); + } + r_args[sz] = CG::r(r_A); + + cond.push_back(CG_Cond::call({"element"}, -ctx, true, types, r_args)); + return CG_Cond::forall(ctx, cond); + } else { + // Just read the vector, and get the appropriate element. + int r; + CG_Cond::T ncond; + std::tie(r, ncond) = execute_array_access(r_A, r_idxs, cg, frag); + cond.push_back(ncond); + cond.push_back(CG_Cond::reg(r, a->type().ispar())); + return CG_Cond::forall(ctx, cond); + } +} + +CG_Cond::T CG::compile(ITE* ite, Mode ctx, CodeGen& cg, CG_Builder& frag) { + int sz(ite->size()); + + // Check whether all the conditions are par. + bool all_par = true; + for (int ii = 0; ii < sz; ++ii) { + if (!ite->e_if(ii)->type().ispar()) { + all_par = false; + break; + } + } + + if (all_par) { + // We need to interfere with the env, here, because stuff may not be available. + // Except, ite->e_if(0) will always be available. + int l_end(GET_LABEL(cg)); + int r_ret(GET_REG(cg)); + for (int ii = 0; ii < sz; ++ii) { + int r_sel = CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag); + int l_cont(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_sel), CG::l(l_cont)); + cg.env_push(); + if (!ite->e_then(ii)->type().ispar()) all_par = false; + int r_val = CG::force(CG::compile(ite->e_then(ii), cg, frag), ctx, cg, frag); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_val), CG::r(r_ret)); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(l_end)); + cg.env_pop(); + cg.env_push(); + PUSH_LABEL(frag, l_cont); + } + // Else case. + if (!ite->e_else()->type().ispar()) all_par = false; + int r_val = CG::force(CG::compile(ite->e_else(), cg, frag), ctx, cg, frag); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_val), CG::r(r_ret)); + PUSH_LABEL(frag, l_end); + // Now kill the availability of all the expressions. + for (int ii = 0; ii < sz; ++ii) cg.env_pop(); + return CG_Cond::reg(r_ret, all_par); + } else { + GCLock lock; + + // Collect conditions + OPEN_VEC(cg, frag); + for (int ii = 0; ii < sz; ++ii) { + int r_if(CG::force(CG::compile(ite->e_if(ii), cg, frag), BytecodeProc::FUN, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_if)); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(1, cg, frag))); + CLOSE_AGG(cg, frag); + int r_if(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_if)); + + // Collect results + OPEN_VEC(cg, frag); + for (int ii = 0; ii < sz; ++ii) { + int r_then(CG::force(CG::compile(ite->e_then(ii), cg, frag), ctx, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_then)); + } + { + int r_else(CG::force(CG::compile(ite->e_else(), cg, frag), ctx, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r_else)); + } + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(bind_cst(1, cg, frag))); + CLOSE_AGG(cg, frag); + int r_then(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_then)); + + return CG_Cond::call({"if_then_else"}, BytecodeProc::FUN, true, + {Type::varbool(), Type::varbool(1), Type::varbool(1)}, + {CG::r(r_if), CG::r(r_then)}); + } +} + +CG_Cond::T shortcut_par_or(Mode ctx, CodeGen& cg, CG_Builder& frag, Expression* lhs, + Expression* rhs) { + Mode f_mode(ctx.strength(), false); // Check + assert(lhs->type().ispar()); + int r(GET_REG(cg)); + int l_exit(GET_LABEL(cg)); + int r_lhs(CG::force(CG::compile(lhs, cg, frag), f_mode, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_lhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(r), CG::l(l_exit)); + cg.env_push(); + int r_rhs(CG::force(CG::compile(rhs, cg, frag), f_mode, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_rhs), CG::r(r)); + cg.env_pop(); + PUSH_LABEL(frag, l_exit); + return CG_Cond::reg(r, rhs->type().ispar()); +} + +CG_Cond::T shortcut_par_and(Mode ctx, CodeGen& cg, CG_Builder& frag, Expression* lhs, + Expression* rhs) { + Mode f_mode(ctx.strength(), false); // Check + assert(lhs->type().ispar()); + int r(GET_REG(cg)); + int l_exit(GET_LABEL(cg)); + int r_lhs(CG::force(CG::compile(lhs, cg, frag), f_mode, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_lhs), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r), CG::l(l_exit)); + cg.env_push(); + int r_rhs(CG::force(CG::compile(rhs, cg, frag), f_mode, cg, frag)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_rhs), CG::r(r)); + cg.env_pop(); + PUSH_LABEL(frag, l_exit); + return CG_Cond::reg(r, rhs->type().ispar()); +} + +CG_Cond::T CG::compile(BinOp* b, Mode ctx, CodeGen& cg, CG_Builder& frag) { + std::vector cond; + // For anything we force here, its calling mode is considered positive (because + // it needs to mean what we said.) + CG::Mode f_mode(ctx.strength(), false); + if (b->lhs()->type().dim() > 0) { + GCLock lock; + assert(b->rhs()->type().dim() > 0); + assert(b->op() == BOT_EQ || b->op() == BOT_EQUIV); + int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag); + int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag); + cond.push_back(CG_Cond::call( + {"op_equals"}, BytecodeProc::FUN, true, + {b->type().isvar() ? Type::varbool() : Type::parbool(), b->lhs()->type(), b->rhs()->type()}, + {CG::r(r_lhs), CG::r(r_rhs)})); + return CG_Cond::forall(ctx, cond); + } + if (b->op() == BOT_AND) { + if (b->lhs()->type().ispar()) return shortcut_par_and(ctx, cg, frag, b->lhs(), b->rhs()); + if (b->rhs()->type().ispar()) return shortcut_par_and(ctx, cg, frag, b->rhs(), b->lhs()); + } else if (b->op() == BOT_OR) { + if (b->lhs()->type().ispar()) return shortcut_par_or(ctx, cg, frag, b->lhs(), b->rhs()); + if (b->rhs()->type().ispar()) return shortcut_par_or(ctx, cg, frag, b->rhs(), b->lhs()); + } + if (b->type().ispar()) { + int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag); + int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag); + std::vector r_cond; + for (CG_Cond::T c : cond) { + // r_cond.push_back(CG::force(c, ctx, cg, frag)); + // FIXME: This is probably safe, assuming all the conditions + // are also par. We need these to be in their literal, + // non-context-adjusted modes for the disentailment check to work. + // If we want to handle non-par conditions, we probably need to + // force in ctx, then flip back. + r_cond.push_back(CG::force(c, f_mode, cg, frag)); + } + int r_ret; + if (b->lhs()->type().isintset()) { + r_ret = bind_binop_par_set(cg, frag, b->op(), r_lhs, r_rhs); + } else { + r_ret = bind_binop_par_int(cg, frag, b->op(), r_lhs, r_rhs); + } + if (r_cond.size() > 0) { + // If any conditions don't hold, evaluate to false. + int r(GET_REG(cg)); + int l_tl(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(r)); + for (int r_c : r_cond) { + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(r_c), CG::l(l_tl)); + } + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(r_ret), CG::r(r)); + PUSH_LABEL(frag, l_tl); + return CG_Cond::reg(r, true); + } else { + return CG_Cond::reg(r_ret, true); + } + } + switch (b->op()) { + case BOT_LE: + case BOT_LQ: + case BOT_GR: + case BOT_GQ: + case BOT_EQ: + case BOT_EQUIV: + case BOT_NQ: { + // Potentially partial. + int r_lhs = CG::force_or_bind(b->lhs(), f_mode, cond, cg, frag); + int r_rhs = CG::force_or_bind(b->rhs(), f_mode, cond, cg, frag); + cond.push_back(linear_cond(cg, frag, b->op(), ctx, r_lhs, r_rhs)); + return CG_Cond::forall(ctx, cond); + } break; + case BOT_XOR: { + CG_Cond::T c_lhs = CG::compile(b->lhs(), cg, frag); + CG_Cond::T c_rhs = CG::compile(b->rhs(), cg, frag); + return CG_Cond::forall(ctx, CG_Cond::exists(ctx, c_lhs, c_rhs), + CG_Cond::exists(ctx, ~c_lhs, ~c_rhs)); + } break; + case BOT_AND: { + cond.push_back(CG::compile(b->lhs(), cg, frag)); + cond.push_back(CG::compile(b->rhs(), cg, frag)); + return CG_Cond::forall(ctx, cond); + } break; + case BOT_OR: { + cond.push_back(CG::compile(b->lhs(), cg, frag)); + cond.push_back(CG::compile(b->rhs(), cg, frag)); + return CG_Cond::exists(ctx, cond); + } break; + case BOT_IMPL: { + cond.push_back(~CG::compile(b->lhs(), cg, frag)); + cond.push_back(CG::compile(b->rhs(), cg, frag)); + return CG_Cond::exists(ctx, cond); + } break; + case BOT_RIMPL: { + cond.push_back(CG::compile(b->lhs(), cg, frag)); + cond.push_back(~CG::compile(b->rhs(), cg, frag)); + return CG_Cond::exists(ctx, cond); + } + case BOT_IN: { + CG::Binding b_lhs(CG::bind(b->lhs(), cg, frag)); + CG::Binding b_rhs(CG::bind(b->rhs(), cg, frag)); + assert(b->rhs()->type().ispar()); + cond.push_back(b_lhs.second); + cond.push_back(b_rhs.second); + if (ctx == BytecodeProc::ROOT) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(b_lhs.first), CG::r(b_rhs.first), + CG::r(r)); + // If INTERSECT_DOMAIN fails, then the interpreter will mark inconsistent and ABORT + cond.push_back(CG_Cond::ttt()); + } else if (ctx == BytecodeProc::ROOT_NEG) { + int r(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::DOM, CG::r(b_lhs.first), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::DIFF, CG::r(r), CG::r(b_rhs.first), CG::r(r)); + PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(b_lhs.first), CG::r(r), CG::r(r)); + // If INTERSECT_DOMAIN fails, then the interpreter will mark inconsistent and ABORT + cond.push_back(CG_Cond::ttt()); + } else { + GCLock lock; + cond.push_back(CG_Cond::call({"set_in"}, ctx, true, + {Type::varbool(), Type::varint(), Type::varsetint()}, + {CG::r(b_lhs.first), CG::r(b_rhs.first)})); + } + return CG_Cond::forall(ctx, cond); + } + default: + break; + } + throw InternalError("Unexpected fall-through in compilation of BinOp."); +} + +CG_Cond::T CG::compile(UnOp* u, Mode ctx, CodeGen& cg, CG_Builder& frag) { + assert(u->op() == UOT_NOT); + return ~CG::compile(u->e(), cg, frag); +} + +CG_Cond::T compile_call(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + int sz = call->n_args(); + std::vector r_arg(sz); + std::vector p_arg; + + // Evaluate the args, collecting the conditionality. + std::vector arg_types(sz + 1); + arg_types[0] = call->type(); + for (int ii = 0; ii < sz; ++ii) { + arg_types[ii + 1] = call->arg(ii)->type(); + r_arg[ii] = CG::r(CG::force_or_bind(call->arg(ii), BytecodeProc::FUN, p_arg, cg, frag)); + } + + // And finally, add the call itself + p_arg.push_back(CG_Cond::call(call->id(), ctx, call->type().isvar(), arg_types, r_arg)); + return CG_Cond::forall(ctx, p_arg); +} + +CG_Cond::T CG::compile(Call* call, Mode ctx, CodeGen& cg, CG_Builder& frag) { + // If we have a builtin for this, dispatch to that instead. + { + GCLock gc; + auto it(builtins().find(call->id().str())); + if (it != builtins().end()) { + return it->second.boolean(call, ctx, cg, frag); + } + } + + return compile_call(call, ctx, cg, frag); +} + +void eval_let_body(Let* let, BytecodeProc::Mode ctx, CodeGen& cg, CG_Builder& frag, + std::vector& conj) { + ASTExprVec bindings(let->let()); + for (Expression* e : bindings) { + if (auto vd = e->dyn_cast()) { + // Bind the new definitions in context + CodeGen::Binding to_bind(0xdeadbeef, CG_Cond::ttt()); + if (vd->e() && vd->type().isbool()) { + CG_Cond::T cond = CG::compile(vd->e(), cg, frag); + if (!cond.get()) { + to_bind.first = bind_cst(cond.sign(), cg, frag); + } else { + to_bind.second = cond; + } + } else if (vd->e()) { + // FIXME: Assuming domain isn't constraining. + CG::Binding b_v(CG::bind(vd->e(), cg, frag)); + to_bind.first = b_v.first; + conj.push_back(b_v.second); + if (Expression* d = vd->ti()->domain()) { + if (!vd->ti()->isarray()) { + CG::Binding b_d = CG::bind(d, cg, frag); // Discarding any constraints on the set. + conj.push_back(b_d.second); + int r_dp = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::INTERSECT_DOMAIN, CG::r(to_bind.first), + CG::r(b_d.first), CG::r(r_dp)); + } + } + } else { + // Variable declaration. Assumes is total and nonempty. + CG::Binding b_d(bind_domain(vd, cg, frag)); + to_bind.first = GET_REG(cg); + if (!vd->ti()->isarray()) { + conj.push_back(b_d.second); + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::ROOT, cg.find_builtin("mk_intvar"), + CG::i(0), CG::r(b_d.first)); + } else { + int rTMP(GET_REG(cg)); + std::vector r_regs; + for (Expression* r : vd->ti()->ranges()) { + Expression* dim(r->template cast()->domain()); + CG::Binding b_reg(CG::bind(dim, cg, frag)); + // post_cond(cg, frag, b_reg.second); + r_regs.push_back(b_reg.first); + } + std::vector nesting; + OPEN_VEC(cg, frag); + for (int r_r : r_regs) { + Forset iter(cg, r_r); + nesting.push_back(iter); + iter.emit_pre(frag); + } + PUSH_INSTR(frag, BytecodeStream::CALL, BytecodeProc::ROOT, cg.find_builtin("mk_intvar"), + CG::i(0), CG::r(b_d.first)); + for (int r_i = r_regs.size() - 1; r_i >= 0; --r_i) { + nesting[r_i].emit_post(frag); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(to_bind.first)); + OPEN_VEC(cg, frag); + for (int r_r : r_regs) { + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_r), CG::r(bind_cst(1, cg, frag)), + CG::r(rTMP)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rTMP)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r_r), CG::r(bind_cst(2, cg, frag)), + CG::r(rTMP)); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(rTMP)); + } + CLOSE_AGG(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(rTMP)); + + PUSH_INSTR(frag, BytecodeStream::BUILTIN, cg.find_builtin("array_Xd"), + CG::r(to_bind.first), CG::r(rTMP)); + } + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(to_bind.first)); + } + cg.env().bind(vd->id()->v(), to_bind); + } else { + conj.push_back(CG::compile(e, cg, frag)); + } + } +} + +CG_Cond::T CG::compile(Let* let, Mode ctx, CodeGen& cg, CG_Builder& frag) { + std::vector conj; + + cg.env_push(); + + if (let->ann().contains(constants().ann.promise_total)) { + OPEN_OTHER(cg, frag); + eval_let_body(let, BytecodeProc::ROOT, cg, frag, conj); + // int r = CG::force(CG_Cond::forall(ctx, conj), BytecodeProc::ROOT, cg, frag); + post_cond(cg, frag, CG_Cond::forall(BytecodeProc::ROOT, conj)); + int r = CG::force(CG::compile(let->in(), cg, frag), ctx, cg, frag); + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + CLOSE_AGG(cg, frag); + r = GET_REG(cg); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r)); + conj.clear(); + conj.push_back(CG_Cond::reg(r, let->in()->type().ispar())); + } else { + eval_let_body(let, BytecodeProc::ROOT, cg, frag, conj); + conj.push_back(CG::compile(let->in(), cg, frag)); + } + cg.env_pop(); + return CG_Cond::forall(ctx, conj); +} + +int CG::vec2array(std::vector vec, CodeGen& cg, CG_Builder& frag) { + int sz(vec.size()); + OPEN_VEC(cg, frag); + for (int ii = 0; ii < sz; ++ii) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(vec[ii])); + } + CLOSE_AGG(cg, frag); + int r_vec(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(r_vec)); + + return r_vec; +} +}; // namespace MiniZinc diff --git a/lib/codegen/analysis.cpp b/lib/codegen/analysis.cpp new file mode 100644 index 0000000..beb5110 --- /dev/null +++ b/lib/codegen/analysis.cpp @@ -0,0 +1,215 @@ +#include + +#include <../lib/codegen/analysis.hpp> + +namespace MiniZinc { + +/// Visit identifier +void ModeAnalysis::vId(Id& x, Occurrence o, CG::Mode m) { + // Use sites follow + VarDecl* decl(x.decl()); + if (o == Use && decl) update(decl, o, m); +} +/// Visit array literal +void ModeAnalysis::vArrayLit(ArrayLit& a, Occurrence o, CG::Mode m) { + // If the array contents are Boolean, this doesn't count as an occurrence; + // we care about the context of the access. + // But for anything else, the partiality emerges at the occurrence site. + int sz(a.size()); + for (int ii = 0; ii < sz; ++ii) { + Expression* elt(a[ii]); + if (o == Use || elt->type().bt() != Type::BT_BOOL) { + update(elt, o, m); + } + } +} +/// Visit array access +void ModeAnalysis::vArrayAccess(ArrayAccess& a, Occurrence o, CG::Mode m) { + ASTExprVec idx(a.idx()); + for (int ii = 0; ii < idx.size(); ++ii) update(idx[ii], o, m); + if (a.type().bt() == Type::BT_BOOL) + update(a.v(), o, +m); + else + update(a.v(), o, m); +} +/// Visit array comprehension +void ModeAnalysis::vComprehension(Comprehension& comp, Occurrence o, CG::Mode m) { + // Comprehension behaves like an array access: + // The current mode propagates to the generators, but only to non-Boolean bodies. + if (o == Def) { + int g_n(comp.n_generators()); + for (int gg = 0; gg < g_n; ++gg) { + int d_n(comp.n_decls(gg)); + for (int d = 0; d < d_n; ++d) update(comp.decl(gg, d), o, m); + update(comp.in(gg), o, m); + if (comp.where(gg)) { + update(comp.where(gg), Use, BytecodeProc::FUN); + } + } + } + if (o == Use || comp.e()->type().bt() != Type::BT_BOOL) { + int g_n(comp.n_generators()); + for (int gg = 0; gg < comp.n_generators(); ++gg) { + if (comp.where(gg)) { + update(comp.where(gg), Use, BytecodeProc::FUN); + } + } + update(comp.e(), o, m); + } +} +/// Visit if-then-else +void ModeAnalysis::vITE(ITE& ite, Occurrence o, CG::Mode m) { + int sz(ite.size()); + for (int ii = 0; ii < sz; ++ii) { + // We need the condition to compute the value. + update(ite.e_if(ii), Use, BytecodeProc::FUN); + update(ite.e_then(ii), o, m); + } + update(ite.e_else(), o, m); +} + +/// Visit binary operator +void ModeAnalysis::vBinOp(BinOp& b, Occurrence o, CG::Mode m) { + switch (b.op()) { + case BOT_IMPL: { + CG::Mode m_c = m.is_neg() ? m : +m; + update(b.lhs(), o, -m_c); + update(b.rhs(), o, m_c); + break; + } + case BOT_RIMPL: { + CG::Mode m_c = m.is_neg() ? m : +m; + update(b.lhs(), o, m_c); + update(b.rhs(), o, -m_c); + break; + } + case BOT_OR: { + CG::Mode m_c = m.is_neg() ? m : +m; + update(b.lhs(), o, m_c); + update(b.rhs(), o, m_c); + break; + } + case BOT_AND: { + CG::Mode m_c = m.is_neg() ? +m : m; + update(b.lhs(), o, m_c); + update(b.rhs(), o, m_c); + break; + } + case BOT_EQ: + case BOT_NQ: { + CG::Mode m_l = b.lhs()->type().bt() == Type::BT_BOOL ? CG::Mode(BytecodeProc::FUN) : +m; + CG::Mode m_r = b.rhs()->type().bt() == Type::BT_BOOL ? CG::Mode(BytecodeProc::FUN) : +m; + update(b.lhs(), o, m_l); + update(b.rhs(), o, m_r); + } + case BOT_EQUIV: + case BOT_XOR: + update(b.lhs(), o, BytecodeProc::FUN); + update(b.rhs(), o, BytecodeProc::FUN); + break; + default: + update(b.lhs(), o, m); + update(b.rhs(), o, m); + } +} +/// Visit unary operator +void ModeAnalysis::vUnOp(UnOp& u, Occurrence o, CG::Mode m) { + switch (u.op()) { + case UOT_NOT: + update(u.e(), o, -m); + break; + default: + update(u.e(), o, m); + } +} + +/// Visit call +// TODO: Add special cases for builtin calls, and interprocedural analysis +// so we can get more precise mode analysis. +void ModeAnalysis::vCall(Call& call, Occurrence o, CG::Mode m) { + int sz(call.n_args()); + if (call.id() == constants().ids.forall) { + assert(sz == 1); + CG::Mode m_r(m.is_neg() ? +m : m); + update(call.arg(0), o, m_r); + } else if (call.id() == constants().ids.exists) { + assert(sz == 1); + CG::Mode m_r(m.is_neg() ? m : +m); + update(call.arg(0), o, m_r); + } else if (call.id() == constants().ids.clause) { + assert(sz == 2); + CG::Mode m_r(m.is_neg() ? m : +m); + update(call.arg(0), o, m_r); + update(call.arg(1), o, -m_r); + } else if (call.id() == constants().ids.assert) { + update(call.arg(0), Use, BytecodeProc::ROOT); + if (sz == 3) update(call.arg(2), o, m); + } else if (call.id() == "array1d") { + // No-op + update(call.arg(sz - 1), o, m); + } else if (call.id() == "index_set" || call.id() == "length") { + if (o == Def) update(call.arg(0), Def, m); + } else if (call.id() == "symmetry_breaking_constraint" || call.id() == "redundant_constraint") { + // No-op + update(call.arg(0), o, m); + } else { + // Propagate to the other functions. + for (int ii = 0; ii < sz; ++ii) { + Expression* arg(call.arg(ii)); + // If arg is non-Boolean, this the partiality gets embedded here. + // But if arg is Boolean, we have to assume the callee can do + // anything with it, so the occurrence is considered FUN. + if (arg->type().bt() == Type::BT_BOOL) { + update(arg, Use, BytecodeProc::FUN); + } else { + update(arg, o, m); + } + } + } +} +/// Visit let +void ModeAnalysis::vLet(Let& let, Occurrence o, CG::Mode m) { + ASTExprVec bindings(let.let()); + if (o == Def || let.type().bt() == Type::BT_BOOL) { + // If the let has Boolean type, the _use_ of the let defines + // the def-mode of the bound variables. + for (Expression* e : bindings) { + // Check whether this is a decl with a def. + if (auto vd = e->dyn_cast()) { + if (vd->type().bt() != Type::BT_BOOL) update(vd, Def, m); + /* + if(Expression* v_e = vd->e()) { + if(!v_e->type().isbool()) { + update(v_e, Def, m); + } + } + */ + } else { + // Must be a constraint, so it's a use. + assert(e->type().isbool()); + update(e, Use, m); + } + } + update(let.in(), o, m); + } else { + // Non-Boolean use; the partiality is in an enclosing + // context. + update(let.in(), o, m); + } +} +/// Visit variable declaration +void ModeAnalysis::vVarDecl(VarDecl& decl, Occurrence o, CG::Mode m) { + if (Id* x = decl.id()) update(x, o, m); + if (Expression* d = decl.ti()->domain()) { + if (o == Def) { + update(d, Def, m); + update(d, Use, m); + } + } + if (Expression* e = decl.e()) update(e, o, m); +} + +void ModeAnalysis::vFunctionI(FunctionI& fun, Occurrence o, CG::Mode m) { + // DO STUFF +} +}; // namespace MiniZinc diff --git a/lib/codegen/analysis.hpp b/lib/codegen/analysis.hpp new file mode 100644 index 0000000..74d36c9 --- /dev/null +++ b/lib/codegen/analysis.hpp @@ -0,0 +1,256 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Graeme Gange + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_CODEGEN_ANALYSIS_HPP__ +#define __MINIZINC_CODEGEN_ANALYSIS_HPP__ +#include +#include +#include + +#include + +// From a given top-level expression and +// mode, what is the weakest mode covering +// all occurrences of each sub-expression? +namespace MiniZinc { + +static void annotate_total(FunctionI* func) { + class AnnotateTotal : public EVisitor { + public: + void vLet(Let& let) { + let.addAnnotation(constants().ann.promise_total); + ASTExprVec bindings(let.let()); + for (auto expr : bindings) { + if (expr->eid() != Expression::E_VARDECL) { + // Must be a constraint, so it's a use. + assert(expr->type().isbool()); + expr->addAnnotation(constants().ann.promise_total); + } + } + } + } _at; + if (func->ann().contains(constants().ann.promise_total)) { + topDown(_at, func->e()); + } +} + +class ModeAnalysis { + enum Occurrence { Def = 0, Use = 1 }; + +public: + // There are two relevant modes for an expression: the mode + // where the value is introduced, and the mode where the value + // is used. + // This distinction mostly matters for let-bindings or definitions + // which construct results containing Booleans (i.e. array [int] of var bool). + // At the end, Boolean values are built according to their use-context, and + // non-Boolean values by their def-context. + + // We use user_flag0 and user_flag1 to track whether + // the definition and use-modes are queued. + inline void enqueue(Expression* e, Occurrence o) { + if (e->isUnboxedVal()) return; + if (o == Def) { + if (!e->user_flag0()) { + e->user_flag0(true); + worklist.push(reinterpret_cast(e)); + } + } else { + if (!e->user_flag1()) { + e->user_flag1(true); + worklist.push(reinterpret_cast(e) | 1); + } + } + } + + void update(Expression* e, Occurrence o, CG::Mode m) { + if (e->type().isbool()) { + if (o == Def) { + // We only keep Use-modes for Boolean things. + return; + } + if (e->ann().contains(constants().ann.promise_total)) { + m = BytecodeProc::ROOT; + } + } + ExprMap::t& t(o == Def ? def_map : use_map); + auto it(t.find(e)); + if (it == t.end()) { + // First time we've seen e. + t.insert(std::make_pair(e, m)); + } else { + if (it->second.is_submode(m)) return; + it->second = it->second.join(m); + } + enqueue(e, o); + } + + void update(FunctionI* fun, Occurrence o, CG::Mode m) { + // We only keep Use-modes for Boolean things. + /* + if(o == Def) { + if(e->type().isbool()) + return; + if(e->ann().contains(constants().ann.promise_total)) + m = BytecodeProc::ROOT; + } + ExprMap::t& t(o == Def ? def_map : use_map); + auto it(t.find(e)); + if(it == t.end()) { + // First time we've seen e. + t.insert(std::make_pair(e, m)); + } else { + if(it->second.is_submode(m)) + return; + it->second = it->second.join(m); + } + enqueue(e, o); + */ + } + + // Better to make this fifo, but... whatever. + void process(void) { + while (!worklist.empty()) { + uintptr_t tag(worklist.front()); + worklist.pop(); + Expression* e(reinterpret_cast(tag & ~((uintptr_t)1))); + Occurrence o(static_cast(tag & 1)); + // Clear the queued-ness flags + if (o == Def) + e->user_flag0(false); + else + e->user_flag1(false); + + // Get the updated mode for e. + ExprMap::t& t(o == Def ? def_map : use_map); + CG::Mode m(t.at(e)); + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_SETLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + break; + case Expression::E_ID: + vId(*e->template cast(), o, m); + break; + case Expression::E_ARRAYLIT: + vArrayLit(*e->template cast(), o, m); + break; + case Expression::E_ARRAYACCESS: + vArrayAccess(*e->template cast(), o, m); + break; + case Expression::E_COMP: + vComprehension(*e->template cast(), o, m); + break; + case Expression::E_ITE: + vITE(*e->template cast(), o, m); + break; + case Expression::E_BINOP: + vBinOp(*e->template cast(), o, m); + break; + case Expression::E_UNOP: + vUnOp(*e->template cast(), o, m); + break; + case Expression::E_CALL: + vCall(*e->template cast(), o, m); + break; + case Expression::E_VARDECL: + vVarDecl(*e->template cast(), o, m); + break; + case Expression::E_LET: + vLet(*e->template cast(), o, m); + break; + /* + case Expression::E_TI: + _t.vTypeInst(*c._e->template cast()); + break; + case Expression::E_TIID: + _t.vTIId(*c._e->template cast()); + break; + */ + default: + throw InternalError("Mode analyser encounted unexpected expression type."); + } + } + } + + ModeAnalysis(void) {} + + void def(Expression* e, CG::Mode m) { update(e, Def, m); } + + void use(Expression* e, CG::Mode m) { + if (e->type().isbool()) + update(e, Use, m); + else + update(e, Def, m); + } + + ExprMap::t extract(void) { + // Compute the fixpoint. + process(); + // Now fuse the two maps. + // Start from the def_map, and add any Boolean uses. + ExprMap::t fused(def_map); + for (auto p : use_map) { + /* + if(p.first->type().isbool()) + fused.insert(p); + */ + // If we haven't stored a def-mode, we should + // apply the use-mode. + fused.insert(p); + } + return fused; + } + /* + static ExprMap run(Expression* e, CG::Mode m) { + ModeAnalysis analyzer; + analyzer.update(e, Use, m); + analyzer.process(); + + // Now we fuse the two maps. + } + */ + + std::queue worklist; + ExprMap::t def_map; + ExprMap::t use_map; + + /// Visit identifier + void vId(Id&, Occurrence, CG::Mode); + /// Visit array literal + void vArrayLit(ArrayLit&, Occurrence, CG::Mode); + /// Visit array access + void vArrayAccess(ArrayAccess&, Occurrence, CG::Mode); + /// Visit array comprehension + void vComprehension(Comprehension&, Occurrence, CG::Mode); + /// Visit if-then-else + void vITE(ITE&, Occurrence, CG::Mode); + /// Visit binary operator + void vBinOp(BinOp&, Occurrence, CG::Mode); + /// Visit unary operator + void vUnOp(UnOp&, Occurrence, CG::Mode); + /// Visit call + void vCall(Call&, Occurrence, CG::Mode); + /// Visit let + void vLet(Let&, Occurrence, CG::Mode); + /// Visit variable declaration + void vVarDecl(VarDecl&, Occurrence, CG::Mode); + + // Process a function body. + void vFunctionI(FunctionI&, Occurrence, CG::Mode); +}; + +}; // namespace MiniZinc + +#endif diff --git a/lib/codegen/codegen_internal.hpp b/lib/codegen/codegen_internal.hpp new file mode 100644 index 0000000..28cf710 --- /dev/null +++ b/lib/codegen/codegen_internal.hpp @@ -0,0 +1,445 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Graeme Gange + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __MINIZINC_CODEGEN_INTERNAL_HPP__ +#define __MINIZINC_CODEGEN_INTERNAL_HPP__ +// Helper definitions for assorted bytecode generation stuff. +#include + +namespace MiniZinc { + +template +void PUSH_INSTR_OPERAND(CG_Instr& i, std::vector& vec) { + for (auto v : vec) PUSH_INSTR_OPERAND(i, v); +} +void PUSH_INSTR_OPERAND(CG_Instr& i, CG_Value x) { i.params.push_back(x); } +void PUSH_INSTR_OPERAND(CG_Instr& i, CG_ProcID p) { i.params.push_back(CG_Value::proc(p.p)); } +void PUSH_INSTR_OPERAND(CG_Instr& i, BytecodeProc::Mode m) { i.params.push_back(CG::i((int)m)); } +void PUSH_INSTR_OPERAND(CG_Instr& i, AggregationCtx::Symbol s) { + i.params.push_back(CG::i((int)s)); +} +void PUSH_INSTR_OPERANDS(CG_Instr& i) {} + +template +void PUSH_INSTR_OPERANDS(CG_Instr& i, T x, Args... args) { + PUSH_INSTR_OPERAND(i, x); + PUSH_INSTR_OPERANDS(i, args...); +} + +template +void PUSH_INSTR(CG_Builder& cg, BytecodeStream::Instr i, Args... args) { + cg.instrs.push_back(CG_Instr::instr(i)); + PUSH_INSTR_OPERANDS(cg.instrs.back(), args...); +} + +void PUSH_LABEL(CG_Builder& frag, unsigned int label) { + frag.instrs.push_back(CG_Instr::label(label)); +} + +// Basic generator manipulation. +inline int GET_LABEL(CodeGen& cg) { return cg.current_label_count++; } +inline int GET_REG(CodeGen& cg) { return cg.current_reg_count++; } + +struct REG { + REG(int _r) : r(_r) {} + void operator()(CodeGen& cg, CG_Builder& frag) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + } + int r; +}; + +// Combinators for slightly safer code generation. +struct PUSH_REG { + PUSH_REG(int _r) : r(_r) {} + void operator()(CodeGen& cg, CG_Builder& frag) { + PUSH_INSTR(frag, BytecodeStream::PUSH, CG::r(r)); + } + int r; +}; +struct PUSH { + PUSH(void) {} + PUSH_REG operator()(int r) { return PUSH_REG(r); } +}; + +template +struct Retn { + Retn(T _x) : x(_x) {} + + T operator()(CodeGen& cg, CG_Builder& frag) const { return x; } + T x; +}; +struct RETN { + RETN() {} + + template + Retn operator()(T x) { + return Retn(x); + } +}; + +/* +template +struct _LET { + V v; + E e; + auto operator()(CodeGen& cg, CG_Builder& frag) -> decltype(e(0)(cg)) { + // + int reg(GET_REG(cg)); + PUSH_INSTR(frag, BytecodeStream::OPEN_AGGREGATION, AggregationCtx::VCTX_OTHER); + v(cg, frag); + PUSH_INSTR(frag, BytecodeStream::POP, CG::r(reg)); + PUSH_INSTR(frag, BytecodeStream::CLOSE_AGGREGATION); + return e(reg)(cg, frag); + } +}; +template +_LET LET(V&& v, E&& e) { return _LET { std::move(v), std::move(e) }; } +*/ + +// Iterating over various things -- vectors, interleaved vectors, and sets. +template +struct _FOREACH { + V v; + E e; + void operator()(CodeGen& cg, CG_Builder& frag) { + int r(v(cg, frag)); // Get the register for v. + int rB(GET_REG(cg)); + int rE(GET_REG(cg)); + int rV(GET_REG(cg)); + int lblH(GET_LABEL(cg)); + int lblE(GET_LABEL(cg)); + // Set up the iterators + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(rB)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r), CG::r(rE)); + // Check if the vec is non-empty + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rV)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rV), CG::l(lblE)); + PUSH_LABEL(frag, lblH); + // Dereference the iterator + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rV)); + // Emit code for the body + e(rV)(cg, frag); + // Now increment and loop back. + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rV)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rV), CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } +}; +template +_FOREACH FOREACH(V&& v, E&& e) { + return _FOREACH{std::move(v), std::move(e)}; +} + +// Slightly nicer version of FOREACH. +template +void ITER_ARRAY(CodeGen& cg, CG_Builder& frag, int r, E e) { + int rV(GET_REG(cg)); + int lblH(GET_LABEL(cg)); + int lblE(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::ITER_ARRAY, CG::r(r), CG::l(lblE)); + PUSH_LABEL(frag, lblH); + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(rV)); + e(cg, frag, rV); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH)); + PUSH_LABEL(frag, lblE); +} +template +void ITER_SET(CodeGen& cg, CG_Builder& frag, int r, E e) { + int r_elt(GET_REG(cg)); + int r_range_min(GET_REG(cg)); + int r_range_max(GET_REG(cg)); + int lblH1(GET_LABEL(cg)); + int lblH2(GET_LABEL(cg)); + int lblE(GET_LABEL(cg)); + PUSH_INSTR(frag, BytecodeStream::ITER_VEC, CG::r(r), CG::l(lblE)); + PUSH_LABEL(frag, lblH1); + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r_range_min)); + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r_range_max)); + PUSH_INSTR(frag, BytecodeStream::ITER_RANGE, CG::r(r_range_min), CG::r(r_range_max), + CG::l(lblH1)); + PUSH_LABEL(frag, lblH2); + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(r_elt)); + e(cg, frag, r_elt); + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH2)); + PUSH_LABEL(frag, lblE); +} + +// Same as FOREACH, but when working with a vector of pairs (i.e. sets) +/* +template +struct _FOREACH2 { + V v; + E e; + auto operator()(CodeGen& cg, CG_Builder& frag) -> decltype(e(0,0)(cg, frag)) { + int r(v(cg, frag)); // Get the register for v. + int rB(GET_REG(cg)); + int rE(GET_REG(cg)); + int rV1(GET_REG(cg)); + int rV2(GET_REG(cg)); + int lblH(GET_LABEL(cg)); + int lblE(GET_LABEL(cg)); + // Set up the iterators + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(0), CG::r(rB)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r), CG::r(rE)); + // Check if the vec is non-empty + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rB), CG::r(rE), CG::r(rV1)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rV1), CG::l(lblE)); + PUSH_LABEL(frag, lblH); + // Dereference the iterator + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rV1)); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rV2)); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); + // Emit code for the body + e(rV1, rV2)(cg, frag); + // Now increment and loop back. + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rB), CG::r(rE), CG::r(rV1)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rV1), CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } +}; +template +_FOREACH2 FOREACH2(V v, E e) { return _FOREACH2 { v, e }; } + +template +struct _FORRANGE { + int rL; + int rU; + E e; + _FORRANGE(int _rL, int _rU, E _e) : rL(_rL), rU(_rU), e(_e) { } + + auto operator()(CodeGen& cg, CG_Builder& frag) -> decltype(e(0)(cg, frag)) { + int lblH(GET_LABEL(cg)); + int lblE(GET_LABEL(cg)); + int rV(GET_REG(cg)); + int rC(GET_REG(cg)); + // Check if the range is non-empty. + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rL), CG::r(rU), CG::r(rC)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblE)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(rL), CG::r(rV)); + PUSH_LABEL(frag, lblH); + // Emit code for the body + e(rV)(cg, frag); + // Now increment and loop back. + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rV)); + PUSH_INSTR(frag, BytecodeStream::LTI, CG::r(rV), CG::r(rU), CG::r(rC)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rC), CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } +}; +template +_FORRANGE FORRANGE(int rL, int rU, E e) { return _FORRANGE(rL, rU, e); } + + +// We can use FOREACH2 and FORRANGE to iterate over sets. +template +struct _FORSET { + V v; + E e; + _FORSET(V&& _v, E&& _e) : v(_v), e(_e) { } + + void operator()(CodeGen& cg, CG_Builder& frag) { + FOREACH2(v, [this](int rL, int rU) { return FORRANGE(rL, rU, e); })(cg, frag); + } +}; +template +_FORSET FORSET(V&& v, E&& e) { return _FORSET(std::move(v), std::move(e)); } +*/ + +// Non-combinator versions of the iteration generators. +// Less safe, because they don't automatically resolve containment, but more convenient +// if, say, we need unbounded + +struct EmitPost { + virtual ~EmitPost(void){}; + virtual void emit_post(CG_Builder& frag) = 0; + virtual int cont(void) = 0; +}; + +struct Foreach : public EmitPost { +#if 0 + Foreach(CodeGen& _cg, int _r, int k = 1) + : cg(_cg), lblCont(-1), lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)) + , r(_r), rB(GET_REG(cg)), rE(GET_REG(cg)) { + assert(k > 0); + for(int ii = 0; ii < k; ++ii) + rVS.push_back(GET_REG(cg)); + } + + void emit_pre(CG_Builder& frag) { + // Set up the iterators + PUSH_INSTR(frag, BytecodeStream::IMMI, CG::i(1), CG::r(rB)); + PUSH_INSTR(frag, BytecodeStream::LENGTH, CG::r(r), CG::r(rE)); + // Check if the vec is non-empty + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rVS[0])); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rVS[0]), CG::l(lblE)); + PUSH_LABEL(frag, lblH); + // Dereference the iterator + for(int rv : rVS) { + PUSH_INSTR(frag, BytecodeStream::GET_VEC, CG::r(r), CG::r(rB), CG::r(rv)); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rB)); + } + } + + void emit_post(CG_Builder& frag) { + // Now increment and loop back. + if(lblCont != -1) + PUSH_LABEL(frag, lblCont); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rB), CG::r(rE), CG::r(rVS[0])); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rVS[0]), CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } + + int val(void) const { return rVS[0]; } + int val(int i) const { return rVS[i]; } + int cont(void) { + if(lblCont == -1) + lblCont = GET_LABEL(cg); + return lblCont; + } + + CodeGen& cg; + int lblCont; + int lblH; + int lblE; + int r; + int rB; + int rE; + std::vector rVS; +#else + Foreach(CodeGen& _cg, int _r, int k = 1) + : cg(_cg), lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)), r(_r) { + assert(k > 0); + for (int ii = 0; ii < k; ++ii) rVS.push_back(GET_REG(cg)); + } + + void emit_pre(CG_Builder& frag) { + // Set up the iterators + PUSH_INSTR(frag, BytecodeStream::ITER_ARRAY, CG::r(r), CG::l(lblE)); + PUSH_LABEL(frag, lblH); + // Dereference the iterator + for (int rv : rVS) { + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(rv)); + } + } + + void emit_post(CG_Builder& frag) { + // Now increment and loop back. + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } + + int val(void) const { return rVS[0]; } + int val(int i) const { return rVS[i]; } + int cont(void) { return lblH; } + + CodeGen& cg; + int lblH; + int lblE; + int r; + std::vector rVS; +#endif +}; + +struct Forrange : public EmitPost { +#if 0 + Forrange(CodeGen& _cg, int _rL, int _rU) + : cg(_cg) + , lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)), lblCont(-1) + , rL(_rL), rU(_rU) + , rV(GET_REG(cg)), rC(GET_REG(cg)) { + + } + + void emit_pre(CG_Builder& frag) { + // Check if the range is non-empty. + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rL), CG::r(rU), CG::r(rC)); + PUSH_INSTR(frag, BytecodeStream::JMPIFNOT, CG::r(rC), CG::l(lblE)); + PUSH_INSTR(frag, BytecodeStream::MOV, CG::r(rL), CG::r(rV)); + PUSH_LABEL(frag, lblH); + } + + void emit_post(CG_Builder& frag) { + // Now increment and loop back. + if(lblCont != -1) + PUSH_LABEL(frag, lblCont); + PUSH_INSTR(frag, BytecodeStream::INCI, CG::r(rV)); + PUSH_INSTR(frag, BytecodeStream::LEI, CG::r(rV), CG::r(rU), CG::r(rC)); + PUSH_INSTR(frag, BytecodeStream::JMPIF, CG::r(rC), CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } + int val(void) const { return rV; } + int cont(void) { + if(lblCont == -1) + lblCont = GET_LABEL(cg); + return lblCont; + } + + CodeGen& cg; + int lblH; + int lblE; + int lblCont; + int rL; + int rU; + int rV; + int rC; +#else + Forrange(CodeGen& _cg, int _rL, int _rU) + : cg(_cg), lblH(GET_LABEL(cg)), lblE(GET_LABEL(cg)), rL(_rL), rU(_rU), rV(GET_REG(cg)) {} + + void emit_pre(CG_Builder& frag) { + // Set up the loop, and get the first element. + PUSH_INSTR(frag, BytecodeStream::ITER_RANGE, CG::r(rL), CG::r(rU), CG::l(lblE)); + PUSH_LABEL(frag, lblH); + PUSH_INSTR(frag, BytecodeStream::ITER_NEXT, CG::r(rV)); + } + + void emit_post(CG_Builder& frag) { + // Jump back if we made it. + PUSH_INSTR(frag, BytecodeStream::JMP, CG::l(lblH)); + PUSH_LABEL(frag, lblE); + } + + int val(void) const { return rV; } + int cont(void) { return lblH; } + + CodeGen& cg; + int lblH; + int lblE; + int rL; + int rU; + int rV; +#endif +}; + +struct Forset : public EmitPost { + Forset(CodeGen& cg, int r) : ranges(cg, r, 2), values(cg, ranges.val(0), ranges.val(1)) {} + + void emit_pre(CG_Builder& frag) { + ranges.emit_pre(frag); + values.emit_pre(frag); + } + void emit_post(CG_Builder& frag) { + values.emit_post(frag); + ranges.emit_post(frag); + } + + int val(void) const { return values.val(); } + int cont(void) { return values.cont(); } + + Foreach ranges; + Forrange values; +}; + +}; // namespace MiniZinc + +#endif diff --git a/lib/copy.cpp b/lib/copy.cpp new file mode 100644 index 0000000..47f3a7c --- /dev/null +++ b/lib/copy.cpp @@ -0,0 +1,513 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +void CopyMap::insert(Expression* e0, Expression* e1) { + if (!e0->isUnboxedVal() && !e1->isUnboxedVal()) node_m.insert(e0, e1); +} +Expression* CopyMap::find(Expression* e) { return static_cast(node_m.find(e)); } +void CopyMap::insert(Item* e0, Item* e1) { node_m.insert(e0, e1); } +Item* CopyMap::find(Item* e) { return static_cast(node_m.find(e)); } +void CopyMap::insert(Model* e0, Model* e1) { model_m.insert(std::make_pair(e0, e1)); } +Model* CopyMap::find(Model* e) { + ModelMap::iterator it = model_m.find(e); + if (it == model_m.end()) return NULL; + return it->second; +} +void CopyMap::insert(const ASTString& e0, const ASTString& e1) { + node_m.insert(e0.aststr(), e1.aststr()); +} +ASTStringO* CopyMap::find(const ASTString& e) { + return static_cast(node_m.find(e.aststr())); +} +void CopyMap::insert(IntSetVal* e0, IntSetVal* e1) { node_m.insert(e0, e1); } +IntSetVal* CopyMap::find(IntSetVal* e) { return static_cast(node_m.find(e)); } +void CopyMap::insert(FloatSetVal* e0, FloatSetVal* e1) { node_m.insert(e0, e1); } +FloatSetVal* CopyMap::find(FloatSetVal* e) { return static_cast(node_m.find(e)); } + +Location copy_location(CopyMap& m, const Location& _loc) { return _loc; } +Location copy_location(CopyMap& m, Expression* e) { return copy_location(m, e->loc()); } +Location copy_location(CopyMap& m, Item* i) { return copy_location(m, i->loc()); } + +void copy_ann(EnvI& env, CopyMap& m, Annotation& oldAnn, Annotation& newAnn, bool followIds, + bool copyFundecls, bool isFlatModel); + +Expression* copy(EnvI& env, CopyMap& m, Expression* e, bool followIds, bool copyFundecls, + bool isFlatModel) { + if (e == NULL) return NULL; + if (Expression* cached = m.find(e)) return cached; + Expression* ret = NULL; + switch (e->eid()) { + case Expression::E_INTLIT: { + IntLit* c = IntLit::a(e->cast()->v()); + m.insert(e, c); + ret = c; + } break; + case Expression::E_FLOATLIT: { + FloatLit* c = FloatLit::a(e->cast()->v()); + m.insert(e, c); + ret = c; + } break; + case Expression::E_SETLIT: { + SetLit* s = e->cast(); + SetLit* c = new SetLit(copy_location(m, e), static_cast(NULL)); + m.insert(e, c); + if (s->isv()) { + IntSetVal* isv; + if (IntSetVal* isvc = m.find(s->isv())) { + isv = isvc; + } else { + IntSetRanges r(s->isv()); + isv = IntSetVal::ai(r); + m.insert(s->isv(), isv); + } + c->isv(isv); + } else if (s->fsv()) { + FloatSetVal* fsv; + if (FloatSetVal* fsvc = m.find(s->fsv())) { + fsv = fsvc; + } else { + FloatSetRanges r(s->fsv()); + fsv = FloatSetVal::ai(r); + m.insert(s->fsv(), fsv); + } + c->fsv(fsv); + } else { + if (ASTExprVecO* ve = m.find(s->v())) { + c->v(ASTExprVec(ve)); + } else { + std::vector elems(s->v().size()); + for (unsigned int i = s->v().size(); i--;) + elems[i] = copy(env, m, s->v()[i], followIds, copyFundecls, isFlatModel); + ASTExprVec ce(elems); + m.insert(s->v(), ce); + c->v(ce); + } + } + ret = c; + } break; + case Expression::E_BOOLLIT: { + ret = e; + } break; + case Expression::E_STRINGLIT: { + StringLit* sl = e->cast(); + StringLit* c; + if (ASTStringO* cs = m.find(sl->v())) { + c = new StringLit(copy_location(m, e), ASTString(cs)); + } else { + ASTString s(sl->v().str()); + m.insert(sl->v(), s); + c = new StringLit(copy_location(m, e), s); + } + m.insert(e, c); + ret = c; + } break; + case Expression::E_ID: { + if (e == constants().absent) return e; + Id* id = e->cast(); + + if (followIds) { + Id* prevId = id; + Expression* cur = e; + bool done = false; + do { + if (cur == NULL) { + cur = prevId; + done = true; + } else { + switch (cur->eid()) { + case Expression::E_ID: + prevId = cur->cast(); + cur = prevId->decl(); + break; + case Expression::E_VARDECL: + if (cur->cast()->e()) { + cur = cur->cast()->e(); + } else { + cur = prevId; + done = true; + } + break; + default: + done = true; + } + } + } while (!done); + if (cur->isa()) { + return cur; + } else { + return copy(env, m, cur, false); + } + } else { + Id* c; + if (id->decl()) { + VarDecl* vd = + static_cast(copy(env, m, id->decl(), followIds, copyFundecls, isFlatModel)); + c = vd->id(); + } else { + if (id->idn() != -1) { + c = new Id(copy_location(m, e), id->idn(), NULL); + } else { + ASTString id_v; + if (ASTStringO* cs = m.find(id->v())) { + id_v = ASTString(cs); + } else { + id_v = ASTString(id->v().str()); + m.insert(id->v(), id_v); + } + c = new Id(copy_location(m, e), id_v, NULL); + } + } + m.insert(e, c); + ret = c; + } + } break; + case Expression::E_ANON: { + AnonVar* c = new AnonVar(copy_location(m, e)); + m.insert(e, c); + ret = c; + } break; + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + std::vector> dims(al->dims()); + for (unsigned int i = 0; i < dims.size(); i++) { + dims[i].first = al->min(i); + dims[i].second = al->max(i); + } + if (ArrayLit* sliceView = al->getSliceLiteral()) { + ASTIntVec dimsInternal = al->dimsInternal(); + int sliceDims = sliceView->dims(); + int dimsOffset = al->dims() * 2; + std::vector> slice(sliceDims); + for (int i = 0; i < sliceDims; i++) { + slice[i].first = dimsInternal[dimsOffset + i * 2]; + slice[i].second = dimsInternal[dimsOffset + i * 2 + 1]; + } + ArrayLit* c = new ArrayLit( + copy_location(m, e), + copy(env, m, sliceView, followIds, copyFundecls, isFlatModel)->cast(), dims, + slice); + m.insert(e, c); + ret = c; + } else { + ArrayLit* c = new ArrayLit(copy_location(m, e), std::vector(), dims); + m.insert(e, c); + + ASTExprVecO* v; + if (ASTExprVecO* cv = m.find(al->getVec())) { + v = cv; + } else { + std::vector elems(al->size()); + for (unsigned int i = al->size(); i--;) + elems[i] = copy(env, m, (*al)[i], followIds, copyFundecls, isFlatModel); + ASTExprVec ce(elems); + m.insert(al->getVec(), ce); + v = ce.vec(); + } + c->setVec(ASTExprVec(v)); + ret = c; + } + } break; + case Expression::E_ARRAYACCESS: { + ArrayAccess* aa = e->cast(); + ArrayAccess* c = new ArrayAccess(copy_location(m, e), NULL, std::vector()); + m.insert(e, c); + + ASTExprVecO* idx; + if (ASTExprVecO* cidx = m.find(aa->idx())) { + idx = cidx; + } else { + std::vector elems(aa->idx().size()); + for (unsigned int i = aa->idx().size(); i--;) + elems[i] = copy(env, m, aa->idx()[i], followIds, copyFundecls, isFlatModel); + ASTExprVec ce(elems); + m.insert(aa->idx(), ce); + idx = ce.vec(); + } + c->v(copy(env, m, aa->v(), followIds, copyFundecls, isFlatModel)); + c->idx(ASTExprVec(idx)); + ret = c; + } break; + case Expression::E_COMP: { + Comprehension* c = e->cast(); + Generators g; + Comprehension* cc = new Comprehension(copy_location(m, e), NULL, g, c->set()); + m.insert(c, cc); + + for (int i = 0; i < c->n_generators(); i++) { + std::vector vv; + for (int j = 0; j < c->n_decls(i); j++) + vv.push_back(static_cast( + copy(env, m, c->decl(i, j), followIds, copyFundecls, isFlatModel))); + g._g.push_back(Generator(vv, copy(env, m, c->in(i), followIds, copyFundecls, isFlatModel), + copy(env, m, c->where(i), followIds, copyFundecls, isFlatModel))); + } + cc->init(copy(env, m, c->e(), followIds, copyFundecls, isFlatModel), g); + ret = cc; + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + ITE* c = new ITE(copy_location(m, e), std::vector(), NULL); + m.insert(e, c); + std::vector ifthen(2 * ite->size()); + for (unsigned int i = ite->size(); i--;) { + ifthen[2 * i] = copy(env, m, ite->e_if(i), followIds, copyFundecls, isFlatModel); + ifthen[2 * i + 1] = copy(env, m, ite->e_then(i), followIds, copyFundecls, isFlatModel); + } + c->init(ifthen, copy(env, m, ite->e_else(), followIds, copyFundecls, isFlatModel)); + ret = c; + } break; + case Expression::E_BINOP: { + BinOp* b = e->cast(); + BinOp* c = new BinOp(copy_location(m, e), NULL, b->op(), NULL); + m.insert(e, c); + c->lhs(copy(env, m, b->lhs(), followIds, copyFundecls, isFlatModel)); + c->rhs(copy(env, m, b->rhs(), followIds, copyFundecls, isFlatModel)); + ret = c; + } break; + case Expression::E_UNOP: { + UnOp* b = e->cast(); + UnOp* c = new UnOp(copy_location(m, e), b->op(), NULL); + m.insert(e, c); + c->e(copy(env, m, b->e(), followIds, copyFundecls, isFlatModel)); + ret = c; + } break; + case Expression::E_CALL: { + Call* ca = e->cast(); + ASTString id_v; + if (ASTStringO* cs = m.find(ca->id())) { + id_v = ASTString(cs); + } else { + id_v = ASTString(ca->id().str()); + m.insert(ca->id(), id_v); + } + Call* c = new Call(copy_location(m, e), id_v, std::vector()); + + if (ca->decl()) { + if (copyFundecls) { + c->decl(Item::cast(copy(env, m, ca->decl()))); + } else { + c->decl(ca->decl()); + } + } + + m.insert(e, c); + std::vector args(ca->n_args()); + for (unsigned int i = static_cast(args.size()); i--;) + args[i] = copy(env, m, ca->arg(i), followIds, copyFundecls, isFlatModel); + c->args(args); + ret = c; + } break; + case Expression::E_VARDECL: { + VarDecl* vd = e->cast(); + VarDecl* c; + if (vd->id()->idn() == -1) { + ASTString id_v; + if (ASTStringO* cs = m.find(vd->id()->v())) { + id_v = ASTString(cs); + } else { + id_v = ASTString(vd->id()->v().str()); + m.insert(vd->id()->v(), id_v); + } + c = new VarDecl(copy_location(m, e), NULL, id_v, NULL); + } else { + c = new VarDecl(copy_location(m, e), NULL, vd->id()->idn(), NULL); + } + c->toplevel(vd->toplevel()); + c->introduced(vd->introduced()); + if (isFlatModel && vd->flat() == vd) + c->flat(c); + else + c->flat(vd->flat()); + c->payload(vd->payload()); + m.insert(e, c); + m.insert(c, c); + c->ti(static_cast(copy(env, m, vd->ti(), followIds, copyFundecls, isFlatModel))); + c->e(copy(env, m, vd->e(), followIds, copyFundecls, isFlatModel)); + c->type(c->ti()->type()); + c->id()->type(c->type()); + ret = c; + } break; + case Expression::E_LET: { + Let* l = e->cast(); + std::vector let(l->let().size()); + for (unsigned int i = l->let().size(); i--;) + let[i] = copy(env, m, l->let()[i], followIds, copyFundecls, isFlatModel); + Let* c = new Let(copy_location(m, e), let, + copy(env, m, l->in(), followIds, copyFundecls, isFlatModel)); + for (unsigned int i = l->let().size(); i--;) { + c->_let_orig[i] = copy(env, m, l->_let_orig[i], followIds, copyFundecls, isFlatModel); + } + + m.insert(e, c); + ret = c; + } break; + case Expression::E_TI: { + TypeInst* t = e->cast(); + ASTExprVecO* r; + if (t->ranges().size() == 0) { + r = NULL; + } else if (ASTExprVecO* cr = m.find(t->ranges())) { + r = cr; + } else { + std::vector rr(t->ranges().size()); + for (unsigned int i = t->ranges().size(); i--;) + rr[i] = static_cast( + copy(env, m, t->ranges()[i], followIds, copyFundecls, isFlatModel)); + r = ASTExprVecO::a(rr); + } + TypeInst* c = new TypeInst(copy_location(m, e), t->type(), ASTExprVec(r), + copy(env, m, t->domain(), followIds, copyFundecls, isFlatModel)); + c->setIsEnum(t->isEnum()); + m.insert(e, c); + ret = c; + } break; + case Expression::E_TIID: { + TIId* t = e->cast(); + TIId* c = new TIId(copy_location(m, e), t->v().str()); + m.insert(e, c); + ret = c; + } break; + default: + assert(false); + } + if (!ret->isa() || ret->cast()->decl() == NULL) ret->type(e->type()); + copy_ann(env, m, e->ann(), ret->ann(), followIds, copyFundecls, isFlatModel); + return ret; +} + +void copy_ann(EnvI& env, CopyMap& m, Annotation& oldAnn, Annotation& newAnn, bool followIds, + bool copyFundecls, bool isFlatModel) { + for (ExpressionSetIter it = oldAnn.begin(); it != oldAnn.end(); ++it) + newAnn.add(copy(env, m, *it, followIds, copyFundecls, isFlatModel)); +} + +Expression* copy(EnvI& env, Expression* e, bool followIds, bool copyFundecls, bool isFlatModel) { + CopyMap m; + return copy(env, m, e, followIds, copyFundecls, isFlatModel); +} + +Model* copy(EnvI& env, CopyMap& cm, Model* m, bool isFlatModel); + +Item* copy(EnvI& env, CopyMap& m, Item* i, bool followIds, bool copyFundecls, bool isFlatModel) { + if (i == NULL) return NULL; + if (Item* cached = m.find(i)) return cached; + switch (i->iid()) { + case Item::II_INC: { + IncludeI* ii = i->cast(); + IncludeI* c = new IncludeI(copy_location(m, i), ASTString(ii->f().str())); + m.insert(i, c); + c->m(copy(env, m, ii->m()), ii->own()); + return c; + } + case Item::II_VD: { + VarDeclI* v = i->cast(); + VarDeclI* c = new VarDeclI(copy_location(m, i), NULL); + m.insert(i, c); + c->e(static_cast(copy(env, m, v->e(), followIds, copyFundecls, isFlatModel))); + return c; + } + case Item::II_ASN: { + AssignI* a = i->cast(); + AssignI* c = new AssignI(copy_location(m, i), a->id().str(), NULL); + m.insert(i, c); + c->e(copy(env, m, a->e(), followIds, copyFundecls, isFlatModel)); + c->decl(static_cast(copy(env, m, a->decl(), followIds, copyFundecls, isFlatModel))); + return c; + } + case Item::II_CON: { + ConstraintI* cc = i->cast(); + ConstraintI* c = new ConstraintI(copy_location(m, i), NULL); + m.insert(i, c); + c->e(copy(env, m, cc->e(), followIds, copyFundecls, isFlatModel)); + return c; + } + case Item::II_SOL: { + SolveI* s = i->cast(); + SolveI* c; + switch (s->st()) { + case SolveI::ST_SAT: + c = SolveI::sat(Location()); + break; + case SolveI::ST_MIN: + c = SolveI::min(Location(), copy(env, m, s->e(), followIds, copyFundecls, isFlatModel)); + break; + case SolveI::ST_MAX: + c = SolveI::max(Location(), copy(env, m, s->e(), followIds, copyFundecls, isFlatModel)); + break; + } + copy_ann(env, m, s->ann(), c->ann(), followIds, copyFundecls, isFlatModel); + m.insert(i, c); + return c; + } + case Item::II_OUT: { + OutputI* o = i->cast(); + OutputI* c = new OutputI(copy_location(m, i), + copy(env, m, o->e(), followIds, copyFundecls, isFlatModel)); + m.insert(i, c); + return c; + } + case Item::II_FUN: { + FunctionI* f = i->cast(); + std::vector params(f->params().size()); + for (unsigned int j = f->params().size(); j--;) + params[j] = static_cast( + copy(env, m, f->params()[j], followIds, copyFundecls, isFlatModel)); + FunctionI* c = new FunctionI( + copy_location(m, i), f->id().str(), + static_cast(copy(env, m, f->ti(), followIds, copyFundecls, isFlatModel)), + params, copy(env, m, f->e(), followIds, copyFundecls, isFlatModel)); + c->_builtins.e = f->_builtins.e; + c->_builtins.i = f->_builtins.i; + c->_builtins.f = f->_builtins.f; + c->_builtins.b = f->_builtins.b; + c->_builtins.s = f->_builtins.s; + c->_builtins.str = f->_builtins.str; + + copy_ann(env, m, f->ann(), c->ann(), followIds, copyFundecls, isFlatModel); + m.insert(i, c); + return c; + } + default: + assert(false); + return NULL; + } +} + +Item* copy(EnvI& env, Item* i, bool followIds, bool copyFundecls, bool isFlatModel) { + CopyMap m; + return copy(env, m, i, followIds, copyFundecls, isFlatModel); +} + +Model* copy(EnvI& env, CopyMap& cm, Model* m, bool isFlatModel) { + if (m == NULL) return NULL; + if (Model* cached = cm.find(m)) return cached; + Model* c = new Model; + for (unsigned int i = 0; i < m->size(); i++) c->addItem(copy(env, cm, (*m)[i], false, true)); + + for (Model::FnMap::iterator it = m->fnmap.begin(); it != m->fnmap.end(); ++it) { + for (unsigned int i = 0; i < it->second.size(); i++) + c->registerFn(env, + copy(env, cm, it->second[i].fi, false, true, isFlatModel)->cast()); + } + cm.insert(m, c); + return c; +} +Model* copy(EnvI& env, Model* m) { + CopyMap cm; + return copy(env, cm, m); +} + +} // namespace MiniZinc diff --git a/lib/eval_par.cpp b/lib/eval_par.cpp new file mode 100644 index 0000000..e097852 --- /dev/null +++ b/lib/eval_par.cpp @@ -0,0 +1,2934 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace MiniZinc { + +bool checkParDomain(EnvI& env, Expression* e, Expression* domain) { + if (e->type() == Type::parint()) { + IntSetVal* isv = eval_intset(env, domain); + if (!isv->contains(eval_int(env, e))) return false; + } else if (e->type() == Type::parfloat()) { + FloatSetVal* fsv = eval_floatset(env, domain); + if (!fsv->contains(eval_float(env, e))) return false; + } else if (e->type() == Type::parsetint()) { + IntSetVal* isv = eval_intset(env, domain); + IntSetRanges ir(isv); + IntSetVal* rsv = eval_intset(env, e); + IntSetRanges rr(rsv); + if (!Ranges::subset(rr, ir)) return false; + } else if (e->type() == Type::parsetfloat()) { + FloatSetVal* fsv = eval_floatset(env, domain); + FloatSetRanges fr(fsv); + FloatSetVal* rsv = eval_floatset(env, e); + FloatSetRanges rr(rsv); + if (!Ranges::subset(rr, fr)) return false; + } + return true; +} + +template +typename E::Val eval_id(EnvI& env, Expression* e) { + Id* id = e->cast(); + if (id->decl() == NULL) throw EvalError(env, e->loc(), "undeclared identifier", id->str().str()); + VarDecl* vd = id->decl(); + while (vd->flat() && vd->flat() != vd) vd = vd->flat(); + if (vd->e() == NULL) + throw EvalError(env, vd->loc(), "cannot evaluate expression", id->str().str()); + typename E::Val r = E::e(env, vd->e()); + if (!vd->evaluated() && (vd->toplevel() || vd->type().dim() > 0)) { + Expression* ne = E::exp(r); + vd->e(ne); + vd->evaluated(true); + } + return r; +} + +class EvalIntLit { +public: + typedef IntLit* Val; + typedef Expression* ArrayVal; + static IntLit* e(EnvI& env, Expression* e) { return IntLit::a(eval_int(env, e)); } + static Expression* exp(IntLit* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalIntVal { +public: + typedef IntVal Val; + typedef IntVal ArrayVal; + static IntVal e(EnvI& env, Expression* e) { return eval_int(env, e); } + static Expression* exp(IntVal e) { return IntLit::a(e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { + if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { + IntSetVal* isv = eval_intset(env, fi->ti()->domain()); + if (!isv->contains(v)) { + throw ResultUndefinedError(env, Location().introduce(), + "function result violates function type-inst"); + } + } + } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalFloatVal { +public: + typedef FloatVal Val; + typedef FloatVal ArrayVal; + static FloatVal e(EnvI& env, Expression* e) { return eval_float(env, e); } + static Expression* exp(FloatVal e) { return FloatLit::a(e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { + if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { + FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); + if (!fsv->contains(v)) { + throw ResultUndefinedError(env, Location().introduce(), + "function result violates function type-inst"); + } + } + } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalFloatLit { +public: + typedef FloatLit* Val; + typedef Expression* ArrayVal; + static FloatLit* e(EnvI& env, Expression* e) { return FloatLit::a(eval_float(env, e)); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalString { +public: + typedef std::string Val; + typedef std::string ArrayVal; + static std::string e(EnvI& env, Expression* e) { return eval_string(env, e); } + static Expression* exp(const std::string& e) { return new StringLit(Location(), e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalStringLit { +public: + typedef StringLit* Val; + typedef Expression* ArrayVal; + static StringLit* e(EnvI& env, Expression* e) { + return new StringLit(Location(), eval_string(env, e)); + } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalBoolLit { +public: + typedef BoolLit* Val; + typedef Expression* ArrayVal; + static BoolLit* e(EnvI& env, Expression* e) { return constants().boollit(eval_bool(env, e)); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalBoolVal { +public: + typedef bool Val; + static bool e(EnvI& env, Expression* e) { return eval_bool(env, e); } + static Expression* exp(bool e) { return constants().boollit(e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalArrayLit { +public: + typedef ArrayLit* Val; + typedef Expression* ArrayVal; + static ArrayLit* e(EnvI& env, Expression* e) { return eval_array_lit(env, e); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalArrayLitCopy { +public: + typedef ArrayLit* Val; + typedef Expression* ArrayVal; + static ArrayLit* e(EnvI& env, Expression* e) { + return copy(env, eval_array_lit(env, e), true)->cast(); + } + static Expression* exp(Expression* e) { return e; } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { + for (unsigned int i = 0; i < fi->ti()->ranges().size(); i++) { + if (fi->ti()->ranges()[i]->domain() && !fi->ti()->ranges()[i]->domain()->isa()) { + IntSetVal* isv = eval_intset(env, fi->ti()->ranges()[i]->domain()); + bool bothEmpty = isv->min() > isv->max() && v->min(i) > v->max(i); + if (!bothEmpty && (v->min(i) != isv->min() || v->max(i) != isv->max())) { + std::ostringstream oss; + oss << "array index set " << (i + 1) << " of function result violates function type-inst"; + throw ResultUndefinedError(env, fi->e()->loc(), oss.str()); + } + } + } + if (fi->ti()->domain() && !fi->ti()->domain()->isa() && + fi->ti()->type().ti() == Type::TI_PAR) { + Type base_t = fi->ti()->type(); + if (base_t.bt() == Type::BT_INT) { + IntSetVal* isv = eval_intset(env, fi->ti()->domain()); + if (base_t.st() == Type::ST_PLAIN) { + for (unsigned int i = 0; i < v->size(); i++) { + IntVal iv = eval_int(env, (*v)[i]); + if (!isv->contains(iv)) { + std::ostringstream oss; + oss << "array contains value " << iv << " which is not contained in " << *isv; + throw ResultUndefinedError( + env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); + } + } + } else { + for (unsigned int i = 0; i < v->size(); i++) { + IntSetVal* iv = eval_intset(env, (*v)[i]); + IntSetRanges isv_r(isv); + IntSetRanges v_r(iv); + if (!Ranges::subset(v_r, isv_r)) { + std::ostringstream oss; + oss << "array contains value " << *iv << " which is not a subset of " << *isv; + throw ResultUndefinedError( + env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); + } + } + } + } else if (base_t.bt() == Type::BT_FLOAT) { + FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); + if (base_t.st() == Type::ST_PLAIN) { + for (unsigned int i = 0; i < v->size(); i++) { + FloatVal fv = eval_float(env, (*v)[i]); + if (!fsv->contains(fv)) { + std::ostringstream oss; + oss << "array contains value " << fv << " which is not contained in " << *fsv; + throw ResultUndefinedError( + env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); + } + } + } else { + for (unsigned int i = 0; i < v->size(); i++) { + FloatSetVal* fv = eval_floatset(env, (*v)[i]); + FloatSetRanges fsv_r(fsv); + FloatSetRanges v_r(fv); + if (!Ranges::subset(v_r, fsv_r)) { + std::ostringstream oss; + oss << "array contains value " << *fv << " which is not a subset of " << *fsv; + throw ResultUndefinedError( + env, fi->e()->loc(), "function result violates function type-inst, " + oss.str()); + } + } + } + } + } + } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalIntSet { +public: + typedef IntSetVal* Val; + static IntSetVal* e(EnvI& env, Expression* e) { return eval_intset(env, e); } + static Expression* exp(IntSetVal* e) { return new SetLit(Location(), e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { + if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { + IntSetVal* isv = eval_intset(env, fi->ti()->domain()); + IntSetRanges isv_r(isv); + IntSetRanges v_r(v); + if (!Ranges::subset(v_r, isv_r)) { + throw ResultUndefinedError(env, Location().introduce(), + "function result violates function type-inst"); + } + } + } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalFloatSet { +public: + typedef FloatSetVal* Val; + static FloatSetVal* e(EnvI& env, Expression* e) { return eval_floatset(env, e); } + static Expression* exp(FloatSetVal* e) { return new SetLit(Location(), e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) { + if (fi->ti()->domain() && !fi->ti()->domain()->isa()) { + FloatSetVal* fsv = eval_floatset(env, fi->ti()->domain()); + FloatSetRanges fsv_r(fsv); + FloatSetRanges v_r(v); + if (!Ranges::subset(v_r, fsv_r)) { + throw ResultUndefinedError(env, Location().introduce(), + "function result violates function type-inst"); + } + } + } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalBoolSet { +public: + typedef IntSetVal* Val; + static IntSetVal* e(EnvI& env, Expression* e) { return eval_boolset(env, e); } + static Expression* exp(IntSetVal* e) { return new SetLit(Location(), e); } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalSetLit { +public: + typedef SetLit* Val; + typedef Expression* ArrayVal; + static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_intset(env, e)); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalFloatSetLit { +public: + typedef SetLit* Val; + typedef Expression* ArrayVal; + static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_floatset(env, e)); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalBoolSetLit { +public: + typedef SetLit* Val; + typedef Expression* ArrayVal; + static SetLit* e(EnvI& env, Expression* e) { return new SetLit(e->loc(), eval_boolset(env, e)); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalCopy { +public: + typedef Expression* Val; + typedef Expression* ArrayVal; + static Expression* e(EnvI& env, Expression* e) { return copy(env, e, true); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; +class EvalPar { +public: + typedef Expression* Val; + typedef Expression* ArrayVal; + static Expression* e(EnvI& env, Expression* e) { return eval_par(env, e); } + static Expression* exp(Expression* e) { return e; } + static void checkRetVal(EnvI& env, Val v, FunctionI* fi) {} + Expression* flatten(EnvI&, Expression*) { + throw InternalError("evaluating var assignment generator inside par expression not supported"); + } +}; + +void checkDom(EnvI& env, Id* arg, IntSetVal* dom, Expression* e) { + bool oob = false; + if (!e->type().isopt()) { + if (e->type().isintset()) { + IntSetVal* ev = eval_intset(env, e); + IntSetRanges ev_r(ev); + IntSetRanges dom_r(dom); + oob = !Ranges::subset(ev_r, dom_r); + } else { + oob = !dom->contains(eval_int(env, e)); + } + } + if (oob) { + std::ostringstream oss; + oss << "value for argument `" << *arg << "' out of bounds"; + throw EvalError(env, e->loc(), oss.str()); + } +} + +void checkDom(EnvI& env, Id* arg, FloatVal dom_min, FloatVal dom_max, Expression* e) { + if (!e->type().isopt()) { + FloatVal ev = eval_float(env, e); + if (ev < dom_min || ev > dom_max) { + std::ostringstream oss; + oss << "value for argument `" << *arg << "' out of bounds"; + throw EvalError(env, e->loc(), oss.str()); + } + } +} + +template +typename Eval::Val eval_call(EnvI& env, CallClass* ce) { + std::vector previousParameters(ce->decl()->params().size()); + std::vector params(ce->decl()->params().size()); + for (unsigned int i = 0; i < ce->decl()->params().size(); i++) { + params[i] = eval_par(env, ce->arg(i)); + } + for (unsigned int i = ce->decl()->params().size(); i--;) { + VarDecl* vd = ce->decl()->params()[i]; + if (vd->type().dim() > 0) { + // Check array index sets + ArrayLit* al = params[i]->cast(); + for (unsigned int j = 0; j < vd->ti()->ranges().size(); j++) { + TypeInst* range_ti = vd->ti()->ranges()[j]; + if (range_ti->domain() && !range_ti->domain()->isa()) { + IntSetVal* isv = eval_intset(env, range_ti->domain()); + if (isv->min() != al->min(j) || isv->max() != al->max(j)) { + std::ostringstream oss; + oss << "array index set " << (j + 1) << " of argument " << (i + 1) + << " does not match declared index set"; + throw EvalError(env, ce->loc(), oss.str()); + } + } + } + } + previousParameters[i] = vd->e(); + vd->flat(vd); + vd->e(params[i]); + if (vd->e()->type().ispar()) { + if (Expression* dom = vd->ti()->domain()) { + if (!dom->isa()) { + if (vd->e()->type().bt() == Type::BT_INT) { + IntSetVal* isv = eval_intset(env, dom); + if (vd->e()->type().dim() > 0) { + ArrayLit* al = eval_array_lit(env, vd->e()); + for (unsigned int i = 0; i < al->size(); i++) { + checkDom(env, vd->id(), isv, (*al)[i]); + } + } else { + checkDom(env, vd->id(), isv, vd->e()); + } + } else if (vd->e()->type().bt() == Type::BT_FLOAT) { + GCLock lock; + FloatSetVal* fsv = eval_floatset(env, dom); + FloatVal dom_min = fsv->min(); + FloatVal dom_max = fsv->max(); + checkDom(env, vd->id(), dom_min, dom_max, vd->e()); + } + } + } + } + } + typename Eval::Val ret = Eval::e(env, ce->decl()->e()); + Eval::checkRetVal(env, ret, ce->decl()); + for (unsigned int i = ce->decl()->params().size(); i--;) { + VarDecl* vd = ce->decl()->params()[i]; + vd->e(previousParameters[i]); + vd->flat(vd->e() ? vd : NULL); + } + return ret; +} + +ArrayLit* eval_array_comp(EnvI& env, Comprehension* e) { + ArrayLit* ret; + if (e->type() == Type::parint(1)) { + std::vector a = eval_comp(env, e); + ret = new ArrayLit(e->loc(), a); + } else if (e->type() == Type::parbool(1)) { + std::vector a = eval_comp(env, e); + ret = new ArrayLit(e->loc(), a); + } else if (e->type() == Type::parfloat(1)) { + std::vector a = eval_comp(env, e); + ret = new ArrayLit(e->loc(), a); + } else if (e->type() == Type::parsetint(1)) { + std::vector a = eval_comp(env, e); + ret = new ArrayLit(e->loc(), a); + } else if (e->type() == Type::parstring(1)) { + std::vector a = eval_comp(env, e); + ret = new ArrayLit(e->loc(), a); + } else { + std::vector a = eval_comp(env, e); + ret = new ArrayLit(e->loc(), a); + } + ret->type(e->type()); + return ret; +} + +ArrayLit* eval_array_lit(EnvI& env, Expression* e) { + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_SETLIT: + case Expression::E_ANON: + case Expression::E_TI: + case Expression::E_TIID: + case Expression::E_VARDECL: + throw EvalError(env, e->loc(), "not an array expression"); + case Expression::E_ID: + return eval_id(env, e); + case Expression::E_ARRAYLIT: + return e->cast(); + case Expression::E_ARRAYACCESS: + throw EvalError(env, e->loc(), "arrays of arrays not supported"); + case Expression::E_COMP: + return eval_array_comp(env, e->cast()); + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_array_lit(env, ite->e_then(i)); + } + return eval_array_lit(env, ite->e_else()); + } + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->op() == BOT_PLUSPLUS) { + ArrayLit* al0 = eval_array_lit(env, bo->lhs()); + ArrayLit* al1 = eval_array_lit(env, bo->rhs()); + std::vector v(al0->size() + al1->size()); + for (unsigned int i = al0->size(); i--;) v[i] = (*al0)[i]; + for (unsigned int i = al1->size(); i--;) v[al0->size() + i] = (*al1)[i]; + ArrayLit* ret = new ArrayLit(e->loc(), v); + ret->flat(al0->flat() && al1->flat()); + ret->type(e->type()); + return ret; + } else { + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + throw EvalError(env, e->loc(), "not an array expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + throw EvalError(env, e->loc(), "not an array expression"); + } + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + + if (ce->decl()->_builtins.e) return eval_array_lit(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + ArrayLit* l_in = eval_array_lit(env, l->in()); + ArrayLit* ret = copy(env, l_in, true)->cast(); + ret->flat(l_in->flat()); + l->popbindings(); + return ret; + } + } + assert(false); + return NULL; +} + +Expression* eval_arrayaccess(EnvI& env, ArrayLit* al, const std::vector& dims, + bool& success) { + success = true; + assert(al->dims() == dims.size()); + IntVal realidx = 0; + int realdim = 1; + for (int i = 0; i < al->dims(); i++) realdim *= al->max(i) - al->min(i) + 1; + for (int i = 0; i < al->dims(); i++) { + IntVal ix = dims[i]; + if (ix < al->min(i) || ix > al->max(i)) { + success = false; + Type t = al->type(); + t.dim(0); + if (t.isint()) return IntLit::a(0); + if (t.isbool()) return constants().lit_false; + if (t.isfloat()) return FloatLit::a(0.0); + if (t.st() == Type::ST_SET || t.isbot()) { + SetLit* ret = new SetLit(Location(), std::vector()); + ret->type(t); + return ret; + } + if (t.isstring()) return new StringLit(Location(), ""); + throw EvalError(env, al->loc(), "Internal error: unexpected type in array access expression"); + } + realdim /= al->max(i) - al->min(i) + 1; + realidx += (ix - al->min(i)) * realdim; + } + assert(realidx >= 0 && realidx <= al->size()); + return (*al)[static_cast(realidx.toInt())]; +} +Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e, bool& success) { + ArrayLit* al = eval_array_lit(env, e->v()); + std::vector dims(e->idx().size()); + for (unsigned int i = e->idx().size(); i--;) { + dims[i] = eval_int(env, e->idx()[i]); + } + return eval_arrayaccess(env, al, dims, success); +} +Expression* eval_arrayaccess(EnvI& env, ArrayAccess* e) { + bool success; + Expression* ret = eval_arrayaccess(env, e, success); + if (success) + return ret; + else + throw ResultUndefinedError(env, e->loc(), "array access out of bounds"); +} + +IntSetVal* eval_intset(EnvI& env, Expression* e) { + if (SetLit* sl = e->dyn_cast()) { + if (sl->isv()) return sl->isv(); + } + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_SETLIT: { + SetLit* sl = e->cast(); + std::vector vals(sl->v().size()); + for (unsigned int i = 0; i < sl->v().size(); i++) vals[i] = eval_int(env, sl->v()[i]); + return IntSetVal::a(vals); + } + case Expression::E_BOOLLIT: + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_VARDECL: + case Expression::E_TI: + throw EvalError(env, e->loc(), "not a set of int expression"); + break; + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + std::vector vals(al->size()); + for (unsigned int i = 0; i < al->size(); i++) vals[i] = eval_int(env, (*al)[i]); + return IntSetVal::a(vals); + } break; + case Expression::E_COMP: { + Comprehension* c = e->cast(); + std::vector a = eval_comp(env, c); + return IntSetVal::a(a); + } + case Expression::E_ID: { + GCLock lock; + return eval_id(env, e)->isv(); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_intset(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_intset(env, ite->e_then(i)); + } + return eval_intset(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + Expression* lhs = eval_par(env, bo->lhs()); + Expression* rhs = eval_par(env, bo->rhs()); + if (lhs->type().isintset() && rhs->type().isintset()) { + IntSetVal* v0 = eval_intset(env, lhs); + IntSetVal* v1 = eval_intset(env, rhs); + IntSetRanges ir0(v0); + IntSetRanges ir1(v1); + switch (bo->op()) { + case BOT_UNION: { + Ranges::Union u(ir0, ir1); + return IntSetVal::ai(u); + } + case BOT_DIFF: { + Ranges::Diff u(ir0, ir1); + return IntSetVal::ai(u); + } + case BOT_SYMDIFF: { + Ranges::Union u(ir0, ir1); + Ranges::Inter i(ir0, ir1); + Ranges::Diff, + Ranges::Inter> + sd(u, i); + return IntSetVal::ai(sd); + } + case BOT_INTERSECT: { + Ranges::Inter u(ir0, ir1); + return IntSetVal::ai(u); + } + default: + throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); + } + } else if (lhs->type().isint() && rhs->type().isint()) { + if (bo->op() != BOT_DOTDOT) + throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); + return IntSetVal::a(eval_int(env, lhs), eval_int(env, rhs)); + } else { + throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + throw EvalError(env, e->loc(), "not a set of int expression"); + } + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + + if (ce->decl()->_builtins.s) return ce->decl()->_builtins.s(env, ce); + + if (ce->decl()->_builtins.e) return eval_intset(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + IntSetVal* ret = eval_intset(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return NULL; + } +} + +FloatSetVal* eval_floatset(EnvI& env, Expression* e) { + if (SetLit* sl = e->dyn_cast()) { + if (sl->fsv()) return sl->fsv(); + } + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_SETLIT: { + SetLit* sl = e->cast(); + std::vector vals(sl->v().size()); + for (unsigned int i = 0; i < sl->v().size(); i++) vals[i] = eval_float(env, sl->v()[i]); + return FloatSetVal::a(vals); + } + case Expression::E_BOOLLIT: + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_VARDECL: + case Expression::E_TI: + throw EvalError(env, e->loc(), "not a set of float expression"); + break; + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + std::vector vals(al->size()); + for (unsigned int i = 0; i < al->size(); i++) vals[i] = eval_float(env, (*al)[i]); + return FloatSetVal::a(vals); + } break; + case Expression::E_COMP: { + Comprehension* c = e->cast(); + std::vector a = eval_comp(env, c); + return FloatSetVal::a(a); + } + case Expression::E_ID: { + GCLock lock; + return eval_floatset(env, eval_id(env, e)); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_floatset(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_floatset(env, ite->e_then(i)); + } + return eval_floatset(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + Expression* lhs = eval_par(env, bo->lhs()); + Expression* rhs = eval_par(env, bo->rhs()); + if (lhs->type().isfloatset() && rhs->type().isfloatset()) { + FloatSetVal* v0 = eval_floatset(env, lhs); + FloatSetVal* v1 = eval_floatset(env, rhs); + FloatSetRanges fr0(v0); + FloatSetRanges fr1(v1); + switch (bo->op()) { + case BOT_UNION: { + Ranges::Union u(fr0, fr1); + return FloatSetVal::ai(u); + } + case BOT_DIFF: { + Ranges::Diff u(fr0, fr1); + return FloatSetVal::ai(u); + } + case BOT_SYMDIFF: { + Ranges::Union u(fr0, fr1); + Ranges::Inter i(fr0, fr1); + Ranges::Diff, + Ranges::Inter> + sd(u, i); + return FloatSetVal::ai(sd); + } + case BOT_INTERSECT: { + Ranges::Inter u(fr0, fr1); + return FloatSetVal::ai(u); + } + default: + throw EvalError(env, e->loc(), "not a set of int expression", bo->opToString()); + } + } else if (lhs->type().isfloat() && rhs->type().isfloat()) { + if (bo->op() != BOT_DOTDOT) + throw EvalError(env, e->loc(), "not a set of float expression", bo->opToString()); + return FloatSetVal::a(eval_float(env, lhs), eval_float(env, rhs)); + } else { + throw EvalError(env, e->loc(), "not a set of float expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + throw EvalError(env, e->loc(), "not a set of float expression"); + } + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + + if (ce->decl()->_builtins.e) return eval_floatset(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + FloatSetVal* ret = eval_floatset(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return NULL; + } +} + +bool eval_bool(EnvI& env, Expression* e) { + CallStackItem csi(env, e); + try { + if (BoolLit* bl = e->dyn_cast()) { + return bl->v(); + } + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_SETLIT: + case Expression::E_ARRAYLIT: + case Expression::E_COMP: + case Expression::E_VARDECL: + case Expression::E_TI: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression"); + break; + case Expression::E_ID: { + GCLock lock; + return eval_id(env, e)->v(); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_bool(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_bool(env, ite->e_then(i)); + } + return eval_bool(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + Expression* lhs = bo->lhs(); + if (lhs->type().bt() == Type::BT_TOP) lhs = eval_par(env, lhs); + Expression* rhs = bo->rhs(); + if (rhs->type().bt() == Type::BT_TOP) rhs = eval_par(env, rhs); + if (bo->op() == BOT_EQ && (lhs->type().isopt() || rhs->type().isopt())) { + if (lhs == constants().absent || rhs == constants().absent) return lhs == rhs; + } + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + + if (lhs->type().isbool() && rhs->type().isbool()) { + try { + switch (bo->op()) { + case BOT_LE: + return eval_bool(env, lhs) < eval_bool(env, rhs); + case BOT_LQ: + return eval_bool(env, lhs) <= eval_bool(env, rhs); + case BOT_GR: + return eval_bool(env, lhs) > eval_bool(env, rhs); + case BOT_GQ: + return eval_bool(env, lhs) >= eval_bool(env, rhs); + case BOT_EQ: + return eval_bool(env, lhs) == eval_bool(env, rhs); + case BOT_NQ: + return eval_bool(env, lhs) != eval_bool(env, rhs); + case BOT_EQUIV: + return eval_bool(env, lhs) == eval_bool(env, rhs); + case BOT_IMPL: + return (!eval_bool(env, lhs)) || eval_bool(env, rhs); + case BOT_RIMPL: + return (!eval_bool(env, rhs)) || eval_bool(env, lhs); + case BOT_OR: + return eval_bool(env, lhs) || eval_bool(env, rhs); + case BOT_AND: + return eval_bool(env, lhs) && eval_bool(env, rhs); + case BOT_XOR: + return eval_bool(env, lhs) ^ eval_bool(env, rhs); + default: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (lhs->type().isint() && rhs->type().isint()) { + try { + IntVal v0 = eval_int(env, lhs); + IntVal v1 = eval_int(env, rhs); + switch (bo->op()) { + case BOT_LE: + return v0 < v1; + case BOT_LQ: + return v0 <= v1; + case BOT_GR: + return v0 > v1; + case BOT_GQ: + return v0 >= v1; + case BOT_EQ: + return v0 == v1; + case BOT_NQ: + return v0 != v1; + default: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (lhs->type().isfloat() && rhs->type().isfloat()) { + try { + FloatVal v0 = eval_float(env, lhs); + FloatVal v1 = eval_float(env, rhs); + switch (bo->op()) { + case BOT_LE: + return v0 < v1; + case BOT_LQ: + return v0 <= v1; + case BOT_GR: + return v0 > v1; + case BOT_GQ: + return v0 >= v1; + case BOT_EQ: + return v0 == v1; + case BOT_NQ: + return v0 != v1; + default: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (lhs->type().isint() && rhs->type().isintset()) { + try { + IntVal v0 = eval_int(env, lhs); + GCLock lock; + IntSetVal* v1 = eval_intset(env, rhs); + switch (bo->op()) { + case BOT_IN: + return v1->contains(v0); + default: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (lhs->type().isfloat() && rhs->type().isfloatset()) { + try { + FloatVal v0 = eval_float(env, lhs); + GCLock lock; + FloatSetVal* v1 = eval_floatset(env, rhs); + switch (bo->op()) { + case BOT_IN: + return v1->contains(v0); + default: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (lhs->type().is_set() && rhs->type().is_set()) { + try { + GCLock lock; + IntSetVal* v0 = eval_intset(env, lhs); + IntSetVal* v1 = eval_intset(env, rhs); + IntSetRanges ir0(v0); + IntSetRanges ir1(v1); + switch (bo->op()) { + case BOT_LE: + return Ranges::less(ir0, ir1); + case BOT_LQ: + return Ranges::lessEq(ir0, ir1); + case BOT_GR: + return Ranges::less(ir1, ir0); + case BOT_GQ: + return Ranges::lessEq(ir1, ir0); + case BOT_EQ: + return Ranges::equal(ir0, ir1); + case BOT_NQ: + return !Ranges::equal(ir0, ir1); + case BOT_SUBSET: + return Ranges::subset(ir0, ir1); + case BOT_SUPERSET: + return Ranges::subset(ir1, ir0); + default: + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (lhs->type().isstring() && rhs->type().isstring()) { + try { + GCLock lock; + std::string s0 = eval_string(env, lhs); + std::string s1 = eval_string(env, rhs); + switch (bo->op()) { + case BOT_EQ: + return s0 == s1; + case BOT_NQ: + return s0 != s1; + case BOT_LE: + return s0 < s1; + case BOT_LQ: + return s0 <= s1; + case BOT_GR: + return s0 > s1; + case BOT_GQ: + return s0 >= s1; + default: + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } catch (ResultUndefinedError&) { + return false; + } + } else if (bo->op() == BOT_EQ && lhs->type().isann()) { + return Expression::equal(lhs, rhs); + } else if (bo->op() == BOT_EQ && lhs->type().dim() > 0 && rhs->type().dim() > 0) { + try { + ArrayLit* al0 = eval_array_lit(env, lhs); + ArrayLit* al1 = eval_array_lit(env, rhs); + if (al0->size() != al1->size()) return false; + for (unsigned int i = 0; i < al0->size(); i++) { + if (!Expression::equal(eval_par(env, (*al0)[i]), eval_par(env, (*al1)[i]))) { + return false; + } + } + return true; + } catch (ResultUndefinedError&) { + return false; + } + } else { + throw EvalError(env, e->loc(), "not a bool expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + bool v0 = eval_bool(env, uo->e()); + switch (uo->op()) { + case UOT_NOT: + return !v0; + default: + assert(false); + throw EvalError(env, e->loc(), "not a bool expression", uo->opToString()); + } + } break; + case Expression::E_CALL: { + try { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + + if (ce->decl()->_builtins.b) return ce->decl()->_builtins.b(env, ce); + + if (ce->decl()->_builtins.e) return eval_bool(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), + "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } catch (ResultUndefinedError&) { + return false; + } + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + bool ret = true; + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + if (vdi->ann().contains(constants().ann.maybe_partial)) { + ret = false; + } else { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is false. + if (!eval_bool(env, l->let()[i])) { + if (l->let()[i]->ann().contains(constants().ann.maybe_partial)) { + ret = false; + } else { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } + } + ret = ret && eval_bool(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return false; + } + } catch (ResultUndefinedError&) { + // undefined means false + return false; + } +} + +IntSetVal* eval_boolset(EnvI& env, Expression* e) { + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_SETLIT: { + SetLit* sl = e->cast(); + if (sl->isv()) return sl->isv(); + std::vector vals(sl->v().size()); + for (unsigned int i = 0; i < sl->v().size(); i++) vals[i] = eval_bool(env, sl->v()[i]); + return IntSetVal::a(vals); + } + case Expression::E_BOOLLIT: + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_VARDECL: + case Expression::E_TI: + throw EvalError(env, e->loc(), "not a set of bool expression"); + break; + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + std::vector vals(al->size()); + for (unsigned int i = 0; i < al->size(); i++) vals[i] = eval_bool(env, (*al)[i]); + return IntSetVal::a(vals); + } break; + case Expression::E_COMP: { + Comprehension* c = e->cast(); + std::vector a = eval_comp(env, c); + return IntSetVal::a(a); + } + case Expression::E_ID: { + GCLock lock; + return eval_id(env, e)->isv(); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_boolset(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_boolset(env, ite->e_then(i)); + } + return eval_boolset(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + Expression* lhs = eval_par(env, bo->lhs()); + Expression* rhs = eval_par(env, bo->rhs()); + if (lhs->type().isintset() && rhs->type().isintset()) { + IntSetVal* v0 = eval_boolset(env, lhs); + IntSetVal* v1 = eval_boolset(env, rhs); + IntSetRanges ir0(v0); + IntSetRanges ir1(v1); + switch (bo->op()) { + case BOT_UNION: { + Ranges::Union u(ir0, ir1); + return IntSetVal::ai(u); + } + case BOT_DIFF: { + Ranges::Diff u(ir0, ir1); + return IntSetVal::ai(u); + } + case BOT_SYMDIFF: { + Ranges::Union u(ir0, ir1); + Ranges::Inter i(ir0, ir1); + Ranges::Diff, + Ranges::Inter> + sd(u, i); + return IntSetVal::ai(sd); + } + case BOT_INTERSECT: { + Ranges::Inter u(ir0, ir1); + return IntSetVal::ai(u); + } + default: + throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); + } + } else if (lhs->type().isbool() && rhs->type().isbool()) { + if (bo->op() != BOT_DOTDOT) + throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); + return IntSetVal::a(eval_bool(env, lhs), eval_bool(env, rhs)); + } else { + throw EvalError(env, e->loc(), "not a set of bool expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + throw EvalError(env, e->loc(), "not a set of bool expression"); + } + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + + if (ce->decl()->_builtins.s) return ce->decl()->_builtins.s(env, ce); + + if (ce->decl()->_builtins.e) return eval_boolset(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + IntSetVal* ret = eval_boolset(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return NULL; + } +} + +IntVal eval_int(EnvI& env, Expression* e) { + if (e->type().isbool()) { + return eval_bool(env, e); + } + if (IntLit* il = e->dyn_cast()) { + return il->v(); + } + CallStackItem csi(env, e); + try { + switch (e->eid()) { + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_SETLIT: + case Expression::E_ARRAYLIT: + case Expression::E_COMP: + case Expression::E_VARDECL: + case Expression::E_TI: + throw EvalError(env, e->loc(), "not an integer expression"); + break; + case Expression::E_ID: { + GCLock lock; + return eval_id(env, e)->v(); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_int(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_int(env, ite->e_then(i)); + } + return eval_int(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + IntVal v0 = eval_int(env, bo->lhs()); + IntVal v1 = eval_int(env, bo->rhs()); + switch (bo->op()) { + case BOT_PLUS: + return v0 + v1; + case BOT_MINUS: + return v0 - v1; + case BOT_MULT: + return v0 * v1; + case BOT_POW: + return v0.pow(v1); + case BOT_IDIV: + if (v1 == 0) throw ResultUndefinedError(env, e->loc(), "division by zero"); + return v0 / v1; + case BOT_MOD: + if (v1 == 0) throw ResultUndefinedError(env, e->loc(), "division by zero"); + return v0 % v1; + default: + throw EvalError(env, e->loc(), "not an integer expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + IntVal v0 = eval_int(env, uo->e()); + switch (uo->op()) { + case UOT_PLUS: + return v0; + case UOT_MINUS: + return -v0; + default: + throw EvalError(env, e->loc(), "not an integer expression", uo->opToString()); + } + } break; + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + if (ce->decl()->_builtins.i) return ce->decl()->_builtins.i(env, ce); + + if (ce->decl()->_builtins.e) return eval_int(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), + "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + IntVal ret = eval_int(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return 0; + } + } catch (ArithmeticError& err) { + throw EvalError(env, e->loc(), err.msg()); + } +} + +FloatVal eval_float(EnvI& env, Expression* e) { + CallStackItem csi(env, e); + try { + if (e->type().isint()) { + return FloatVal(eval_int(env, e).toInt()); + } else if (e->type().isbool()) { + return eval_bool(env, e); + } + if (FloatLit* fl = e->dyn_cast()) { + return fl->v(); + } + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_SETLIT: + case Expression::E_ARRAYLIT: + case Expression::E_COMP: + case Expression::E_VARDECL: + case Expression::E_TI: + throw EvalError(env, e->loc(), "not a float expression"); + break; + case Expression::E_ID: { + GCLock lock; + return eval_id(env, e)->v(); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_float(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_float(env, ite->e_then(i)); + } + return eval_float(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + FloatVal v0 = eval_float(env, bo->lhs()); + FloatVal v1 = eval_float(env, bo->rhs()); + switch (bo->op()) { + case BOT_PLUS: + return v0 + v1; + case BOT_MINUS: + return v0 - v1; + case BOT_MULT: + return v0 * v1; + case BOT_POW: + return std::pow(v0.toDouble(), v1.toDouble()); + case BOT_DIV: + if (v1 == 0.0) throw ResultUndefinedError(env, e->loc(), "division by zero"); + return v0 / v1; + default: + throw EvalError(env, e->loc(), "not a float expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + FloatVal v0 = eval_float(env, uo->e()); + switch (uo->op()) { + case UOT_PLUS: + return v0; + case UOT_MINUS: + return -v0; + default: + throw EvalError(env, e->loc(), "not a float expression", uo->opToString()); + } + } break; + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + if (ce->decl()->_builtins.f) return ce->decl()->_builtins.f(env, ce); + + if (ce->decl()->_builtins.e) return eval_float(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), + "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + FloatVal ret = eval_float(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return 0.0; + } + } catch (ArithmeticError& err) { + throw EvalError(env, e->loc(), err.msg()); + } +} + +std::string eval_string(EnvI& env, Expression* e) { + CallStackItem csi(env, e); + switch (e->eid()) { + case Expression::E_STRINGLIT: + return e->cast()->v().str(); + case Expression::E_FLOATLIT: + case Expression::E_INTLIT: + case Expression::E_BOOLLIT: + case Expression::E_ANON: + case Expression::E_TIID: + case Expression::E_SETLIT: + case Expression::E_ARRAYLIT: + case Expression::E_COMP: + case Expression::E_VARDECL: + case Expression::E_TI: + throw EvalError(env, e->loc(), "not a string expression"); + break; + case Expression::E_ID: { + GCLock lock; + return eval_id(env, e)->v().str(); + } break; + case Expression::E_ARRAYACCESS: { + GCLock lock; + return eval_string(env, eval_arrayaccess(env, e->cast())); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (eval_bool(env, ite->e_if(i))) return eval_string(env, ite->e_then(i)); + } + return eval_string(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->decl() && bo->decl()->e()) { + return eval_call(env, bo); + } + std::string v0 = eval_string(env, bo->lhs()); + std::string v1 = eval_string(env, bo->rhs()); + switch (bo->op()) { + case BOT_PLUSPLUS: + return v0 + v1; + default: + throw EvalError(env, e->loc(), "not a string expression", bo->opToString()); + } + } break; + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + if (uo->decl() && uo->decl()->e()) { + return eval_call(env, uo); + } + throw EvalError(env, e->loc(), "not a string expression"); + } break; + case Expression::E_CALL: { + Call* ce = e->cast(); + if (ce->decl() == NULL) throw EvalError(env, e->loc(), "undeclared function", ce->id()); + + if (ce->decl()->_builtins.str) return ce->decl()->_builtins.str(env, ce); + if (ce->decl()->_builtins.e) return eval_string(env, ce->decl()->_builtins.e(env, ce)); + + if (ce->decl()->e() == NULL) + throw EvalError(env, ce->loc(), "internal error: missing builtin '" + ce->id().str() + "'"); + + return eval_call(env, ce); + } break; + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + for (unsigned int i = 0; i < l->let().size(); i++) { + // Evaluate all variable declarations + if (VarDecl* vdi = l->let()[i]->dyn_cast()) { + vdi->e(eval_par(env, vdi->e())); + if (vdi->ti()->domain()) { + if (!checkParDomain(env, vdi->e(), vdi->ti()->domain())) { + throw ResultUndefinedError(env, l->let()[i]->loc(), + "domain constraint in let failed"); + } + } + } else { + // This is a constraint item. Since the let is par, + // it can only be a par bool expression. If it evaluates + // to false, it means that the value of this let is undefined. + if (!eval_bool(env, l->let()[i])) { + throw ResultUndefinedError(env, l->let()[i]->loc(), "constraint in let failed"); + } + } + } + std::string ret = eval_string(env, l->in()); + l->popbindings(); + return ret; + } break; + default: + assert(false); + return NULL; + } +} + +Expression* eval_par(EnvI& env, Expression* e) { + if (e == NULL) return NULL; + switch (e->eid()) { + case Expression::E_ANON: + case Expression::E_TIID: { + return e; + } + case Expression::E_COMP: + if (e->cast()->set()) return EvalSetLit::e(env, e); + // fall through + case Expression::E_ARRAYLIT: { + ArrayLit* al = eval_array_lit(env, e); + std::vector args(al->size()); + for (unsigned int i = al->size(); i--;) args[i] = eval_par(env, (*al)[i]); + std::vector> dims(al->dims()); + for (unsigned int i = al->dims(); i--;) { + dims[i].first = al->min(i); + dims[i].second = al->max(i); + } + ArrayLit* ret = new ArrayLit(al->loc(), args, dims); + Type t = al->type(); + if (t.isbot() && ret->size() > 0) { + t.bt((*ret)[0]->type().bt()); + } + ret->type(t); + return ret; + } + case Expression::E_VARDECL: { + VarDecl* vd = e->cast(); + throw EvalError(env, vd->loc(), "cannot evaluate variable declaration", vd->id()->v()); + } + case Expression::E_TI: { + TypeInst* t = e->cast(); + ASTExprVec r; + if (t->ranges().size() > 0) { + std::vector rv(t->ranges().size()); + for (unsigned int i = t->ranges().size(); i--;) + rv[i] = static_cast(eval_par(env, t->ranges()[i])); + r = ASTExprVec(rv); + } + return new TypeInst(Location(), t->type(), r, eval_par(env, t->domain())); + } + case Expression::E_ID: { + if (e == constants().absent) return e; + Id* id = e->cast(); + if (id->decl() == NULL) throw EvalError(env, e->loc(), "undefined identifier", id->v()); + if (id->decl()->ti()->domain()) { + if (BoolLit* bl = id->decl()->ti()->domain()->dyn_cast()) return bl; + if (id->decl()->ti()->type().isint()) { + if (SetLit* sl = id->decl()->ti()->domain()->dyn_cast()) { + if (sl->isv() && sl->isv()->min() == sl->isv()->max()) { + return IntLit::a(sl->isv()->min()); + } + } + } else if (id->decl()->ti()->type().isfloat()) { + if (id->decl()->ti()->domain()) { + FloatSetVal* fsv = eval_floatset(env, id->decl()->ti()->domain()); + if (fsv->min() == fsv->max()) { + return FloatLit::a(fsv->min()); + } + } + } + } + if (id->decl()->e() == NULL) { + return id; + } else { + return eval_par(env, id->decl()->e()); + } + } + case Expression::E_STRINGLIT: + return e; + default: { + if (e->type().dim() != 0) { + ArrayLit* al = eval_array_lit(env, e); + std::vector args(al->size()); + for (unsigned int i = al->size(); i--;) args[i] = eval_par(env, (*al)[i]); + std::vector> dims(al->dims()); + for (unsigned int i = al->dims(); i--;) { + dims[i].first = al->min(i); + dims[i].second = al->max(i); + } + ArrayLit* ret = new ArrayLit(al->loc(), args, dims); + Type t = al->type(); + if ((t.bt() == Type::BT_BOT || t.bt() == Type::BT_TOP) && ret->size() > 0) { + t.bt((*ret)[0]->type().bt()); + } + ret->type(t); + return ret; + } + if (e->type().ispar()) { + if (e->type().isintset()) { + return EvalSetLit::e(env, e); + } + if (e->type().isfloatset()) { + return EvalFloatSetLit::e(env, e); + } + if (e->type().isboolset()) { + return EvalBoolSetLit::e(env, e); + } + if (e->type() == Type::parint()) { + return EvalIntLit::e(env, e); + } + if (e->type() == Type::parbool()) { + return EvalBoolLit::e(env, e); + } + if (e->type() == Type::parfloat()) { + return EvalFloatLit::e(env, e); + } + if (e->type() == Type::parstring()) { + return EvalStringLit::e(env, e); + } + } + switch (e->eid()) { + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + if (ite->e_if(i)->type() == Type::parbool()) { + if (eval_bool(env, ite->e_if(i))) return eval_par(env, ite->e_then(i)); + } else { + std::vector e_ifthen(ite->size() * 2); + for (int i = 0; i < ite->size(); i++) { + e_ifthen[2 * i] = eval_par(env, ite->e_if(i)); + e_ifthen[2 * i + 1] = eval_par(env, ite->e_then(i)); + } + ITE* n_ite = new ITE(ite->loc(), e_ifthen, eval_par(env, ite->e_else())); + n_ite->type(ite->type()); + return n_ite; + } + } + return eval_par(env, ite->e_else()); + } + case Expression::E_CALL: { + Call* c = e->cast(); + if (c->decl()) { + if (c->decl()->_builtins.e) { + return eval_par(env, c->decl()->_builtins.e(env, c)); + } else { + if (c->decl()->e() == NULL) { + if (c->id() == "deopt" && Expression::equal(c->arg(0), constants().absent)) + throw ResultUndefinedError(env, e->loc(), "deopt(<>) is undefined"); + return c; + } + return eval_call(env, c); + } + } else { + std::vector args(c->n_args()); + for (unsigned int i = 0; i < args.size(); i++) { + args[i] = eval_par(env, c->arg(i)); + } + Call* nc = new Call(c->loc(), c->id(), args); + nc->type(c->type()); + return nc; + } + } + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + BinOp* nbo = + new BinOp(e->loc(), eval_par(env, bo->lhs()), bo->op(), eval_par(env, bo->rhs())); + nbo->type(bo->type()); + return nbo; + } + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + UnOp* nuo = new UnOp(e->loc(), uo->op(), eval_par(env, uo->e())); + nuo->type(uo->type()); + return nuo; + } + case Expression::E_ARRAYACCESS: { + ArrayAccess* aa = e->cast(); + for (unsigned int i = 0; i < aa->idx().size(); i++) { + if (!aa->idx()[i]->type().ispar()) { + std::vector idx(aa->idx().size()); + for (unsigned int j = 0; j < aa->idx().size(); j++) { + idx[j] = eval_par(env, aa->idx()[j]); + } + ArrayAccess* aa_new = new ArrayAccess(e->loc(), eval_par(env, aa->v()), idx); + aa_new->type(aa->type()); + return aa_new; + } + } + return eval_par(env, eval_arrayaccess(env, aa)); + } + default: + return e; + } + } + } +} + +class ComputeIntBounds : public EVisitor { +public: + typedef std::pair Bounds; + std::vector _bounds; + bool valid; + EnvI& env; + ComputeIntBounds(EnvI& env0) : valid(true), env(env0) {} + bool enter(Expression* e) { + if (e->type().isann()) return false; + if (e->isa()) return false; + if (e->type().dim() > 0) return false; + if (e->type().ispar()) { + if (e->type().isint()) { + IntVal v = eval_int(env, e); + _bounds.push_back(Bounds(v, v)); + } else { + valid = false; + } + return false; + } + if (e->type().isint()) { + if (ITE* ite = e->dyn_cast()) { + Bounds itebounds(IntVal::infinity(), -IntVal::infinity()); + for (int i = 0; i < ite->size(); i++) { + if (ite->e_if(i)->type().ispar() && ite->e_if(i)->type().cv() == Type::CV_NO) { + if (eval_bool(env, ite->e_if(i))) { + BottomUpIterator cbi(*this); + cbi.run(ite->e_then(i)); + Bounds& back = _bounds.back(); + back.first = std::min(itebounds.first, back.first); + back.second = std::max(itebounds.second, back.second); + return false; + } + } else { + BottomUpIterator cbi(*this); + cbi.run(ite->e_then(i)); + Bounds back = _bounds.back(); + _bounds.pop_back(); + itebounds.first = std::min(itebounds.first, back.first); + itebounds.second = std::max(itebounds.second, back.second); + } + } + BottomUpIterator cbi(*this); + cbi.run(ite->e_else()); + Bounds& back = _bounds.back(); + back.first = std::min(itebounds.first, back.first); + back.second = std::max(itebounds.second, back.second); + return false; + } + return true; + } + return false; + } + /// Visit integer literal + void vIntLit(const IntLit& i) { _bounds.push_back(Bounds(i.v(), i.v())); } + /// Visit floating point literal + void vFloatLit(const FloatLit&) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit Boolean literal + void vBoolLit(const BoolLit&) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit set literal + void vSetLit(const SetLit&) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit string literal + void vStringLit(const StringLit&) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit identifier + void vId(const Id& id) { + VarDecl* vd = id.decl(); + while (vd->flat() && vd->flat() != vd) vd = vd->flat(); + if (vd->ti()->domain()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, vd->ti()->domain()); + if (isv->size() == 0) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } else { + _bounds.push_back(Bounds(isv->min(0), isv->max(isv->size() - 1))); + } + } else { + if (vd->e()) { + BottomUpIterator cbi(*this); + cbi.run(vd->e()); + } else { + _bounds.push_back(Bounds(-IntVal::infinity(), IntVal::infinity())); + } + } + } + /// Visit anonymous variable + void vAnonVar(const AnonVar& v) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit array literal + void vArrayLit(const ArrayLit& al) {} + /// Visit array access + void vArrayAccess(ArrayAccess& aa) { + bool parAccess = true; + for (unsigned int i = aa.idx().size(); i--;) { + _bounds.pop_back(); + if (!aa.idx()[i]->type().ispar()) { + parAccess = false; + } + } + if (Id* id = aa.v()->dyn_cast()) { + while (id->decl()->e() && id->decl()->e()->isa()) { + id = id->decl()->e()->cast(); + } + if (parAccess && id->decl()->e()) { + bool success; + Expression* e = eval_arrayaccess(env, &aa, success); + if (success) { + BottomUpIterator cbi(*this); + cbi.run(e); + return; + } + } + if (id->decl()->ti()->domain()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, id->decl()->ti()->domain()); + if (isv->size() > 0) { + _bounds.push_back(Bounds(isv->min(0), isv->max(isv->size() - 1))); + return; + } + } + } + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit array comprehension + void vComprehension(const Comprehension& c) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit if-then-else + void vITE(const ITE& ite) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit binary operator + void vBinOp(const BinOp& bo) { + Bounds b1 = _bounds.back(); + _bounds.pop_back(); + Bounds b0 = _bounds.back(); + _bounds.pop_back(); + if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || + !b0.second.isFinite()) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } else { + switch (bo.op()) { + case BOT_PLUS: + _bounds.push_back(Bounds(b0.first + b1.first, b0.second + b1.second)); + break; + case BOT_MINUS: + _bounds.push_back(Bounds(b0.first - b1.second, b0.second - b1.first)); + break; + case BOT_MULT: { + IntVal x0 = b0.first * b1.first; + IntVal x1 = b0.first * b1.second; + IntVal x2 = b0.second * b1.first; + IntVal x3 = b0.second * b1.second; + IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(Bounds(m, n)); + } break; + case BOT_IDIV: { + IntVal b0f = b0.first == 0 ? 1 : b0.first; + IntVal b0s = b0.second == 0 ? -1 : b0.second; + IntVal b1f = b1.first == 0 ? 1 : b1.first; + IntVal b1s = b1.second == 0 ? -1 : b1.second; + IntVal x0 = b0f / b1f; + IntVal x1 = b0f / b1s; + IntVal x2 = b0s / b1f; + IntVal x3 = b0s / b1s; + IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(Bounds(m, n)); + } break; + case BOT_MOD: { + IntVal b0f = b0.first == 0 ? 1 : b0.first; + IntVal b0s = b0.second == 0 ? -1 : b0.second; + IntVal b1f = b1.first == 0 ? 1 : b1.first; + IntVal b1s = b1.second == 0 ? -1 : b1.second; + IntVal x0 = b0f % b1f; + IntVal x1 = b0f % b1s; + IntVal x2 = b0s % b1f; + IntVal x3 = b0s % b1s; + IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(Bounds(m, n)); + } break; + case BOT_POW: { + IntVal exp_min = std::min(0, b1.first); + IntVal exp_max = std::min(0, b1.second); + + IntVal x0 = b0.first.pow(exp_min); + IntVal x1 = b0.first.pow(exp_max); + IntVal x2 = b0.second.pow(exp_min); + IntVal x3 = b0.second.pow(exp_max); + IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(Bounds(m, n)); + } break; + case BOT_DIV: + case BOT_LE: + case BOT_LQ: + case BOT_GR: + case BOT_GQ: + case BOT_EQ: + case BOT_NQ: + case BOT_IN: + case BOT_SUBSET: + case BOT_SUPERSET: + case BOT_UNION: + case BOT_DIFF: + case BOT_SYMDIFF: + case BOT_INTERSECT: + case BOT_PLUSPLUS: + case BOT_EQUIV: + case BOT_IMPL: + case BOT_RIMPL: + case BOT_OR: + case BOT_AND: + case BOT_XOR: + case BOT_DOTDOT: + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + } + } + /// Visit unary operator + void vUnOp(const UnOp& uo) { + switch (uo.op()) { + case UOT_PLUS: + break; + case UOT_MINUS: + _bounds.back().first = -_bounds.back().first; + _bounds.back().second = -_bounds.back().second; + std::swap(_bounds.back().first, _bounds.back().second); + break; + case UOT_NOT: + valid = false; + } + } + /// Visit call + void vCall(Call& c) { + if (c.id() == constants().ids.lin_exp || c.id() == constants().ids.sum) { + bool le = c.id() == constants().ids.lin_exp; + ArrayLit* coeff = le ? eval_array_lit(env, c.arg(0)) : NULL; + if (c.arg(le ? 1 : 0)->type().isopt()) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + return; + } + ArrayLit* al = eval_array_lit(env, c.arg(le ? 1 : 0)); + if (le) { + _bounds.pop_back(); // remove constant (third arg) from stack + } + + IntVal d = le ? c.arg(2)->cast()->v() : 0; + int stacktop = static_cast(_bounds.size()); + for (unsigned int i = al->size(); i--;) { + BottomUpIterator cbi(*this); + cbi.run((*al)[i]); + if (!valid) { + for (unsigned int j = al->size() - 1; j > i; j--) _bounds.pop_back(); + return; + } + } + assert(stacktop + al->size() == _bounds.size()); + IntVal lb = d; + IntVal ub = d; + for (unsigned int i = 0; i < al->size(); i++) { + Bounds b = _bounds.back(); + _bounds.pop_back(); + IntVal cv = le ? eval_int(env, (*coeff)[i]) : 1; + if (cv > 0) { + if (b.first.isFinite()) { + if (lb.isFinite()) { + lb += cv * b.first; + } + } else { + lb = b.first; + } + if (b.second.isFinite()) { + if (ub.isFinite()) { + ub += cv * b.second; + } + } else { + ub = b.second; + } + } else { + if (b.second.isFinite()) { + if (lb.isFinite()) { + lb += cv * b.second; + } + } else { + lb = -b.second; + } + if (b.first.isFinite()) { + if (ub.isFinite()) { + ub += cv * b.first; + } + } else { + ub = -b.first; + } + } + } + _bounds.push_back(Bounds(lb, ub)); + } else if (c.id() == "card") { + if (IntSetVal* isv = compute_intset_bounds(env, c.arg(0))) { + IntSetRanges isr(isv); + _bounds.push_back(Bounds(0, Ranges::cardinality(isr))); + } else { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + } else if (c.id() == "int_times") { + Bounds b1 = _bounds.back(); + _bounds.pop_back(); + Bounds b0 = _bounds.back(); + _bounds.pop_back(); + if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || + !b0.second.isFinite()) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } else { + IntVal x0 = b0.first * b1.first; + IntVal x1 = b0.first * b1.second; + IntVal x2 = b0.second * b1.first; + IntVal x3 = b0.second * b1.second; + IntVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + IntVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(Bounds(m, n)); + } + } else if (c.id() == constants().ids.bool2int) { + _bounds.push_back(Bounds(0, 1)); + } else if (c.id() == "abs") { + Bounds b0 = _bounds.back(); + if (b0.first < 0) { + _bounds.pop_back(); + if (b0.second < 0) + _bounds.push_back(Bounds(-b0.second, -b0.first)); + else + _bounds.push_back(Bounds(0, std::max(-b0.first, b0.second))); + } + } else if (c.decl() && c.decl()->ti()->domain() && !c.decl()->ti()->domain()->isa()) { + for (int i = 0; i < c.n_args(); i++) { + if (c.arg(i)->type().isint()) { + assert(_bounds.size() > 0); + _bounds.pop_back(); + } + } + IntSetVal* isv = eval_intset(env, c.decl()->ti()->domain()); + _bounds.push_back(Bounds(isv->min(), isv->max())); + } else { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + } + /// Visit let + void vLet(const Let& l) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit variable declaration + void vVarDecl(const VarDecl& vd) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit annotation + void vAnnotation(const Annotation& e) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit type inst + void vTypeInst(const TypeInst& e) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } + /// Visit TIId + void vTIId(const TIId& e) { + valid = false; + _bounds.push_back(Bounds(0, 0)); + } +}; + +IntBounds compute_int_bounds(EnvI& env, Expression* e) { + try { + ComputeIntBounds cb(env); + BottomUpIterator cbi(cb); + cbi.run(e); + if (cb.valid) { + assert(cb._bounds.size() == 1); + return IntBounds(cb._bounds.back().first, cb._bounds.back().second, true); + } else { + return IntBounds(0, 0, false); + } + } catch (ResultUndefinedError&) { + return IntBounds(0, 0, false); + } +} + +class ComputeFloatBounds : public EVisitor { +protected: + typedef std::pair FBounds; + +public: + std::vector _bounds; + bool valid; + EnvI& env; + ComputeFloatBounds(EnvI& env0) : valid(true), env(env0) {} + bool enter(Expression* e) { + if (e->type().isann()) return false; + if (e->isa()) return false; + if (e->type().dim() > 0) return false; + if (e->type().ispar()) { + if (e->type().isfloat()) { + FloatVal v = eval_float(env, e); + _bounds.push_back(FBounds(v, v)); + } + return false; + } else { + return e->type().isfloat(); + } + } + /// Visit integer literal + void vIntLit(const IntLit& i) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit floating point literal + void vFloatLit(const FloatLit& f) { _bounds.push_back(FBounds(f.v(), f.v())); } + /// Visit Boolean literal + void vBoolLit(const BoolLit&) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit set literal + void vSetLit(const SetLit&) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit string literal + void vStringLit(const StringLit&) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit identifier + void vId(const Id& id) { + VarDecl* vd = id.decl(); + while (vd->flat() && vd->flat() != vd) vd = vd->flat(); + if (vd->ti()->domain()) { + FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); + _bounds.push_back(FBounds(fsv->min(), fsv->max())); + } else { + if (vd->e()) { + BottomUpIterator cbi(*this); + cbi.run(vd->e()); + } else { + valid = false; + _bounds.push_back(FBounds(0, 0)); + } + } + } + /// Visit anonymous variable + void vAnonVar(const AnonVar& v) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit array literal + void vArrayLit(const ArrayLit& al) {} + /// Visit array access + void vArrayAccess(ArrayAccess& aa) { + bool parAccess = true; + for (unsigned int i = aa.idx().size(); i--;) { + if (!aa.idx()[i]->type().ispar()) { + parAccess = false; + } + } + if (Id* id = aa.v()->dyn_cast()) { + while (id->decl()->e() && id->decl()->e()->isa()) { + id = id->decl()->e()->cast(); + } + if (parAccess && id->decl()->e()) { + bool success; + Expression* e = eval_arrayaccess(env, &aa, success); + if (success) { + BottomUpIterator cbi(*this); + cbi.run(e); + return; + } + } + if (id->decl()->ti()->domain()) { + FloatSetVal* fsv = eval_floatset(env, id->decl()->ti()->domain()); + FBounds b(fsv->min(), fsv->max()); + _bounds.push_back(b); + return; + } + } + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit array comprehension + void vComprehension(const Comprehension& c) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit if-then-else + void vITE(const ITE& ite) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit binary operator + void vBinOp(const BinOp& bo) { + FBounds b1 = _bounds.back(); + _bounds.pop_back(); + FBounds b0 = _bounds.back(); + _bounds.pop_back(); + if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || + !b0.second.isFinite()) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } else { + switch (bo.op()) { + case BOT_PLUS: + _bounds.push_back(FBounds(b0.first + b1.first, b0.second + b1.second)); + break; + case BOT_MINUS: + _bounds.push_back(FBounds(b0.first - b1.second, b0.second - b1.first)); + break; + case BOT_MULT: { + FloatVal x0 = b0.first * b1.first; + FloatVal x1 = b0.first * b1.second; + FloatVal x2 = b0.second * b1.first; + FloatVal x3 = b0.second * b1.second; + FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(FBounds(m, n)); + } break; + case BOT_POW: { + FloatVal x0 = std::pow(b0.first.toDouble(), b1.first.toDouble()); + FloatVal x1 = std::pow(b0.first.toDouble(), b1.second.toDouble()); + FloatVal x2 = std::pow(b0.second.toDouble(), b1.first.toDouble()); + FloatVal x3 = std::pow(b0.second.toDouble(), b1.second.toDouble()); + FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(FBounds(m, n)); + } break; + case BOT_DIV: + case BOT_IDIV: + case BOT_MOD: + case BOT_LE: + case BOT_LQ: + case BOT_GR: + case BOT_GQ: + case BOT_EQ: + case BOT_NQ: + case BOT_IN: + case BOT_SUBSET: + case BOT_SUPERSET: + case BOT_UNION: + case BOT_DIFF: + case BOT_SYMDIFF: + case BOT_INTERSECT: + case BOT_PLUSPLUS: + case BOT_EQUIV: + case BOT_IMPL: + case BOT_RIMPL: + case BOT_OR: + case BOT_AND: + case BOT_XOR: + case BOT_DOTDOT: + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + } + } + /// Visit unary operator + void vUnOp(const UnOp& uo) { + switch (uo.op()) { + case UOT_PLUS: + break; + case UOT_MINUS: + _bounds.back().first = -_bounds.back().first; + _bounds.back().second = -_bounds.back().second; + break; + case UOT_NOT: + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + } + /// Visit call + void vCall(Call& c) { + if (c.id() == constants().ids.lin_exp || c.id() == constants().ids.sum) { + bool le = c.id() == constants().ids.lin_exp; + ArrayLit* coeff = le ? eval_array_lit(env, c.arg(0)) : NULL; + if (le) { + _bounds.pop_back(); // remove constant (third arg) from stack + } + if (c.arg(le ? 1 : 0)->type().isopt()) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + return; + } + ArrayLit* al = eval_array_lit(env, c.arg(le ? 1 : 0)); + FloatVal d = le ? c.arg(2)->cast()->v() : 0.0; + int stacktop = static_cast(_bounds.size()); + for (unsigned int i = al->size(); i--;) { + BottomUpIterator cbi(*this); + cbi.run((*al)[i]); + if (!valid) return; + } + assert(stacktop + al->size() == _bounds.size()); + FloatVal lb = d; + FloatVal ub = d; + for (unsigned int i = 0; i < (*al).size(); i++) { + FBounds b = _bounds.back(); + _bounds.pop_back(); + FloatVal cv = le ? eval_float(env, (*coeff)[i]) : 1.0; + + if (cv > 0) { + if (b.first.isFinite()) { + if (lb.isFinite()) { + lb += cv * b.first; + } + } else { + lb = b.first; + } + if (b.second.isFinite()) { + if (ub.isFinite()) { + ub += cv * b.second; + } + } else { + ub = b.second; + } + } else { + if (b.second.isFinite()) { + if (lb.isFinite()) { + lb += cv * b.second; + } + } else { + lb = -b.second; + } + if (b.first.isFinite()) { + if (ub.isFinite()) { + ub += cv * b.first; + } + } else { + ub = -b.first; + } + } + } + _bounds.push_back(FBounds(lb, ub)); + } else if (c.id() == "float_times") { + BottomUpIterator cbi(*this); + cbi.run(c.arg(0)); + cbi.run(c.arg(1)); + FBounds b1 = _bounds.back(); + _bounds.pop_back(); + FBounds b0 = _bounds.back(); + _bounds.pop_back(); + if (!b1.first.isFinite() || !b1.second.isFinite() || !b0.first.isFinite() || + !b0.second.isFinite()) { + valid = false; + _bounds.push_back(FBounds(0, 0)); + } else { + FloatVal x0 = b0.first * b1.first; + FloatVal x1 = b0.first * b1.second; + FloatVal x2 = b0.second * b1.first; + FloatVal x3 = b0.second * b1.second; + FloatVal m = std::min(x0, std::min(x1, std::min(x2, x3))); + FloatVal n = std::max(x0, std::max(x1, std::max(x2, x3))); + _bounds.push_back(FBounds(m, n)); + } + } else if (c.id() == "int2float") { + ComputeIntBounds ib(env); + BottomUpIterator cbi(ib); + cbi.run(c.arg(0)); + if (!ib.valid) valid = false; + ComputeIntBounds::Bounds result = ib._bounds.back(); + if (!result.first.isFinite() || !result.second.isFinite()) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } else { + _bounds.push_back(FBounds(static_cast(result.first.toInt()), + static_cast(result.second.toInt()))); + } + } else if (c.id() == "abs") { + BottomUpIterator cbi(*this); + cbi.run(c.arg(0)); + FBounds b0 = _bounds.back(); + if (b0.first < 0) { + _bounds.pop_back(); + if (b0.second < 0) + _bounds.push_back(FBounds(-b0.second, -b0.first)); + else + _bounds.push_back(FBounds(0.0, std::max(-b0.first, b0.second))); + } + } else if (c.decl() && c.decl()->ti()->domain() && !c.decl()->ti()->domain()->isa()) { + for (int i = 0; i < c.n_args(); i++) { + if (c.arg(i)->type().isfloat()) { + assert(_bounds.size() > 0); + _bounds.pop_back(); + } + } + FloatSetVal* fsv = eval_floatset(env, c.decl()->ti()->domain()); + _bounds.push_back(FBounds(fsv->min(), fsv->max())); + } else { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + } + /// Visit let + void vLet(const Let& l) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit variable declaration + void vVarDecl(const VarDecl& vd) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit annotation + void vAnnotation(const Annotation& e) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit type inst + void vTypeInst(const TypeInst& e) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } + /// Visit TIId + void vTIId(const TIId& e) { + valid = false; + _bounds.push_back(FBounds(0.0, 0.0)); + } +}; + +FloatBounds compute_float_bounds(EnvI& env, Expression* e) { + try { + ComputeFloatBounds cb(env); + BottomUpIterator cbi(cb); + cbi.run(e); + if (cb.valid) { + assert(cb._bounds.size() > 0); + return FloatBounds(cb._bounds.back().first, cb._bounds.back().second, true); + } else { + return FloatBounds(0.0, 0.0, false); + } + } catch (ResultUndefinedError&) { + return FloatBounds(0.0, 0.0, false); + } +} + +class ComputeIntSetBounds : public EVisitor { +public: + std::vector _bounds; + bool valid; + EnvI& env; + ComputeIntSetBounds(EnvI& env0) : valid(true), env(env0) {} + bool enter(Expression* e) { + if (e->type().isann()) return false; + if (e->isa()) return false; + if (e->type().dim() > 0) return false; + if (!e->type().isintset()) return false; + if (e->type().ispar()) { + _bounds.push_back(eval_intset(env, e)); + return false; + } else { + return true; + } + } + /// Visit set literal + void vSetLit(const SetLit& sl) { + assert(sl.type().isvar()); + assert(sl.isv() == NULL); + + IntSetVal* isv = IntSetVal::a(); + for (unsigned int i = 0; i < sl.v().size(); i++) { + IntSetRanges i0(isv); + IntBounds ib = compute_int_bounds(env, sl.v()[i]); + if (!ib.valid || !ib.l.isFinite() || !ib.u.isFinite()) { + valid = false; + _bounds.push_back(NULL); + return; + } + Ranges::Const cr(ib.l, ib.u); + Ranges::Union> u(i0, cr); + isv = IntSetVal::ai(u); + } + _bounds.push_back(isv); + } + /// Visit identifier + void vId(const Id& id) { + if (id.decl()->ti()->domain() && !id.decl()->ti()->domain()->isa()) { + _bounds.push_back(eval_intset(env, id.decl()->ti()->domain())); + } else { + if (id.decl()->e()) { + BottomUpIterator cbi(*this); + cbi.run(id.decl()->e()); + } else { + valid = false; + _bounds.push_back(NULL); + } + } + } + /// Visit anonymous variable + void vAnonVar(const AnonVar& v) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit array access + void vArrayAccess(ArrayAccess& aa) { + bool parAccess = true; + for (unsigned int i = aa.idx().size(); i--;) { + if (!aa.idx()[i]->type().ispar()) { + parAccess = false; + break; + } + } + if (Id* id = aa.v()->dyn_cast()) { + while (id->decl()->e() && id->decl()->e()->isa()) { + id = id->decl()->e()->cast(); + } + if (parAccess && id->decl()->e()) { + bool success; + Expression* e = eval_arrayaccess(env, &aa, success); + if (success) { + BottomUpIterator cbi(*this); + cbi.run(e); + return; + } + } + if (id->decl()->ti()->domain()) { + _bounds.push_back(eval_intset(env, id->decl()->ti()->domain())); + return; + } + } + valid = false; + _bounds.push_back(NULL); + } + /// Visit array comprehension + void vComprehension(const Comprehension& c) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit if-then-else + void vITE(const ITE& ite) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit binary operator + void vBinOp(const BinOp& bo) { + if (bo.op() == BOT_DOTDOT) { + IntBounds lb = compute_int_bounds(env, bo.lhs()); + IntBounds ub = compute_int_bounds(env, bo.rhs()); + valid = valid && lb.valid && ub.valid; + _bounds.push_back(IntSetVal::a(lb.l, ub.u)); + } else { + IntSetVal* b1 = _bounds.back(); + _bounds.pop_back(); + IntSetVal* b0 = _bounds.back(); + _bounds.pop_back(); + switch (bo.op()) { + case BOT_INTERSECT: + case BOT_UNION: { + IntSetRanges b0r(b0); + IntSetRanges b1r(b1); + Ranges::Union u(b0r, b1r); + _bounds.push_back(IntSetVal::ai(u)); + } break; + case BOT_DIFF: { + _bounds.push_back(b0); + } break; + case BOT_SYMDIFF: + valid = false; + _bounds.push_back(NULL); + break; + case BOT_PLUS: + case BOT_MINUS: + case BOT_MULT: + case BOT_POW: + case BOT_DIV: + case BOT_IDIV: + case BOT_MOD: + case BOT_LE: + case BOT_LQ: + case BOT_GR: + case BOT_GQ: + case BOT_EQ: + case BOT_NQ: + case BOT_IN: + case BOT_SUBSET: + case BOT_SUPERSET: + case BOT_PLUSPLUS: + case BOT_EQUIV: + case BOT_IMPL: + case BOT_RIMPL: + case BOT_OR: + case BOT_AND: + case BOT_XOR: + case BOT_DOTDOT: + valid = false; + _bounds.push_back(NULL); + } + } + } + /// Visit unary operator + void vUnOp(const UnOp& uo) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit call + void vCall(Call& c) { + if (valid && (c.id() == "set_intersect" || c.id() == "set_union")) { + IntSetVal* b0 = _bounds.back(); + _bounds.pop_back(); + IntSetVal* b1 = _bounds.back(); + _bounds.pop_back(); + IntSetRanges b0r(b0); + IntSetRanges b1r(b1); + Ranges::Union u(b0r, b1r); + _bounds.push_back(IntSetVal::ai(u)); + } else if (valid && c.id() == "set_diff") { + IntSetVal* b0 = _bounds.back(); + _bounds.pop_back(); + _bounds.pop_back(); // don't need bounds of right hand side + _bounds.push_back(b0); + } else if (c.decl() && c.decl()->ti()->domain() && !c.decl()->ti()->domain()->isa()) { + for (int i = 0; i < c.n_args(); i++) { + if (c.arg(i)->type().isintset()) { + assert(_bounds.size() > 0); + _bounds.pop_back(); + } + } + IntSetVal* fsv = eval_intset(env, c.decl()->ti()->domain()); + _bounds.push_back(fsv); + } else { + valid = false; + _bounds.push_back(NULL); + } + } + /// Visit let + void vLet(const Let& l) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit variable declaration + void vVarDecl(const VarDecl& vd) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit annotation + void vAnnotation(const Annotation& e) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit type inst + void vTypeInst(const TypeInst& e) { + valid = false; + _bounds.push_back(NULL); + } + /// Visit TIId + void vTIId(const TIId& e) { + valid = false; + _bounds.push_back(NULL); + } +}; + +IntSetVal* compute_intset_bounds(EnvI& env, Expression* e) { + try { + ComputeIntSetBounds cb(env); + BottomUpIterator cbi(cb); + cbi.run(e); + if (cb.valid) + return cb._bounds.back(); + else + return NULL; + } catch (ResultUndefinedError&) { + return NULL; + } +} + +Expression* follow_id(Expression* e) { + for (;;) { + if (e == NULL) return NULL; + if (e->eid() == Expression::E_ID && e != constants().absent) { + e = e->cast()->decl()->e(); + } else { + return e; + } + } +} + +Expression* follow_id_to_decl(Expression* e) { + for (;;) { + if (e == NULL) return NULL; + if (e == constants().absent) return e; + switch (e->eid()) { + case Expression::E_ID: + e = e->cast()->decl(); + break; + case Expression::E_VARDECL: + if (e->cast()->e() && e->cast()->e()->isa()) + e = e->cast()->e(); + else + return e; + break; + default: + return e; + } + } +} + +Expression* follow_id_to_value(Expression* e) { + Expression* decl = follow_id_to_decl(e); + if (VarDecl* vd = decl->dyn_cast()) { + if (vd->e() && vd->e()->type().ispar()) return vd->e(); + return vd->id(); + } else { + return decl; + } +} + +} // namespace MiniZinc diff --git a/lib/file_utils.cpp b/lib/file_utils.cpp new file mode 100644 index 0000000..2d022eb --- /dev/null +++ b/lib/file_utils.cpp @@ -0,0 +1,683 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef HAS_PIDPATH +#include +#include +#include +#include +#include +#elif defined(HAS_GETMODULEFILENAME) || defined(HAS_GETFILEATTRIBUTES) +#include +#else +#include +#endif +#include +#include + +#ifdef _MSC_VER +#include "Shlwapi.h" +#pragma comment(lib, "Shlwapi.lib") +#include "Shlobj.h" + +#include +#else +#include +#include +#include +#endif + +namespace MiniZinc { +namespace FileUtils { + +#ifdef HAS_PIDPATH +std::string progpath(void) { + pid_t pid = getpid(); + char path[PROC_PIDPATHINFO_MAXSIZE]; + int ret = proc_pidpath(pid, path, sizeof(path)); + if (ret <= 0) { + return ""; + } else { + std::string p(path); + size_t slash = p.find_last_of("/"); + if (slash != std::string::npos) { + p = p.substr(0, slash); + } + return p; + } +} +#elif defined(HAS_GETMODULEFILENAME) +std::string progpath(void) { + char path[MAX_PATH]; + int ret = GetModuleFileName(NULL, path, MAX_PATH); + if (ret <= 0) { + return ""; + } else { + std::string p(path); + size_t slash = p.find_last_of("/\\"); + if (slash != std::string::npos) { + p = p.substr(0, slash); + } + return p; + } +} +#else +std::string progpath(void) { + const int bufsz = 2000; + char path[bufsz + 1]; + ssize_t sz = readlink("/proc/self/exe", path, bufsz); + if (sz < 0) { + return ""; + } else { + path[sz] = '\0'; + std::string p(path); + size_t slash = p.find_last_of("/"); + if (slash != std::string::npos) { + p = p.substr(0, slash); + } + return p; + } +} +#endif + +bool file_exists(const std::string& filename) { +#if defined(HAS_GETFILEATTRIBUTES) + DWORD dwAttrib = GetFileAttributes(filename.c_str()); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat info; + return stat(filename.c_str(), &info) == 0 && (info.st_mode & S_IFREG); +#endif +} + +bool directory_exists(const std::string& dirname) { +#if defined(HAS_GETFILEATTRIBUTES) + DWORD dwAttrib = GetFileAttributes(dirname.c_str()); + + return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat info; + return stat(dirname.c_str(), &info) == 0 && (info.st_mode & S_IFDIR); +#endif +} + +std::string file_path(const std::string& filename, const std::string& basePath) { +#ifdef _MSC_VER + LPSTR lpBuffer, lpFilePart; + DWORD nBufferLength = GetFullPathName(filename.c_str(), 0, 0, &lpFilePart); + if (!(lpBuffer = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * nBufferLength))) return 0; + std::string ret; + DWORD error = GetFullPathName(filename.c_str(), nBufferLength, lpBuffer, &lpFilePart); + DWORD fileAttr = GetFileAttributes(lpBuffer); + DWORD lastError = GetLastError(); + + if (error == 0 || (fileAttr == INVALID_FILE_ATTRIBUTES && lastError != NO_ERROR)) { + if (basePath.empty()) + ret = filename; + else + ret = file_path(basePath + "/" + filename); + } else { + ret = std::string(lpBuffer); + } + LocalFree(lpBuffer); + return ret; +#else + char* rp = realpath(filename.c_str(), NULL); + if (rp == NULL) { + if (basePath.empty()) + return filename; + else + return file_path(basePath + "/" + filename); + } + std::string rp_s(rp); + free(rp); + return rp_s; +#endif +} + +std::string dir_name(const std::string& filename) { +#ifdef _MSC_VER + size_t pos = filename.find_last_of("\\/"); + return (pos == std::string::npos) ? "" : filename.substr(0, pos); +#else + char* fn = strdup(filename.c_str()); + char* dn = dirname(fn); + std::string ret(dn); + free(fn); + return ret; +#endif +} + +std::string base_name(const std::string& filename) { +#ifdef _MSC_VER + size_t pos = filename.find_last_of("\\/"); + return (pos == std::string::npos) ? filename : filename.substr(pos + 1); +#else + char* fn = strdup(filename.c_str()); + char* dn = basename(fn); + std::string ret(dn); + free(fn); + return ret; +#endif +} + +bool is_absolute(const std::string& path) { +#ifdef _MSC_VER + return !PathIsRelative(path.c_str()); +#else + return path.empty() ? false : (path[0] == '/'); +#endif +} + +std::string find_executable(const std::string& filename) { + if (is_absolute(filename)) { + if (file_exists(filename)) { + return filename; + } +#ifdef _MSC_VER + if (FileUtils::file_exists(filename + ".exe")) { + return filename + ".exe"; + } else if (FileUtils::file_exists(filename + ".bat")) { + return filename + ".bat"; + } +#endif + return ""; + } + char* path_c = getenv("PATH"); +#ifdef _MSC_VER + char pathsep = ';'; +#else + char pathsep = ':'; +#endif + std::string path; + if (path_c) { + path = path_c; + if (path.size()) { + path += pathsep; + } + } + path += progpath(); + std::string pathItem; + std::stringstream pathStream(path); + while (std::getline(pathStream, pathItem, pathsep)) { + std::string fileWithPath = pathItem + "/" + filename; + if (file_exists(fileWithPath)) return fileWithPath; +#ifdef _MSC_VER + if (FileUtils::file_exists(fileWithPath + ".exe")) { + return fileWithPath + ".exe"; + } else if (FileUtils::file_exists(fileWithPath + ".bat")) { + return fileWithPath + ".bat"; + } +#endif + } + return ""; +} + +std::vector directory_list(const std::string& dir, const std::string& ext) { + std::vector entries; +#ifdef _MSC_VER + WIN32_FIND_DATA findData; + HANDLE hFind = ::FindFirstFile((dir + "/*." + ext).c_str(), &findData); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + entries.push_back(findData.cFileName); + } + } while (::FindNextFile(hFind, &findData)); + ::FindClose(hFind); + } +#else + DIR* dirp = opendir(dir.c_str()); + if (dirp) { + struct dirent* dp; + while ((dp = readdir(dirp)) != NULL) { + std::string fileName(dp->d_name); + struct stat info; + if (stat((dir + "/" + fileName).c_str(), &info) == 0 && (info.st_mode & S_IFREG)) { + if (ext == "*") { + entries.push_back(fileName); + } else { + if (fileName.size() > ext.size() + 2 && + fileName.substr(fileName.size() - ext.size() - 1) == "." + ext) { + entries.push_back(fileName); + } + } + } + } + closedir(dirp); + } +#endif + return entries; +} + +std::string working_directory(void) { + char wd[FILENAME_MAX]; +#ifdef _MSC_VER + if (!_getcwd(wd, sizeof(wd))) return ""; +#else + if (!getcwd(wd, sizeof(wd))) return ""; +#endif + return wd; +} + +std::string share_directory(void) { + if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) { + return std::string(MZNSTDLIBDIR); + } + std::string mypath = FileUtils::progpath(); + int depth = 0; + for (unsigned int i = 0; i < mypath.size(); i++) + if (mypath[i] == '/' || mypath[i] == '\\') depth++; + for (int i = 0; i <= depth; i++) { + if (FileUtils::file_exists(mypath + "/share/minizinc/std/builtins.mzn")) + return mypath + "/share/minizinc"; + mypath += "/.."; + } + return ""; +} + +std::string user_config_dir(void) { +#ifdef _MSC_VER + HRESULT hr; + PWSTR pszPath = NULL; + + hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &pszPath); + if (SUCCEEDED(hr)) { + int charsRequired = WideCharToMultiByte(CP_ACP, 0, pszPath, -1, 0, 0, 0, 0); + std::string configPath; + if (charsRequired > 0) { + char* tmp = new char[charsRequired]; + if (WideCharToMultiByte(CP_ACP, 0, pszPath, -1, tmp, charsRequired, 0, 0) != 0) { + tmp[charsRequired - 1] = 0; + configPath = tmp; + } + delete[] tmp; + } + CoTaskMemFree(pszPath); + if (configPath.empty()) { + return ""; + } else { + return configPath + "/MiniZinc"; + } + } + return ""; +#else + if (const char* hd = getenv("HOME")) { + return std::string(hd) + "/.minizinc"; + } + return ""; +#endif +} + +std::string global_config_file(void) { + std::string sd = share_directory(); + if (sd.empty()) return ""; + return sd + "/Preferences.json"; +} + +std::string user_config_file(void) { return user_config_dir() + "/Preferences.json"; } + +TmpFile::TmpFile(const std::string& ext) { +#ifdef _WIN32 + TCHAR szTempFileName[MAX_PATH]; + TCHAR lpTempPathBuffer[MAX_PATH]; + + GetTempPath(MAX_PATH, lpTempPathBuffer); + GetTempFileName(lpTempPathBuffer, "tmp_mzn_", 0, szTempFileName); + + _name = szTempFileName; + MoveFile(_name.c_str(), (_name + ext).c_str()); + _name += ext; +#else + _tmpfile_desc = -1; + _name = "/tmp/mznfileXXXXXX" + ext; + char* tmpfile = strndup(_name.c_str(), _name.size()); + _tmpfile_desc = mkstemps(tmpfile, ext.size()); + if (_tmpfile_desc == -1) { + ::free(tmpfile); + throw InternalError("Error occurred when creating temporary file"); + } + _name = std::string(tmpfile); + ::free(tmpfile); +#endif +} + +TmpFile::~TmpFile(void) { + remove(_name.c_str()); +#ifndef _WIN32 + if (_tmpfile_desc != -1) close(_tmpfile_desc); +#endif +} + +TmpDir::TmpDir(void) { +#ifdef _WIN32 + TCHAR szTempFileName[MAX_PATH]; + TCHAR lpTempPathBuffer[MAX_PATH]; + + GetTempPath(MAX_PATH, lpTempPathBuffer); + GetTempFileName(lpTempPathBuffer, "tmp_mzn_", 0, szTempFileName); + + _name = szTempFileName; + DeleteFile(_name.c_str()); + CreateDirectory(_name.c_str(), NULL); +#else + _name = "/tmp/mzndirXXXXXX"; + char* tmpfile = strndup(_name.c_str(), _name.size()); + + if (mkdtemp(tmpfile) == NULL) { + ::free(tmpfile); + throw InternalError("Error occurred when creating temporary directory"); + } + _name = std::string(tmpfile); + ::free(tmpfile); +#endif +} + +#ifdef _WIN32 +namespace { +void remove_dir(const std::string& d) { + HANDLE dh; + WIN32_FIND_DATA info; + + std::string pattern = d + "\\*.*"; + dh = ::FindFirstFile(pattern.c_str(), &info); + if (dh != INVALID_HANDLE_VALUE) { + do { + if (info.cFileName[0] != '.') { + std::string fp = d + "\\" + info.cFileName; + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + remove_dir(fp); + } else { + ::SetFileAttributes(fp.c_str(), FILE_ATTRIBUTE_NORMAL); + ::DeleteFile(fp.c_str()); + } + } + } while (::FindNextFile(dh, &info) == TRUE); + } + ::FindClose(dh); + ::SetFileAttributes(d.c_str(), FILE_ATTRIBUTE_NORMAL); + ::RemoveDirectory(d.c_str()); +} +} // namespace +#else +namespace { +int remove_file(const char* fpath, const struct stat*, int, struct FTW*) { return unlink(fpath); } +} // namespace +#endif + +TmpDir::~TmpDir(void) { +#ifdef _WIN32 + remove_dir(_name); +#else + nftw(_name.c_str(), remove_file, 64, FTW_DEPTH | FTW_PHYS); + rmdir(_name.c_str()); +#endif +} + +std::vector parseCmdLine(const std::string& s) { + // Break the string up at whitespace, except inside quotes, but ignore escaped quotes + std::vector c; + size_t cur = 0; + size_t l = s.length(); + std::ostringstream oss; + bool inside_quote = false; + bool had_escape = false; + for (; cur < l; cur++) { + if (inside_quote) { + if (s[cur] == '"') { + if (had_escape) { + oss << "\""; + had_escape = false; + } else { + inside_quote = false; + } + } else if (s[cur] == '\\') { + had_escape = true; + } else { + if (had_escape) { + oss << "\\"; + had_escape = false; + } + oss << s[cur]; + } + } else { + if (s[cur] == ' ') { + if (had_escape) { + oss << " "; + had_escape = false; + } else { + c.push_back(oss.str()); + oss.str(std::string()); + } + } else if (s[cur] == '\\') { + if (had_escape) { + oss << "\\"; + had_escape = false; + } else { + had_escape = true; + } + } else if (s[cur] == '"') { + if (had_escape) { + oss << "\""; + had_escape = false; + } else { + inside_quote = true; + } + } else { + if (had_escape) { + switch (s[cur]) { + case 'a': + oss << "\a"; + break; + case 'b': + oss << "\b"; + break; + case 'f': + oss << "\f"; + break; + case 'n': + oss << "\n"; + break; + case 'r': + oss << "\r"; + break; + case 't': + oss << "\t"; + break; + case 'v': + oss << "\v"; + break; + default: + oss << "\\" << s[cur]; + break; + } + had_escape = false; + } else { + oss << s[cur]; + } + } + } + } + c.push_back(oss.str()); + return c; +} + +std::string combineCmdLine(const std::vector& cmd) { + std::ostringstream ret; + for (unsigned int i = 0; i < cmd.size(); i++) { + auto& c = cmd[i]; + ret << "\""; + for (size_t i = 0; i < c.size(); i++) { + switch (c[i]) { + case '\a': + ret << "\\a"; + break; + case '\b': + ret << "\\b"; + break; + case '\f': + ret << "\\f"; + break; + case '\n': + ret << "\\n"; + break; + case '\r': + ret << "\\r"; + break; + case '\t': + ret << "\\t"; + break; + case '\v': + ret << "\\v"; + break; + case '"': + ret << "\\\""; + break; + case '\\': + ret << "\\\\"; + break; + default: + ret << c[i]; + break; + } + } + ret << "\""; + if (i < cmd.size() - 1) { + ret << " "; + } + } + return ret.str(); +} + +void inflateString(std::string& s) { + unsigned char* cc = reinterpret_cast(&s[0]); + // autodetect compressed string + if (s.size() >= 2 && ((cc[0] == 0x1F && cc[1] == 0x8B) // gzip + || (cc[0] == 0x78 && (cc[1] == 0x01 // zlib + || cc[1] == 0x9C || cc[1] == 0xDA)))) { + const int BUF_SIZE = 1024; + unsigned char s_outbuf[BUF_SIZE]; + z_stream stream; + std::memset(&stream, 0, sizeof(stream)); + + unsigned char* dataStart; + int windowBits; + size_t dataLen; + if (cc[0] == 0x1F && cc[1] == 0x8B) { + dataStart = cc + 10; + windowBits = -Z_DEFAULT_WINDOW_BITS; + if (cc[3] & 0x4) { + dataStart += 2; + if (dataStart >= cc + s.size()) throw(-1); + } + if (cc[3] & 0x8) { + while (*dataStart != '\0') { + dataStart++; + if (dataStart >= cc + s.size()) throw(-1); + } + dataStart++; + if (dataStart >= cc + s.size()) throw(-1); + } + if (cc[3] & 0x10) { + while (*dataStart != '\0') { + dataStart++; + if (dataStart >= cc + s.size()) throw(-1); + } + dataStart++; + if (dataStart >= cc + s.size()) throw(-1); + } + if (cc[3] & 0x2) { + dataStart += 2; + if (dataStart >= cc + s.size()) throw(-1); + } + dataLen = s.size() - (dataStart - cc); + } else { + dataStart = cc; + windowBits = Z_DEFAULT_WINDOW_BITS; + dataLen = s.size(); + } + + stream.next_in = dataStart; + stream.avail_in = static_cast(dataLen); + stream.next_out = &s_outbuf[0]; + stream.avail_out = BUF_SIZE; + int status = inflateInit2(&stream, windowBits); + if (status != Z_OK) throw(status); + std::ostringstream oss; + while (true) { + status = inflate(&stream, Z_NO_FLUSH); + if (status == Z_STREAM_END || !stream.avail_out) { + // output buffer full or compression finished + oss << std::string(reinterpret_cast(s_outbuf), BUF_SIZE - stream.avail_out); + stream.next_out = &s_outbuf[0]; + stream.avail_out = BUF_SIZE; + } + if (status == Z_STREAM_END) break; + if (status != Z_OK) throw(status); + } + status = inflateEnd(&stream); + if (status != Z_OK) throw(status); + s = oss.str(); + } +} + +std::string deflateString(const std::string& s) { + mz_ulong compressedLength = compressBound(static_cast(s.size())); + unsigned char* cmpr = + static_cast(::malloc(compressedLength * sizeof(unsigned char))); + int status = compress(cmpr, &compressedLength, reinterpret_cast(&s[0]), + static_cast(s.size())); + if (status != Z_OK) { + ::free(cmpr); + throw(status); + } + std::string ret(reinterpret_cast(cmpr), compressedLength); + ::free(cmpr); + return ret; +} + +std::string encodeBase64(const std::string& s) { + base64::encoder E; + std::ostringstream oss; + oss << "@"; // add leading "@" to distinguish from valid MiniZinc code + std::istringstream iss(s); + E.encode(iss, oss); + return oss.str(); +} + +std::string decodeBase64(const std::string& s) { + if (s.size() == 0 || s[0] != '@') throw InternalError("string is not base64 encoded"); + base64::decoder D; + std::ostringstream oss; + std::istringstream iss(s); + (void)iss.get(); // remove leading "@" + D.decode(iss, oss); + return oss.str(); +} + +} // namespace FileUtils +} // namespace MiniZinc diff --git a/lib/flatten.cpp b/lib/flatten.cpp new file mode 100644 index 0000000..7a2660c --- /dev/null +++ b/lib/flatten.cpp @@ -0,0 +1,3748 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace MiniZinc { + +// Create domain constraints. Return true if successful. +bool createExplicitDomainConstraints(EnvI& envi, VarDecl* vd, Expression* domain) { + std::vector calls; + Location iloc = Location().introduce(); + + if (domain->type().isfloat() || domain->type().isfloatset()) { + FloatSetVal* fsv = eval_floatset(envi, domain); + if (fsv->size() == 1) { // Range based + if (fsv->min() == fsv->max()) { + calls.push_back( + new Call(iloc, constants().ids.float_.eq, {vd->id(), FloatLit::a(fsv->min())})); + } else { + FloatSetVal* cfsv; + if (vd->ti()->domain()) { + cfsv = eval_floatset(envi, vd->ti()->domain()); + } else { + cfsv = FloatSetVal::a(-FloatVal::infinity(), FloatVal::infinity()); + } + if (cfsv->min() < fsv->min()) { + calls.push_back( + new Call(iloc, constants().ids.float_.le, {FloatLit::a(fsv->min()), vd->id()})); + } + if (cfsv->max() > fsv->max()) { + calls.push_back( + new Call(iloc, constants().ids.float_.le, {vd->id(), FloatLit::a(fsv->max())})); + } + } + } else { + calls.push_back(new Call(iloc, constants().ids.set_in, {vd->id(), new SetLit(iloc, fsv)})); + } + } else if (domain->type().isint() || domain->type().isintset()) { + IntSetVal* isv = eval_intset(envi, domain); + if (isv->size() == 1) { // Range based + if (isv->min() == isv->max()) { + calls.push_back(new Call(iloc, constants().ids.int_.eq, {vd->id(), IntLit::a(isv->min())})); + } else { + IntSetVal* cisv; + if (vd->ti()->domain()) { + cisv = eval_intset(envi, vd->ti()->domain()); + } else { + cisv = IntSetVal::a(-IntVal::infinity(), IntVal::infinity()); + } + if (cisv->min() < isv->min()) { + calls.push_back( + new Call(iloc, constants().ids.int_.le, {IntLit::a(isv->min()), vd->id()})); + } + if (cisv->max() > isv->max()) { + calls.push_back( + new Call(iloc, constants().ids.int_.le, {vd->id(), IntLit::a(isv->max())})); + } + } + } else { + calls.push_back(new Call(iloc, constants().ids.set_in, {vd->id(), new SetLit(iloc, isv)})); + } + } else { + std::cerr << "Warning: domain change not handled by -g mode: " << *vd->id() << " = " << *domain + << std::endl; + return false; + } + + int counter = 0; + for (Call* c : calls) { + CallStackItem csi(envi, IntLit::a(counter++)); + c->ann().add(constants().ann.domain_change_constraint); + c->type(Type::varbool()); + c->decl(envi.model->matchFn(envi, c, true)); + flat_exp(envi, Ctx(), c, constants().var_true, constants().var_true); + } + return true; +} + +void setComputedDomain(EnvI& envi, VarDecl* vd, Expression* domain, bool is_computed) { + if (!envi.fopts.record_domain_changes || vd->ann().contains(constants().ann.is_defined_var) || + vd->introduced() || !createExplicitDomainConstraints(envi, vd, domain)) { + vd->ti()->domain(domain); + vd->ti()->setComputedDomain(is_computed); + } +} + +/// Output operator for contexts +template +std::basic_ostream& operator<<(std::basic_ostream& os, Ctx& ctx) { + switch (ctx.b) { + case C_ROOT: + os << "R"; + break; + case C_POS: + os << "+"; + break; + case C_NEG: + os << "-"; + break; + case C_MIX: + os << "M"; + break; + default: + assert(false); + break; + } + switch (ctx.i) { + case C_ROOT: + os << "R"; + break; + case C_POS: + os << "+"; + break; + case C_NEG: + os << "-"; + break; + case C_MIX: + os << "M"; + break; + default: + assert(false); + break; + } + if (ctx.neg) os << "!"; + return os; +} + +BCtx operator+(const BCtx& c) { + switch (c) { + case C_ROOT: + return C_POS; + case C_POS: + return C_POS; + case C_NEG: + return C_NEG; + case C_MIX: + return C_MIX; + default: + assert(false); + return C_ROOT; + } +} + +BCtx operator-(const BCtx& c) { + switch (c) { + case C_ROOT: + return C_NEG; + case C_POS: + return C_NEG; + case C_NEG: + return C_POS; + case C_MIX: + return C_MIX; + default: + assert(false); + return C_ROOT; + } +} + +/// Check if \a c is non-positive +bool nonpos(const BCtx& c) { return c == C_NEG || c == C_MIX; } +/// Check if \a c is non-negative +bool nonneg(const BCtx& c) { return c == C_ROOT || c == C_POS; } + +void dumpEEb(const std::vector& ee) { + for (unsigned int i = 0; i < ee.size(); i++) std::cerr << *ee[i].b() << "\n"; +} +void dumpEEr(const std::vector& ee) { + for (unsigned int i = 0; i < ee.size(); i++) std::cerr << *ee[i].r() << "\n"; +} + +std::tuple ann2Ctx(VarDecl* vd) { + if (vd->ann().contains(constants().ctx.root)) { + return std::make_tuple(C_ROOT, true); + } else if (vd->ann().contains(constants().ctx.mix)) { + return std::make_tuple(C_MIX, true); + } else if (vd->ann().contains(constants().ctx.pos)) { + return std::make_tuple(C_POS, true); + } else if (vd->ann().contains(constants().ctx.neg)) { + return std::make_tuple(C_NEG, true); + } else { + return std::make_tuple(C_MIX, false); + } +} + +void addCtxAnn(VarDecl* vd, BCtx& c) { + if (vd) { + Id* ctx_id = NULL; + BCtx nc; + bool annotated; + std::tie(nc, annotated) = ann2Ctx(vd); + // If previously annotated + if (annotated) { + // Early exit + if (nc == c || nc == C_ROOT || (nc == C_MIX && c != C_ROOT)) { + return; + } + // Remove old annotation + switch (nc) { + case C_ROOT: + vd->ann().remove(constants().ctx.root); + break; + case C_MIX: + vd->ann().remove(constants().ctx.mix); + break; + case C_POS: + vd->ann().remove(constants().ctx.pos); + break; + case C_NEG: + vd->ann().remove(constants().ctx.neg); + break; + default: + assert(false); + break; + } + // Determine new context + if (c == C_ROOT) { + nc = C_ROOT; + } else { + nc = C_MIX; + } + } else { + nc = c; + } + switch (nc) { + case C_ROOT: + ctx_id = constants().ctx.root; + break; + case C_POS: + ctx_id = constants().ctx.pos; + break; + case C_NEG: + ctx_id = constants().ctx.neg; + break; + case C_MIX: + ctx_id = constants().ctx.mix; + break; + default: + assert(false); + break; + } + vd->addAnnotation(ctx_id); + } +} + +void makeDefinedVar(VarDecl* vd, Call* c) { + if (!vd->ann().contains(constants().ann.is_defined_var)) { + std::vector args(1); + args[0] = vd->id(); + Call* dv = new Call(Location().introduce(), constants().ann.defines_var, args); + dv->type(Type::ann()); + vd->addAnnotation(constants().ann.is_defined_var); + c->addAnnotation(dv); + } +} + +bool isDefinesVarAnn(Expression* e) { + return e->isa() && e->cast()->id() == constants().ann.defines_var; +} + +/// Check if \a e is NULL or true +bool istrue(EnvI& env, Expression* e) { + GCLock lock; + return e == NULL || (e->type().ispar() && e->type().isbool() && eval_bool(env, e)); +} +/// Check if \a e is non-NULL and false +bool isfalse(EnvI& env, Expression* e) { + GCLock lock; + return e != NULL && e->type().ispar() && e->type().isbool() && !eval_bool(env, e); +} + +EE flat_exp(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +KeepAlive bind(EnvI& env, Ctx ctx, VarDecl* vd, Expression* e); + +/// Use bounds from ovd for vd if they are better. +/// Returns true if ovd's bounds are better. +bool updateBounds(EnvI& envi, VarDecl* ovd, VarDecl* vd) { + bool tighter = false; + bool fixed = false; + if (ovd->ti()->domain() || ovd->e()) { + IntVal intval; + FloatVal doubleval; + bool boolval; + + if (vd->type().isint()) { + IntBounds oldbounds = compute_int_bounds(envi, ovd->id()); + IntBounds bounds(0, 0, false); + if (vd->ti()->domain() || vd->e()) bounds = compute_int_bounds(envi, vd->id()); + + if ((vd->ti()->domain() || vd->e()) && bounds.valid && bounds.l.isFinite() && + bounds.u.isFinite()) { + if (oldbounds.valid && oldbounds.l.isFinite() && oldbounds.u.isFinite()) { + fixed = oldbounds.u == oldbounds.l || bounds.u == bounds.l; + if (fixed) { + tighter = true; + intval = oldbounds.u == oldbounds.l ? oldbounds.u : bounds.l; + ovd->ti()->domain(new SetLit(ovd->loc(), IntSetVal::a(intval, intval))); + } else { + IntSetVal* olddom = ovd->ti()->domain() ? eval_intset(envi, ovd->ti()->domain()) : NULL; + IntSetVal* newdom = vd->ti()->domain() ? eval_intset(envi, vd->ti()->domain()) : NULL; + + if (olddom) { + if (!newdom) { + tighter = true; + } else { + IntSetRanges oisr(olddom); + IntSetRanges nisr(newdom); + IntSetRanges nisr_card(newdom); + + Ranges::Inter inter(oisr, nisr); + + if (Ranges::cardinality(inter) < Ranges::cardinality(nisr_card)) { + IntSetRanges oisr_inter(olddom); + IntSetRanges nisr_inter(newdom); + Ranges::Inter inter_card(oisr_inter, + nisr_inter); + tighter = true; + ovd->ti()->domain(new SetLit(ovd->loc(), IntSetVal::ai(inter_card))); + } + } + } + } + } + } else { + if (oldbounds.valid && oldbounds.l.isFinite() && oldbounds.u.isFinite()) { + tighter = true; + fixed = oldbounds.u == oldbounds.l; + if (fixed) { + intval = oldbounds.u; + ovd->ti()->domain(new SetLit(ovd->loc(), IntSetVal::a(intval, intval))); + } + } + } + } else if (vd->type().isfloat()) { + FloatBounds oldbounds = compute_float_bounds(envi, ovd->id()); + FloatBounds bounds(0.0, 0.0, false); + if (vd->ti()->domain() || vd->e()) bounds = compute_float_bounds(envi, vd->id()); + if ((vd->ti()->domain() || vd->e()) && bounds.valid) { + if (oldbounds.valid) { + fixed = oldbounds.u == oldbounds.l || bounds.u == bounds.l; + if (fixed) doubleval = oldbounds.u == oldbounds.l ? oldbounds.u : bounds.l; + tighter = fixed || (oldbounds.u - oldbounds.l < bounds.u - bounds.l); + } + } else { + if (oldbounds.valid) { + tighter = true; + fixed = oldbounds.u == oldbounds.l; + if (fixed) doubleval = oldbounds.u; + } + } + } else if (vd->type().isbool()) { + if (ovd->ti()->domain()) { + fixed = tighter = true; + boolval = eval_bool(envi, ovd->ti()->domain()); + } else { + fixed = tighter = (ovd->e() && ovd->e()->isa()); + if (fixed) boolval = ovd->e()->cast()->v(); + } + } + + if (tighter) { + vd->ti()->domain(ovd->ti()->domain()); + if (vd->e() == NULL && fixed) { + if (vd->ti()->type().isvarint()) { + vd->type(Type::parint()); + vd->ti(new TypeInst(vd->loc(), Type::parint())); + vd->e(IntLit::a(intval)); + } else if (vd->ti()->type().isvarfloat()) { + vd->type(Type::parfloat()); + vd->ti(new TypeInst(vd->loc(), Type::parfloat())); + vd->e(FloatLit::a(doubleval)); + } else if (vd->ti()->type().isvarbool()) { + vd->type(Type::parbool()); + vd->ti(new TypeInst(vd->loc(), Type::parbool())); + vd->ti()->domain(boolval ? constants().lit_true : constants().lit_false); + vd->e(new BoolLit(vd->loc(), boolval)); + } + } + } + } + + return tighter; +} + +std::string getPath(EnvI& env) { + std::string path; + std::stringstream ss; + if (env.dumpPath(ss)) path = ss.str(); + + return path; +} + +inline Location getLoc(EnvI& env, Expression* e1, Expression* e2) { + if (e1) { + return e1->loc().introduce(); + } else if (e2) { + return e2->loc().introduce(); + } else { + return Location().introduce(); + } +} +inline Id* getId(EnvI& env, Id* origId) { + return origId ? origId : new Id(Location().introduce(), env.genId(), NULL); +} + +StringLit* getLongestMznPathAnnotation(EnvI& env, const Expression* e) { + StringLit* sl = NULL; + + if (const VarDecl* vd = e->dyn_cast()) { + EnvI::ReversePathMap& reversePathMap = env.getReversePathMap(); + KeepAlive vd_decl_ka(vd->id()->decl()); + EnvI::ReversePathMap::iterator it = reversePathMap.find(vd_decl_ka); + if (it != reversePathMap.end()) { + sl = new StringLit(Location(), it->second); + } + } else { + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) { + if (Call* ca = (*it)->dyn_cast()) { + if (ca->id() == constants().ann.mzn_path) { + StringLit* sl1 = ca->arg(0)->cast(); + if (sl) { + if (sl1->v().size() > sl->v().size()) sl = sl1; + } else { + sl = sl1; + } + } + } + } + } + return sl; +} + +void addPathAnnotation(EnvI& env, Expression* e) { + if (!(e->type().isann() || e->isa()) && e->type().dim() == 0) { + GCLock lock; + Annotation& ann = e->ann(); + if (ann.containsCall(constants().ann.mzn_path)) return; + + EnvI::ReversePathMap& reversePathMap = env.getReversePathMap(); + + std::vector path_args(1); + std::string p; + KeepAlive e_ka(e); + EnvI::ReversePathMap::iterator it = reversePathMap.find(e_ka); + if (it == reversePathMap.end()) { + p = getPath(env); + } else { + p = it->second; + } + + if (p.size() != 0) { + path_args[0] = new StringLit(Location(), p); + Call* path_call = new Call(e->loc(), constants().ann.mzn_path, path_args); + path_call->type(Type::ann()); + e->addAnnotation(path_call); + } + } +} + +VarDecl* newVarDecl(EnvI& env, Ctx ctx, TypeInst* ti, Id* origId, VarDecl* origVd, + Expression* rhs) { + VarDecl* vd = nullptr; + + // Is this vardecl already in the FlatZinc (for unification) + bool hasBeenAdded = false; + + // Don't use paths for arrays or annotations + if (ti->type().dim() == 0 && !ti->type().isann()) { + std::string path = getPath(env); + if (!path.empty()) { + EnvI::ReversePathMap& reversePathMap = env.getReversePathMap(); + EnvI::PathMap& pathMap = env.getPathMap(); + EnvI::PathMap::iterator it = pathMap.find(path); + + if (it != pathMap.end()) { + VarDecl* ovd = Expression::cast((it->second.decl)()); + unsigned int ovd_pass = it->second.pass_no; + + if (ovd) { + // If ovd was introduced during the same pass, we can unify + if (env.current_pass_no == ovd_pass) { + vd = ovd; + if (origId) origId->decl(vd); + hasBeenAdded = true; + } else { + vd = new VarDecl(getLoc(env, origVd, rhs), ti, getId(env, origId)); + hasBeenAdded = false; + updateBounds(env, ovd, vd); + } + + // Check whether ovd was unified in a previous pass + if (ovd->id() != ovd->id()->decl()->id()) { + // We may not have seen the pointed to decl just yet + KeepAlive ovd_decl_ka(ovd->id()->decl()); + EnvI::ReversePathMap::iterator path2It = reversePathMap.find(ovd_decl_ka); + if (path2It != reversePathMap.end()) { + std::string path2 = path2It->second; + EnvI::PathVar vd_tup{vd, env.current_pass_no}; + + pathMap[path] = vd_tup; + pathMap[path2] = vd_tup; + KeepAlive vd_ka(vd); + reversePathMap.insert(vd_ka, path); + } + } + } + } else { + // Create new VarDecl and add it to the maps + vd = new VarDecl(getLoc(env, origVd, rhs), ti, getId(env, origId)); + hasBeenAdded = false; + EnvI::PathVar vd_tup{vd, env.current_pass_no}; + pathMap[path] = vd_tup; + KeepAlive vd_ka(vd); + reversePathMap.insert(vd_ka, path); + } + } + } + if (vd == nullptr) { + vd = new VarDecl(getLoc(env, origVd, rhs), ti, getId(env, origId)); + hasBeenAdded = false; + } + + // If vd has an e() use bind to turn rhs into a constraint + if (vd->e()) { + if (rhs) { + bind(env, ctx, vd, rhs); + } + } else { + vd->e(rhs); + } + assert(!vd->type().isbot()); + if (origVd && (origVd->id()->idn() != -1 || origVd->toplevel())) { + vd->introduced(origVd->introduced()); + } else { + vd->introduced(true); + } + + vd->flat(vd); + + // Copy annotations from origVd + if (origVd) { + for (ExpressionSetIter it = origVd->ann().begin(); it != origVd->ann().end(); ++it) { + EE ee_ann = flat_exp(env, Ctx(), *it, NULL, constants().var_true); + vd->addAnnotation(ee_ann.r()); + } + } + + if (!hasBeenAdded) { + if (FunctionI* fi = env.model->matchRevMap(env, vd->type())) { + // We need to introduce a reverse mapper + Call* revmap = new Call(Location().introduce(), fi->id(), {vd->id()}); + revmap->decl(fi); + revmap->type(Type::varbool()); + env.flat_addItem(new ConstraintI(Location().introduce(), revmap)); + } + + VarDeclI* ni = new VarDeclI(Location().introduce(), vd); + env.flat_addItem(ni); + EE ee(vd, NULL); + env.cse_map_insert(vd->id(), ee); + } + + return vd; +} + +#define MZN_FILL_REIFY_MAP(T, ID) \ + reifyMap.insert( \ + std::pair(constants().ids.T.ID, constants().ids.T##reif.ID)); + +EnvI::EnvI(Model* model0, std::ostream& outstream0, std::ostream& errstream0) + : model(model0), + orig_model(NULL), + output(new Model), + outstream(outstream0), + errstream(errstream0), + current_pass_no(0), + final_pass_no(1), + maxPathDepth(0), + ignorePartial(false), + ignoreUnknownIds(false), + maxCallStack(0), + collect_vardecls(false), + in_redundant_constraint(0), + in_maybe_partial(0), + in_reverse_map_var(false), + n_reif_ct(0), + n_imp_ct(0), + n_imp_del(0), + n_lin_del(0), + pathUse(0), + _flat(new Model), + _failed(false), + ids(0) { + MZN_FILL_REIFY_MAP(int_, lin_eq); + MZN_FILL_REIFY_MAP(int_, lin_le); + MZN_FILL_REIFY_MAP(int_, lin_ne); + MZN_FILL_REIFY_MAP(int_, plus); + MZN_FILL_REIFY_MAP(int_, minus); + MZN_FILL_REIFY_MAP(int_, times); + MZN_FILL_REIFY_MAP(int_, div); + MZN_FILL_REIFY_MAP(int_, mod); + MZN_FILL_REIFY_MAP(int_, lt); + MZN_FILL_REIFY_MAP(int_, le); + MZN_FILL_REIFY_MAP(int_, gt); + MZN_FILL_REIFY_MAP(int_, ge); + MZN_FILL_REIFY_MAP(int_, eq); + MZN_FILL_REIFY_MAP(int_, ne); + MZN_FILL_REIFY_MAP(float_, lin_eq); + MZN_FILL_REIFY_MAP(float_, lin_le); + MZN_FILL_REIFY_MAP(float_, lin_lt); + MZN_FILL_REIFY_MAP(float_, lin_ne); + MZN_FILL_REIFY_MAP(float_, plus); + MZN_FILL_REIFY_MAP(float_, minus); + MZN_FILL_REIFY_MAP(float_, times); + MZN_FILL_REIFY_MAP(float_, div); + MZN_FILL_REIFY_MAP(float_, mod); + MZN_FILL_REIFY_MAP(float_, lt); + MZN_FILL_REIFY_MAP(float_, le); + MZN_FILL_REIFY_MAP(float_, gt); + MZN_FILL_REIFY_MAP(float_, ge); + MZN_FILL_REIFY_MAP(float_, eq); + MZN_FILL_REIFY_MAP(float_, ne); + reifyMap.insert( + std::pair(constants().ids.forall, constants().ids.forall_reif)); + reifyMap.insert( + std::pair(constants().ids.bool_eq, constants().ids.bool_eq_reif)); + reifyMap.insert(std::pair(constants().ids.bool_clause, + constants().ids.bool_clause_reif)); + reifyMap.insert( + std::pair(constants().ids.clause, constants().ids.bool_clause_reif)); +} +EnvI::~EnvI(void) { + delete _flat; + delete output; + delete model; + delete orig_model; +} +long long int EnvI::genId(void) { return ids++; } +void EnvI::cse_map_insert(Expression* e, const EE& ee) { + KeepAlive ka(e); + cse_map.insert(ka, WW(ee.r(), ee.b())); +} +EnvI::CSEMap::iterator EnvI::cse_map_find(Expression* e) { + KeepAlive ka(e); + CSEMap::iterator it = cse_map.find(ka); + if (it != cse_map.end()) { + if (it->second.r()) { + if (it->second.r()->isa()) { + int idx = vo.find(it->second.r()->cast()->decl()); + if (idx == -1 || (*_flat)[idx]->removed()) return cse_map.end(); + } + } else { + return cse_map.end(); + } + } + return it; +} +void EnvI::cse_map_remove(Expression* e) { + KeepAlive ka(e); + cse_map.remove(ka); +} +EnvI::CSEMap::iterator EnvI::cse_map_end(void) { return cse_map.end(); } +void EnvI::dump(void) { + struct EED { + static std::string k(Expression* e) { + std::ostringstream oss; + oss << *e; + return oss.str(); + } + static std::string d(const WW& ee) { + std::ostringstream oss; + oss << *ee.r() << " " << ee.b(); + return oss.str(); + } + }; + cse_map.dump(); +} + +void EnvI::flat_addItem(Item* i) { + assert(_flat); + if (_failed) return; + _flat->addItem(i); + + Expression* toAnnotate = NULL; + Expression* toAdd = NULL; + switch (i->iid()) { + case Item::II_VD: { + VarDeclI* vd = i->cast(); + addPathAnnotation(*this, vd->e()); + toAnnotate = vd->e()->e(); + vo.add_idx(vd, _flat->size() - 1); + toAdd = vd->e(); + break; + } + case Item::II_CON: { + ConstraintI* ci = i->cast(); + + if (ci->e()->isa() && !ci->e()->cast()->v()) { + fail(); + } else { + toAnnotate = ci->e(); + addPathAnnotation(*this, ci->e()); + toAdd = ci->e(); + } + break; + } + case Item::II_SOL: { + SolveI* si = i->cast(); + CollectOccurrencesE ce(vo, si); + topDown(ce, si->e()); + for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) topDown(ce, *it); + break; + } + case Item::II_OUT: { + OutputI* si = i->cast(); + toAdd = si->e(); + break; + } + default: + break; + } + if (toAnnotate && toAnnotate->isa()) { + annotateFromCallStack(toAnnotate); + } + if (toAdd) { + CollectOccurrencesE ce(vo, i); + topDown(ce, toAdd); + } +} + +void EnvI::annotateFromCallStack(Expression* e) { + int prev = idStack.size() > 0 ? idStack.back() : 0; + bool allCalls = true; + for (int i = static_cast(callStack.size()) - 1; i >= prev; i--) { + Expression* ee = callStack[i]->untag(); + allCalls = allCalls && (i == callStack.size() - 1 || ee->isa()); + for (ExpressionSetIter it = ee->ann().begin(); it != ee->ann().end(); ++it) { + EE ee_ann = flat_exp(*this, Ctx(), *it, NULL, constants().var_true); + if (allCalls || !isDefinesVarAnn(ee_ann.r())) e->addAnnotation(ee_ann.r()); + } + } +} + +void EnvI::copyPathMapsAndState(EnvI& env) { + final_pass_no = env.final_pass_no; + maxPathDepth = env.maxPathDepth; + current_pass_no = env.current_pass_no; + filenameMap = env.filenameMap; + maxPathDepth = env.maxPathDepth; + pathMap = env.getPathMap(); + reversePathMap = env.getReversePathMap(); +} + +void EnvI::flat_removeItem(MiniZinc::Item* i) { i->remove(); } +void EnvI::flat_removeItem(int i) { flat_removeItem((*_flat)[i]); } + +void EnvI::fail(const std::string& msg) { + if (!_failed) { + addWarning(std::string("model inconsistency detected") + + (msg.empty() ? std::string() : (": " + msg))); + _failed = true; + for (unsigned int i = 0; i < _flat->size(); i++) { + (*_flat)[i]->remove(); + } + ConstraintI* failedConstraint = new ConstraintI(Location().introduce(), constants().lit_false); + _flat->addItem(failedConstraint); + _flat->addItem(SolveI::sat(Location().introduce())); + for (unsigned int i = 0; i < output->size(); i++) { + (*output)[i]->remove(); + } + output->addItem( + new OutputI(Location().introduce(), new ArrayLit(Location(), std::vector()))); + throw ModelInconsistent(*this, Location().introduce()); + } +} + +bool EnvI::failed() const { return _failed; } + +unsigned int EnvI::registerEnum(VarDeclI* vdi) { + EnumMap::iterator it = enumMap.find(vdi); + unsigned int ret; + if (it == enumMap.end()) { + ret = static_cast(enumVarDecls.size()); + enumVarDecls.push_back(vdi); + enumMap.insert(std::make_pair(vdi, ret)); + } else { + ret = it->second; + } + return ret + 1; +} +VarDeclI* EnvI::getEnum(unsigned int i) const { + assert(i > 0 && i <= enumVarDecls.size()); + return enumVarDecls[i - 1]; +} +unsigned int EnvI::registerArrayEnum(const std::vector& arrayEnum) { + std::ostringstream oss; + for (unsigned int i = 0; i < arrayEnum.size(); i++) { + assert(arrayEnum[i] <= enumVarDecls.size()); + oss << arrayEnum[i] << "."; + } + ArrayEnumMap::iterator it = arrayEnumMap.find(oss.str()); + unsigned int ret; + if (it == arrayEnumMap.end()) { + ret = static_cast(arrayEnumDecls.size()); + arrayEnumDecls.push_back(arrayEnum); + arrayEnumMap.insert(std::make_pair(oss.str(), ret)); + } else { + ret = it->second; + } + return ret + 1; +} +const std::vector& EnvI::getArrayEnum(unsigned int i) const { + assert(i > 0 && i <= arrayEnumDecls.size()); + return arrayEnumDecls[i - 1]; +} +bool EnvI::isSubtype(const Type& t1, const Type& t2, bool strictEnums) { + if (!t1.isSubtypeOf(t2, strictEnums)) return false; + if (strictEnums && t1.dim() == 0 && t2.dim() != 0 && t2.enumId() != 0) { + // set assigned to an array + const std::vector& t2enumIds = getArrayEnum(t2.enumId()); + if (t2enumIds[t2enumIds.size() - 1] != 0 && t1.enumId() != t2enumIds[t2enumIds.size() - 1]) + return false; + } + if (strictEnums && t1.dim() > 0 && t1.enumId() != t2.enumId()) { + if (t1.enumId() == 0) { + return t1.isbot(); + } + if (t2.enumId() != 0) { + const std::vector& t1enumIds = getArrayEnum(t1.enumId()); + const std::vector& t2enumIds = getArrayEnum(t2.enumId()); + assert(t1enumIds.size() == t2enumIds.size()); + for (unsigned int i = 0; i < t1enumIds.size() - 1; i++) { + if (t2enumIds[i] != 0 && t1enumIds[i] != t2enumIds[i]) return false; + } + if (!t1.isbot() && t2enumIds[t1enumIds.size() - 1] != 0 && + t1enumIds[t1enumIds.size() - 1] != t2enumIds[t2enumIds.size() - 1]) + return false; + } + } + return true; +} + +void EnvI::collectVarDecls(bool b) { collect_vardecls = b; } +void EnvI::vo_add_exp(VarDecl* vd) { + if (vd->e() && vd->e()->isa() && !vd->e()->type().isann()) { + int prev = idStack.size() > 0 ? idStack.back() : 0; + for (int i = static_cast(callStack.size()) - 1; i >= prev; i--) { + Expression* ee = callStack[i]->untag(); + for (ExpressionSetIter it = ee->ann().begin(); it != ee->ann().end(); ++it) { + Expression* ann = *it; + if (ann != constants().ann.add_to_output && ann != constants().ann.rhs_from_assignment) { + bool needAnnotation = true; + if (Call* ann_c = ann->dyn_cast()) { + if (ann_c->id() == constants().ann.defines_var) { + // only add defines_var annotation if vd is the defined variable + if (Id* defined_var = ann_c->arg(0)->dyn_cast()) { + if (defined_var->decl() != vd->id()->decl()) { + needAnnotation = false; + } + } + } + } + if (needAnnotation) { + EE ee_ann = flat_exp(*this, Ctx(), *it, NULL, constants().var_true); + vd->e()->addAnnotation(ee_ann.r()); + } + } + } + } + } + int idx = vo.find(vd); + CollectOccurrencesE ce(vo, (*_flat)[idx]); + topDown(ce, vd->e()); + if (collect_vardecls) modifiedVarDecls.push_back(idx); +} +Model* EnvI::flat(void) { return _flat; } +void EnvI::swap() { + Model* tmp = model; + model = _flat; + _flat = tmp; +} +ASTString EnvI::reifyId(const ASTString& id) { + ASTStringMap::t::iterator it = reifyMap.find(id); + if (it == reifyMap.end()) { + return id.str() + "_reif"; + } else { + return it->second; + } +} +#undef MZN_FILL_REIFY_MAP +ASTString EnvI::halfReifyId(const ASTString& id) { return id.str() + "_imp"; } + +void EnvI::addWarning(const std::string& msg) { + if (warnings.size() > 20) return; + if (warnings.size() == 20) { + warnings.push_back("Further warnings have been suppressed.\n"); + } else { + std::ostringstream oss; + createErrorStack(); + dumpStack(oss, true); + warnings.push_back(msg + "\n" + oss.str()); + } +} + +void EnvI::createErrorStack(void) { + errorStack.clear(); + for (unsigned int i = static_cast(callStack.size()); i--;) { + Expression* e = callStack[i]->untag(); + bool isCompIter = callStack[i]->isTagged(); + KeepAlive ka(e); + errorStack.push_back(std::make_pair(ka, isCompIter)); + } +} + +Call* EnvI::surroundingCall(void) const { + if (callStack.size() >= 2) return callStack[callStack.size() - 2]->untag()->dyn_cast(); + return NULL; +} + +void EnvI::cleanupExceptOutput() { + cmap.clear(); + cse_map.clear(); + delete _flat; + delete model; + delete orig_model; + _flat = 0; + model = 0; +} + +CallStackItem::CallStackItem(EnvI& env0, Expression* e) : env(env0) { + if (e->isa()) env.idStack.push_back(static_cast(env.callStack.size())); + if (e->isa() && e->cast()->id() == "redundant_constraint") + env.in_redundant_constraint++; + if (e->ann().contains(constants().ann.maybe_partial)) env.in_maybe_partial++; + env.callStack.push_back(e); + env.maxCallStack = std::max(env.maxCallStack, static_cast(env.callStack.size())); +} +CallStackItem::CallStackItem(EnvI& env0, Id* ident, IntVal i) : env(env0) { + Expression* ee = ident->tag(); + env.callStack.push_back(ee); + env.maxCallStack = std::max(env.maxCallStack, static_cast(env.callStack.size())); +} +CallStackItem::~CallStackItem(void) { + Expression* e = env.callStack.back()->untag(); + if (e->isa()) env.idStack.pop_back(); + if (e->isa() && e->cast()->id() == "redundant_constraint") + env.in_redundant_constraint--; + if (e->ann().contains(constants().ann.maybe_partial)) env.in_maybe_partial--; + env.callStack.pop_back(); +} + +FlatteningError::FlatteningError(EnvI& env, const Location& loc, const std::string& msg) + : LocationException(env, loc, msg) {} + +Env::Env(Model* m, std::ostream& outstream, std::ostream& errstream) + : e(new EnvI(m, outstream, errstream)) {} +Env::~Env(void) { delete e; } + +Model* Env::model(void) { return e->model; } +void Env::model(Model* m) { e->model = m; } +Model* Env::flat(void) { return e->flat(); } +void Env::swap() { e->swap(); } +Model* Env::output(void) { return e->output; } + +std::ostream& Env::evalOutput(std::ostream& os) { return e->evalOutput(os); } +EnvI& Env::envi(void) { return *e; } +const EnvI& Env::envi(void) const { return *e; } +std::ostream& Env::dumpErrorStack(std::ostream& os) { return e->dumpStack(os, true); } + +bool EnvI::dumpPath(std::ostream& os, bool force) { + force = force ? force : fopts.collect_mzn_paths; + if (callStack.size() > maxPathDepth) { + if (!force && current_pass_no >= final_pass_no - 1) { + return false; + } + maxPathDepth = static_cast(callStack.size()); + } + + unsigned int lastError = static_cast(callStack.size()); + + std::string major_sep = ";"; + std::string minor_sep = "|"; + for (unsigned int i = 0; i < lastError; i++) { + Expression* e = callStack[i]->untag(); + bool isCompIter = callStack[i]->isTagged(); + Location loc = e->loc(); + int filenameId; + std::unordered_map::iterator findFilename = + filenameMap.find(loc.filename().str()); + if (findFilename == filenameMap.end()) { + if (!force && current_pass_no >= final_pass_no - 1) return false; + filenameId = static_cast(filenameMap.size()); + filenameMap.insert( + std::make_pair(loc.filename().str(), static_cast(filenameMap.size()))); + } else { + filenameId = findFilename->second; + } + + // If this call is not a dummy StringLit with empty Location (so that deferred compilation + // doesn't drop the paths) + if (e->eid() != Expression::E_STRINGLIT || loc.first_line() || loc.first_column() || + loc.last_line() || loc.last_column()) { + os << loc.filename().str() << minor_sep << loc.first_line() << minor_sep << loc.first_column() + << minor_sep << loc.last_line() << minor_sep << loc.last_column() << minor_sep; + switch (e->eid()) { + case Expression::E_INTLIT: + os << "il" << minor_sep << *e; + break; + case Expression::E_FLOATLIT: + os << "fl" << minor_sep << *e; + break; + case Expression::E_SETLIT: + os << "sl" << minor_sep << *e; + break; + case Expression::E_BOOLLIT: + os << "bl" << minor_sep << *e; + break; + case Expression::E_STRINGLIT: + os << "stl" << minor_sep << *e; + break; + case Expression::E_ID: + if (isCompIter) { + // if (e->cast()->decl()->e()->type().ispar()) + os << *e << "=" << *e->cast()->decl()->e(); + // else + // os << *e << "=?"; + } else { + os << "id" << minor_sep << *e; + } + break; + case Expression::E_ANON: + os << "anon"; + break; + case Expression::E_ARRAYLIT: + os << "al"; + break; + case Expression::E_ARRAYACCESS: + os << "aa"; + break; + case Expression::E_COMP: { + const Comprehension* cmp = e->cast(); + if (cmp->set()) + os << "sc"; + else + os << "ac"; + } break; + case Expression::E_ITE: + os << "ite"; + break; + case Expression::E_BINOP: + os << "bin" << minor_sep << e->cast()->opToString(); + break; + case Expression::E_UNOP: + os << "un" << minor_sep << e->cast()->opToString(); + break; + case Expression::E_CALL: + if (fopts.only_toplevel_paths) return false; + os << "ca" << minor_sep << e->cast()->id(); + break; + case Expression::E_VARDECL: + os << "vd"; + break; + case Expression::E_LET: + os << "l"; + break; + case Expression::E_TI: + os << "ti"; + break; + case Expression::E_TIID: + os << "ty"; + break; + default: + assert(false); + os << "unknown expression (internal error)"; + break; + } + os << major_sep; + } else { + os << e->cast()->v() << major_sep; + } + } + return true; +} + +std::ostream& EnvI::dumpStack(std::ostream& os, bool errStack) { + int lastError = 0; + + std::vector errStackCopy; + if (errStack) { + errStackCopy.resize(errorStack.size()); + for (unsigned int i = 0; i < errorStack.size(); i++) { + Expression* e = errorStack[i].first(); + if (errorStack[i].second) { + e = e->tag(); + } + errStackCopy[i] = e; + } + } + + std::vector& stack = errStack ? errStackCopy : callStack; + + for (; lastError < stack.size(); lastError++) { + Expression* e = stack[lastError]->untag(); + bool isCompIter = stack[lastError]->isTagged(); + if (e->loc().is_introduced()) continue; + if (!isCompIter && e->isa()) { + break; + } + } + + ASTString curloc_f; + int curloc_l = -1; + + for (int i = lastError - 1; i >= 0; i--) { + Expression* e = stack[i]->untag(); + bool isCompIter = stack[i]->isTagged(); + ASTString newloc_f = e->loc().filename(); + if (e->loc().is_introduced()) continue; + int newloc_l = e->loc().first_line(); + if (newloc_f != curloc_f || newloc_l != curloc_l) { + os << " " << newloc_f << ":" << newloc_l << ":" << std::endl; + curloc_f = newloc_f; + curloc_l = newloc_l; + } + if (isCompIter) + os << " with "; + else + os << " in "; + switch (e->eid()) { + case Expression::E_INTLIT: + os << "integer literal" << std::endl; + break; + case Expression::E_FLOATLIT: + os << "float literal" << std::endl; + break; + case Expression::E_SETLIT: + os << "set literal" << std::endl; + break; + case Expression::E_BOOLLIT: + os << "bool literal" << std::endl; + break; + case Expression::E_STRINGLIT: + os << "string literal" << std::endl; + break; + case Expression::E_ID: + if (isCompIter) { + if (e->cast()->decl()->e()->type().ispar()) + os << *e << " = " << *e->cast()->decl()->e() << std::endl; + else + os << *e << " = " << std::endl; + } else { + os << "identifier" << *e << std::endl; + } + break; + case Expression::E_ANON: + os << "anonymous variable" << std::endl; + break; + case Expression::E_ARRAYLIT: + os << "array literal" << std::endl; + break; + case Expression::E_ARRAYACCESS: + os << "array access" << std::endl; + break; + case Expression::E_COMP: { + const Comprehension* cmp = e->cast(); + if (cmp->set()) + os << "set "; + else + os << "array "; + os << "comprehension expression" << std::endl; + } break; + case Expression::E_ITE: + os << "if-then-else expression" << std::endl; + break; + case Expression::E_BINOP: + os << "binary " << e->cast()->opToString() << " operator expression" << std::endl; + break; + case Expression::E_UNOP: + os << "unary " << e->cast()->opToString() << " operator expression" << std::endl; + break; + case Expression::E_CALL: + os << "call '" << e->cast()->id() << "'" << std::endl; + break; + case Expression::E_VARDECL: { + GCLock lock; + os << "variable declaration for '" << e->cast()->id()->str() << "'" << std::endl; + } break; + case Expression::E_LET: + os << "let expression" << std::endl; + break; + case Expression::E_TI: + os << "type-inst expression" << std::endl; + break; + case Expression::E_TIID: + os << "type identifier" << std::endl; + break; + default: + assert(false); + os << "unknown expression (internal error)" << std::endl; + break; + } + } + return os; +} + +void populateOutput(Env& env) { + EnvI& envi = env.envi(); + Model* _flat = envi.flat(); + Model* _output = envi.output; + std::vector outputVars; + for (VarDeclIterator it = _flat->begin_vardecls(); it != _flat->end_vardecls(); ++it) { + VarDecl* vd = it->e(); + Annotation& ann = vd->ann(); + ArrayLit* dims = NULL; + bool has_output_ann = false; + if (!ann.isEmpty()) { + for (ExpressionSetIter ait = ann.begin(); ait != ann.end(); ++ait) { + if (Call* c = (*ait)->dyn_cast()) { + if (c->id() == constants().ann.output_array) { + dims = c->arg(0)->cast(); + has_output_ann = true; + break; + } + } else if ((*ait)->isa() && + (*ait)->cast()->str() == constants().ann.output_var->str()) { + has_output_ann = true; + } + } + if (has_output_ann) { + std::ostringstream s; + s << vd->id()->str().str() << " = "; + + VarDecl* vd_output = copy(env.envi(), vd)->cast(); + Type vd_t = vd_output->type(); + vd_t.ti(Type::TI_PAR); + vd_output->type(vd_t); + vd_output->ti()->type(vd_t); + _output->addItem(new VarDeclI(Location().introduce(), vd_output)); + + if (dims) { + s << "array" << dims->size() << "d("; + for (unsigned int i = 0; i < dims->size(); i++) { + IntSetVal* idxset = eval_intset(envi, (*dims)[i]); + s << *idxset << ","; + } + } + StringLit* sl = new StringLit(Location().introduce(), s.str()); + outputVars.push_back(sl); + + std::vector showArgs(1); + showArgs[0] = vd_output->id(); + Call* show = new Call(Location().introduce(), constants().ids.show, showArgs); + show->type(Type::parstring()); + FunctionI* fi = _flat->matchFn(envi, show, false); + assert(fi); + show->decl(fi); + outputVars.push_back(show); + std::string ends = dims ? ")" : ""; + ends += ";\n"; + StringLit* eol = new StringLit(Location().introduce(), ends); + outputVars.push_back(eol); + } + } + } + OutputI* newOutputItem = + new OutputI(Location().introduce(), new ArrayLit(Location().introduce(), outputVars)); + _output->addItem(newOutputItem); + envi.flat()->mergeStdLib(envi, _output); +} + +std::ostream& EnvI::evalOutput(std::ostream& os) { + GCLock lock; + + ArrayLit* al = eval_array_lit(*this, output->outputItem()->e()); + bool fLastEOL = true; + for (unsigned int i = 0; i < al->size(); i++) { + std::string s = eval_string(*this, (*al)[i]); + if (!s.empty()) { + os << s; + fLastEOL = ('\n' == s.back()); + } + } + if (!fLastEOL) os << '\n'; + return os; +} + +const std::vector& Env::warnings(void) { return envi().warnings; } + +void Env::clearWarnings(void) { envi().warnings.clear(); } + +unsigned int Env::maxCallStack(void) const { return envi().maxCallStack; } + +void checkIndexSets(EnvI& env, VarDecl* vd, Expression* e) { + ASTExprVec tis = vd->ti()->ranges(); + std::vector newtis(tis.size()); + bool needNewTypeInst = false; + GCLock lock; + switch (e->eid()) { + case Expression::E_ID: { + Id* id = e->cast(); + ASTExprVec e_tis = id->decl()->ti()->ranges(); + assert(tis.size() == e_tis.size()); + for (unsigned int i = 0; i < tis.size(); i++) { + if (tis[i]->domain() == NULL) { + newtis[i] = e_tis[i]; + needNewTypeInst = true; + } else { + if (!eval_intset(env, tis[i]->domain())->equal(eval_intset(env, e_tis[i]->domain()))) + throw EvalError(env, vd->loc(), "Index set mismatch"); + newtis[i] = tis[i]; + } + } + } break; + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + for (unsigned int i = 0; i < tis.size(); i++) { + if (tis[i]->domain() == NULL) { + newtis[i] = new TypeInst( + Location().introduce(), Type(), + new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i)))); + needNewTypeInst = true; + } else if (i == 0 || al->size() != 0) { + IntSetVal* isv = eval_intset(env, tis[i]->domain()); + assert(isv->size() <= 1); + if ((isv->size() == 0 && al->min(i) <= al->max(i)) || + (isv->size() != 0 && (isv->min(0) != al->min(i) || isv->max(0) != al->max(i)))) + throw EvalError(env, vd->loc(), "Index set mismatch"); + newtis[i] = tis[i]; + } + } + } break; + default: + throw InternalError("not supported yet"); + } + if (needNewTypeInst) { + TypeInst* tic = copy(env, vd->ti())->cast(); + tic->setRanges(newtis); + vd->ti(tic); + } +} + +/// Turn \a c into domain constraints if possible. +/// Return whether \a c is still required in the model. +bool checkDomainConstraints(EnvI& env, Call* c) { + if (env.fopts.record_domain_changes) return true; + if (c->id() == constants().ids.int_.le) { + Expression* e0 = c->arg(0); + Expression* e1 = c->arg(1); + if (e0->type().ispar() && e1->isa()) { + // greater than + Id* id = e1->cast(); + IntVal lb = eval_int(env, e0); + if (id->decl()->ti()->domain()) { + IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain()); + if (domain->min() >= lb) return false; + if (domain->max() < lb) { + env.fail(); + return false; + } + IntSetRanges dr(domain); + Ranges::Const cr(lb, IntVal::infinity()); + Ranges::Inter > i(dr, cr); + IntSetVal* newibv = IntSetVal::ai(i); + id->decl()->ti()->domain(new SetLit(Location().introduce(), newibv)); + id->decl()->ti()->setComputedDomain(false); + } else { + id->decl()->ti()->domain( + new SetLit(Location().introduce(), IntSetVal::a(lb, IntVal::infinity()))); + } + return false; + } else if (e1->type().ispar() && e0->isa()) { + // less than + Id* id = e0->cast(); + IntVal ub = eval_int(env, e1); + if (id->decl()->ti()->domain()) { + IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain()); + if (domain->max() <= ub) return false; + if (domain->min() > ub) { + env.fail(); + return false; + } + IntSetRanges dr(domain); + Ranges::Const cr(-IntVal::infinity(), ub); + Ranges::Inter > i(dr, cr); + IntSetVal* newibv = IntSetVal::ai(i); + id->decl()->ti()->domain(new SetLit(Location().introduce(), newibv)); + id->decl()->ti()->setComputedDomain(false); + } else { + id->decl()->ti()->domain( + new SetLit(Location().introduce(), IntSetVal::a(-IntVal::infinity(), ub))); + } + } + } else if (c->id() == constants().ids.int_.lin_le) { + ArrayLit* al_c = follow_id(c->arg(0))->cast(); + if (al_c->size() == 1) { + ArrayLit* al_x = follow_id(c->arg(1))->cast(); + IntVal coeff = eval_int(env, (*al_c)[0]); + IntVal y = eval_int(env, c->arg(2)); + IntVal lb = -IntVal::infinity(); + IntVal ub = IntVal::infinity(); + IntVal r = y % coeff; + if (coeff >= 0) { + ub = y / coeff; + if (r < 0) --ub; + } else { + lb = y / coeff; + if (r < 0) ++lb; + } + if (Id* id = (*al_x)[0]->dyn_cast()) { + if (id->decl()->ti()->domain()) { + IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain()); + if (domain->max() <= ub && domain->min() >= lb) return false; + if (domain->min() > ub || domain->max() < lb) { + env.fail(); + return false; + } + IntSetRanges dr(domain); + Ranges::Const cr(lb, ub); + Ranges::Inter > i(dr, cr); + IntSetVal* newibv = IntSetVal::ai(i); + id->decl()->ti()->domain(new SetLit(Location().introduce(), newibv)); + id->decl()->ti()->setComputedDomain(false); + } else { + id->decl()->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(lb, ub))); + } + return false; + } + } + } + return true; +} + +KeepAlive bind(EnvI& env, Ctx ctx, VarDecl* vd, Expression* e) { + assert(e == NULL || !e->isa()); + if (vd == constants().var_ignore) return e; + if (Id* ident = e->dyn_cast()) { + if (ident->decl()) { + VarDecl* e_vd = follow_id_to_decl(ident)->cast(); + e = e_vd->id(); + if (!env.in_reverse_map_var && ctx.b != C_ROOT && e->type() == Type::varbool()) { + addCtxAnn(e_vd, ctx.b); + if (e_vd != ident->decl()) { + addCtxAnn(ident->decl(), ctx.b); + } + } + } + } + if (ctx.neg) { + assert(e->type().bt() == Type::BT_BOOL); + if (vd == constants().var_true) { + if (!isfalse(env, e)) { + if (Id* id = e->dyn_cast()) { + while (id != NULL) { + assert(id->decl() != NULL); + if (id->decl()->ti()->domain() && istrue(env, id->decl()->ti()->domain())) { + GCLock lock; + env.flat_addItem(new ConstraintI(Location().introduce(), constants().lit_false)); + } else { + id->decl()->ti()->domain(constants().lit_false); + GCLock lock; + std::vector args(2); + args[0] = id; + args[1] = constants().lit_false; + Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args); + c->decl(env.model->matchFn(env, c, false)); + c->type(c->decl()->rtype(env, args, false)); + if (c->decl()->e()) { + flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + id = id->decl()->e() ? id->decl()->e()->dyn_cast() : NULL; + } + return constants().lit_true; + } else { + GC::lock(); + BinOp* bo = new BinOp(e->loc(), e, BOT_EQUIV, constants().lit_false); + bo->type(e->type()); + KeepAlive ka(bo); + GC::unlock(); + EE ee = flat_exp(env, Ctx(), bo, NULL, constants().var_true); + return bind(env, Ctx(), vd, ee.r()); + } + } + return constants().lit_true; + } else { + GC::lock(); + BinOp* bo = new BinOp(e->loc(), e, BOT_EQUIV, constants().lit_false); + bo->type(e->type()); + KeepAlive ka(bo); + GC::unlock(); + EE ee = flat_exp(env, Ctx(), bo, NULL, constants().var_true); + return bind(env, Ctx(), vd, ee.r()); + } + } else { + if (vd == constants().var_true) { + if (!istrue(env, e)) { + if (Id* id = e->dyn_cast()) { + assert(id->decl() != NULL); + while (id != NULL) { + if (id->decl()->ti()->domain() && isfalse(env, id->decl()->ti()->domain())) { + GCLock lock; + env.flat_addItem(new ConstraintI(Location().introduce(), constants().lit_false)); + } else if (id->decl()->ti()->domain() == NULL) { + id->decl()->ti()->domain(constants().lit_true); + GCLock lock; + std::vector args(2); + args[0] = id; + args[1] = constants().lit_true; + Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args); + c->decl(env.model->matchFn(env, c, false)); + c->type(c->decl()->rtype(env, args, false)); + if (c->decl()->e()) { + flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + id = id->decl()->e() ? id->decl()->e()->dyn_cast() : NULL; + } + } else { + GCLock lock; + // extract domain information from added constraint if possible + if (!e->isa() || checkDomainConstraints(env, e->cast())) { + env.flat_addItem(new ConstraintI(Location().introduce(), e)); + } + } + } + return constants().lit_true; + } else if (vd == constants().var_false) { + if (!isfalse(env, e)) { + throw InternalError("not supported yet"); + } + return constants().lit_true; + } else if (vd == NULL) { + if (e == NULL) return NULL; + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + case Expression::E_ID: + case Expression::E_TIID: + case Expression::E_SETLIT: + case Expression::E_VARDECL: + return e; + case Expression::E_BINOP: + case Expression::E_UNOP: + return e; /// TODO: should not happen once operators are evaluated + case Expression::E_ARRAYACCESS: + case Expression::E_COMP: + case Expression::E_ITE: + case Expression::E_LET: + case Expression::E_TI: + throw InternalError("unevaluated expression"); + case Expression::E_ARRAYLIT: { + GCLock lock; + ArrayLit* al = e->cast(); + /// TODO: review if limit of 10 is a sensible choice + if (al->type().bt() == Type::BT_ANN || al->size() <= 10) return e; + + EnvI::CSEMap::iterator it = env.cse_map_find(al); + if (it != env.cse_map_end()) { + return it->second.r()->cast()->id(); + } + + std::vector ranges(al->dims()); + for (unsigned int i = 0; i < ranges.size(); i++) { + ranges[i] = new TypeInst( + e->loc(), Type(), + new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i)))); + } + ASTExprVec ranges_v(ranges); + assert(!al->type().isbot()); + Expression* domain = NULL; + if (al->size() > 0 && (*al)[0]->type().isint()) { + IntVal min = IntVal::infinity(); + IntVal max = -IntVal::infinity(); + for (unsigned int i = 0; i < al->size(); i++) { + IntBounds ib = compute_int_bounds(env, (*al)[i]); + if (!ib.valid) { + min = -IntVal::infinity(); + max = IntVal::infinity(); + break; + } + min = std::min(min, ib.l); + max = std::max(max, ib.u); + } + if (min != -IntVal::infinity() && max != IntVal::infinity()) { + domain = new SetLit(Location().introduce(), IntSetVal::a(min, max)); + } + } + TypeInst* ti = new TypeInst(e->loc(), al->type(), ranges_v, domain); + if (domain) ti->setComputedDomain(true); + + VarDecl* vd = newVarDecl(env, ctx, ti, NULL, NULL, al); + EE ee(vd, NULL); + env.cse_map_insert(al, ee); + env.cse_map_insert(vd->e(), ee); + return vd->id(); + } + case Expression::E_CALL: { + if (e->type().isann()) return e; + GCLock lock; + /// TODO: handle array types + TypeInst* ti = new TypeInst(Location().introduce(), e->type()); + VarDecl* vd = newVarDecl(env, ctx, ti, NULL, NULL, e); + if (vd->e()->type().bt() == Type::BT_INT && vd->e()->type().dim() == 0) { + IntSetVal* ibv = NULL; + if (vd->e()->type().is_set()) { + ibv = compute_intset_bounds(env, vd->e()); + } else { + IntBounds ib = compute_int_bounds(env, vd->e()); + if (ib.valid) { + ibv = IntSetVal::a(ib.l, ib.u); + } + } + if (ibv) { + Id* id = vd->id(); + while (id != NULL) { + if (id->decl()->ti()->domain()) { + IntSetVal* domain = eval_intset(env, id->decl()->ti()->domain()); + IntSetRanges dr(domain); + IntSetRanges ibr(ibv); + Ranges::Inter i(dr, ibr); + IntSetVal* newibv = IntSetVal::ai(i); + if (ibv->card() == newibv->card()) { + id->decl()->ti()->setComputedDomain(true); + } else { + ibv = newibv; + } + } else { + id->decl()->ti()->setComputedDomain(true); + } + if (id->type().st() == Type::ST_PLAIN && ibv->size() == 0) { + env.fail(); + } else { + id->decl()->ti()->domain(new SetLit(Location().introduce(), ibv)); + } + id = id->decl()->e() ? id->decl()->e()->dyn_cast() : NULL; + } + } + } else if (vd->e()->type().isbool()) { + addCtxAnn(vd, ctx.b); + } else if (vd->e()->type().bt() == Type::BT_FLOAT && vd->e()->type().dim() == 0) { + FloatBounds fb = compute_float_bounds(env, vd->e()); + FloatSetVal* ibv = LinearTraits::intersect_domain(NULL, fb.l, fb.u); + if (fb.valid) { + Id* id = vd->id(); + while (id != NULL) { + if (id->decl()->ti()->domain()) { + FloatSetVal* domain = eval_floatset(env, id->decl()->ti()->domain()); + FloatSetVal* ndomain = + LinearTraits::intersect_domain(domain, fb.l, fb.u); + if (ibv && ndomain == domain) { + id->decl()->ti()->setComputedDomain(true); + } else { + ibv = ndomain; + } + } else { + id->decl()->ti()->setComputedDomain(true); + } + if (LinearTraits::domain_empty(ibv)) { + env.fail(); + } else { + id->decl()->ti()->domain(new SetLit(Location(), ibv)); + } + id = id->decl()->e() ? id->decl()->e()->dyn_cast() : NULL; + } + } + } + + return vd->id(); + } + default: + assert(false); + return NULL; + } + } else { + if (vd->e() == NULL) { + Expression* ret = e; + if (e == NULL || (e->type().ispar() && e->type().isbool())) { + GCLock lock; + bool isTrue = (e == NULL || eval_bool(env, e)); + + // Check if redefinition of bool_eq exists, if yes call it + std::vector args(2); + args[0] = vd->id(); + args[1] = constants().boollit(isTrue); + Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args); + c->decl(env.model->matchFn(env, c, false)); + c->type(c->decl()->rtype(env, args, false)); + bool didRewrite = false; + if (c->decl()->e()) { + flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + didRewrite = true; + } + + vd->e(constants().boollit(isTrue)); + if (vd->ti()->domain()) { + if (vd->ti()->domain() != vd->e()) { + env.fail(); + return vd->id(); + } + } else { + vd->ti()->domain(vd->e()); + vd->ti()->setComputedDomain(true); + } + if (didRewrite) { + return vd->id(); + } + } else { + if (e->type().dim() > 0) { + // Check that index sets match + env.errorStack.clear(); + checkIndexSets(env, vd, e); + if (vd->ti()->domain() && e->isa()) { + ArrayLit* al = e->cast(); + if (e->type().bt() == Type::BT_INT) { + IntSetVal* isv = eval_intset(env, vd->ti()->domain()); + for (unsigned int i = 0; i < al->size(); i++) { + if (Id* id = (*al)[i]->dyn_cast()) { + VarDecl* vdi = id->decl(); + if (vdi->ti()->domain() == NULL) { + vdi->ti()->domain(vd->ti()->domain()); + } else { + IntSetVal* vdi_dom = eval_intset(env, vdi->ti()->domain()); + IntSetRanges isvr(isv); + IntSetRanges vdi_domr(vdi_dom); + Ranges::Inter inter(isvr, vdi_domr); + IntSetVal* newdom = IntSetVal::ai(inter); + if (newdom->size() == 0) { + env.fail(); + } else { + IntSetRanges vdi_domr2(vdi_dom); + IntSetRanges newdomr(newdom); + if (!Ranges::equal(vdi_domr2, newdomr)) { + vdi->ti()->domain(new SetLit(Location().introduce(), newdom)); + vdi->ti()->setComputedDomain(false); + } + } + } + } else { + // at this point, can only be a constant + assert((*al)[i]->type().ispar()); + if (e->type().st() == Type::ST_PLAIN) { + IntVal iv = eval_int(env, (*al)[i]); + if (!isv->contains(iv)) { + std::ostringstream oss; + oss << "value " << iv << " outside declared array domain " << *isv; + env.fail(oss.str()); + } + } else { + IntSetVal* aisv = eval_intset(env, (*al)[i]); + IntSetRanges aisv_r(aisv); + IntSetRanges isv_r(isv); + if (!Ranges::subset(aisv_r, isv_r)) { + std::ostringstream oss; + oss << "value " << *aisv << " outside declared array domain " << *isv; + env.fail(oss.str()); + } + } + } + } + vd->ti()->setComputedDomain(true); + } else if (e->type().bt() == Type::BT_FLOAT) { + FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); + for (unsigned int i = 0; i < al->size(); i++) { + if (Id* id = (*al)[i]->dyn_cast()) { + VarDecl* vdi = id->decl(); + if (vdi->ti()->domain() == NULL) { + vdi->ti()->domain(vd->ti()->domain()); + } else { + FloatSetVal* vdi_dom = eval_floatset(env, vdi->ti()->domain()); + FloatSetRanges fsvr(fsv); + FloatSetRanges vdi_domr(vdi_dom); + Ranges::Inter inter(fsvr, vdi_domr); + FloatSetVal* newdom = FloatSetVal::ai(inter); + if (newdom->size() == 0) { + env.fail(); + } else { + FloatSetRanges vdi_domr2(vdi_dom); + FloatSetRanges newdomr(newdom); + if (!Ranges::equal(vdi_domr2, newdomr)) { + vdi->ti()->domain(new SetLit(Location().introduce(), newdom)); + vdi->ti()->setComputedDomain(false); + } + } + } + } else { + // at this point, can only be a constant + assert((*al)[i]->type().ispar()); + FloatVal fv = eval_float(env, (*al)[i]); + if (!fsv->contains(fv)) { + std::ostringstream oss; + oss << "value " << fv << " outside declared array domain " << *fsv; + env.fail(oss.str()); + } + } + } + vd->ti()->setComputedDomain(true); + } + } + } else if (Id* e_id = e->dyn_cast()) { + if (e_id == vd->id()) { + ret = vd->id(); + } else { + ASTString cid; + if (e->type().isint()) { + if (e->type().isopt()) { + cid = ASTString("int_opt_eq"); + } else { + cid = constants().ids.int_.eq; + } + } else if (e->type().isbool()) { + if (e->type().isopt()) { + cid = ASTString("bool_opt_eq"); + } else { + cid = constants().ids.bool_eq; + } + } else if (e->type().is_set()) { + cid = constants().ids.set_eq; + } else if (e->type().isfloat()) { + cid = constants().ids.float_.eq; + } + if (cid != "") { + GCLock lock; + std::vector args(2); + args[0] = vd->id(); + args[1] = e_id; + Call* c = new Call(Location().introduce(), cid, args); + c->decl(env.model->matchFn(env, c, false)); + c->type(c->decl()->rtype(env, args, false)); + if (c->type().isbool() && ctx.b != C_ROOT) { + addCtxAnn(vd, ctx.b); + addCtxAnn(e_id->decl(), ctx.b); + } + if (c->decl()->e()) { + flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + ret = vd->id(); + vd->e(e); + env.vo_add_exp(vd); + } + } + } + } + + if (ret != vd->id()) { + vd->e(ret); + addPathAnnotation(env, ret); + env.vo_add_exp(vd); + ret = vd->id(); + } + Id* vde_id = Expression::dyn_cast(vd->e()); + if (vde_id && vde_id->decl()->ti()->domain() == NULL) { + if (vd->ti()->domain()) { + GCLock lock; + Expression* vd_dom = eval_par(env, vd->ti()->domain()); + vde_id->decl()->ti()->domain(vd_dom); + } + } else if (vd->e() && vd->e()->type().bt() == Type::BT_INT && + vd->e()->type().dim() == 0) { + GCLock lock; + IntSetVal* ibv = NULL; + if (vd->e()->type().is_set()) { + ibv = compute_intset_bounds(env, vd->e()); + } else { + IntBounds ib = compute_int_bounds(env, vd->e()); + if (ib.valid) { + Call* call = vd->e()->dyn_cast(); + if (call && call->id() == constants().ids.lin_exp) { + ArrayLit* al = eval_array_lit(env, call->arg(1)); + if (al->size() == 1) { + IntBounds check_zeroone = compute_int_bounds(env, (*al)[0]); + if (check_zeroone.l == 0 && check_zeroone.u == 1) { + ArrayLit* coeffs = eval_array_lit(env, call->arg(0)); + std::vector newdom(2); + newdom[0] = 0; + newdom[1] = eval_int(env, (*coeffs)[0]) + eval_int(env, call->arg(2)); + ibv = IntSetVal::a(newdom); + } + } + } + if (ibv == NULL) { + ibv = IntSetVal::a(ib.l, ib.u); + } + } + } + if (ibv) { + if (vd->ti()->domain()) { + IntSetVal* domain = eval_intset(env, vd->ti()->domain()); + IntSetRanges dr(domain); + IntSetRanges ibr(ibv); + Ranges::Inter i(dr, ibr); + IntSetVal* newibv = IntSetVal::ai(i); + if (ibv->card() == newibv->card()) { + vd->ti()->setComputedDomain(true); + } else { + ibv = newibv; + } + } else { + vd->ti()->setComputedDomain(true); + } + SetLit* ibv_l = new SetLit(Location().introduce(), ibv); + vd->ti()->domain(ibv_l); + if (Id* rhs_ident = vd->e()->dyn_cast()) { + if (rhs_ident->decl()->ti()->domain()) { + IntSetVal* rhs_domain = eval_intset(env, rhs_ident->decl()->ti()->domain()); + IntSetRanges dr(rhs_domain); + IntSetRanges ibr(ibv); + Ranges::Inter i(dr, ibr); + IntSetVal* rhs_newibv = IntSetVal::ai(i); + if (rhs_domain->card() != rhs_newibv->card()) { + rhs_ident->decl()->ti()->domain(ibv_l); + rhs_ident->decl()->ti()->setComputedDomain(false); + if (rhs_ident->decl()->type().isopt()) { + std::vector args(2); + args[0] = rhs_ident; + args[1] = ibv_l; + Call* c = new Call(Location().introduce(), "var_dom", args); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + (void)flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + } + } + if (vd->type().isopt()) { + std::vector args(2); + args[0] = vd->id(); + args[1] = ibv_l; + Call* c = new Call(Location().introduce(), "var_dom", args); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + (void)flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + } else if (vd->e() && vd->e()->type().bt() == Type::BT_FLOAT && + vd->e()->type().dim() == 0) { + GCLock lock; + FloatSetVal* fbv = NULL; + FloatBounds fb = compute_float_bounds(env, vd->e()); + if (fb.valid) { + fbv = FloatSetVal::a(fb.l, fb.u); + } + if (fbv) { + if (vd->ti()->domain()) { + FloatSetVal* domain = eval_floatset(env, vd->ti()->domain()); + FloatSetRanges dr(domain); + FloatSetRanges fbr(fbv); + Ranges::Inter i(dr, fbr); + FloatSetVal* newfbv = FloatSetVal::ai(i); + + FloatSetRanges dr_eq(domain); + FloatSetRanges newfbv_eq(newfbv); + if (Ranges::equal(dr_eq, newfbv_eq)) { + vd->ti()->setComputedDomain(true); + } else { + fbv = newfbv; + } + } else { + vd->ti()->setComputedDomain(true); + } + SetLit* fbv_l = new SetLit(Location().introduce(), fbv); + vd->ti()->domain(fbv_l); + if (Id* rhs_ident = vd->e()->dyn_cast()) { + if (rhs_ident->decl()->ti()->domain()) { + FloatSetVal* rhs_domain = eval_floatset(env, rhs_ident->decl()->ti()->domain()); + FloatSetRanges dr(rhs_domain); + FloatSetRanges ibr(fbv); + Ranges::Inter i(dr, ibr); + FloatSetVal* rhs_newfbv = FloatSetVal::ai(i); + if (rhs_domain->card() != rhs_newfbv->card()) { + rhs_ident->decl()->ti()->domain(fbv_l); + rhs_ident->decl()->ti()->setComputedDomain(false); + if (rhs_ident->decl()->type().isopt()) { + std::vector args(2); + args[0] = rhs_ident; + args[1] = fbv_l; + Call* c = new Call(Location().introduce(), "var_dom", args); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + (void)flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + } + } + + if (vd->type().isopt()) { + std::vector args(2); + args[0] = vd->id(); + args[1] = fbv_l; + Call* c = new Call(Location().introduce(), "var_dom", args); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + (void)flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + } + } + return ret; + } else if (vd == e) { + return vd->id(); + } else if (vd->e() != e) { + e = follow_id_to_decl(e); + if (vd == e) return vd->id(); + switch (e->eid()) { + case Expression::E_BOOLLIT: { + Id* id = vd->id(); + while (id != NULL) { + if (id->decl()->ti()->domain() && + eval_bool(env, id->decl()->ti()->domain()) == e->cast()->v()) { + return constants().lit_true; + } else if (id->decl()->ti()->domain() && + eval_bool(env, id->decl()->ti()->domain()) != e->cast()->v()) { + GCLock lock; + env.flat_addItem(new ConstraintI(Location().introduce(), constants().lit_false)); + } else { + id->decl()->ti()->domain(e); + GCLock lock; + std::vector args(2); + args[0] = id; + args[1] = e; + Call* c = new Call(Location().introduce(), constants().ids.bool_eq, args); + c->decl(env.model->matchFn(env, c, false)); + c->type(c->decl()->rtype(env, args, false)); + if (c->decl()->e()) { + flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + } + } + id = id->decl()->e() ? id->decl()->e()->dyn_cast() : NULL; + } + return constants().lit_true; + } + case Expression::E_VARDECL: { + VarDecl* e_vd = e->cast(); + if (vd->e() == e_vd->id() || e_vd->e() == vd->id()) return vd->id(); + if (e->type().dim() != 0) throw InternalError("not supported yet"); + GCLock lock; + ASTString cid; + if (e->type().isint()) { + cid = constants().ids.int_.eq; + } else if (e->type().isbool()) { + cid = constants().ids.bool_eq; + } else if (e->type().is_set()) { + cid = constants().ids.set_eq; + } else if (e->type().isfloat()) { + cid = constants().ids.float_.eq; + } else { + throw InternalError("not yet implemented"); + } + std::vector args(2); + args[0] = vd->id(); + args[1] = e_vd->id(); + Call* c = new Call(vd->loc().introduce(), cid, args); + c->decl(env.model->matchFn(env, c, false)); + c->type(c->decl()->rtype(env, args, false)); + flat_exp(env, Ctx(), c, constants().var_true, constants().var_true); + return vd->id(); + } + case Expression::E_CALL: { + Call* c = e->cast(); + GCLock lock; + Call* nc; + std::vector args; + if (c->id() == constants().ids.lin_exp) { + ArrayLit* le_c = follow_id(c->arg(0))->cast(); + std::vector ncoeff(le_c->size()); + for (unsigned int i = static_cast(ncoeff.size()); i--;) + ncoeff[i] = (*le_c)[i]; + ncoeff.push_back(IntLit::a(-1)); + args.push_back(new ArrayLit(Location().introduce(), ncoeff)); + args[0]->type(le_c->type()); + ArrayLit* le_x = follow_id(c->arg(1))->cast(); + std::vector nx(le_x->size()); + for (unsigned int i = static_cast(nx.size()); i--;) nx[i] = (*le_x)[i]; + nx.push_back(vd->id()); + args.push_back(new ArrayLit(Location().introduce(), nx)); + args[1]->type(le_x->type()); + args.push_back(c->arg(2)); + nc = new Call(c->loc().introduce(), constants().ids.lin_exp, args); + nc->decl(env.model->matchFn(env, nc, false)); + if (nc->decl() == NULL) { + throw InternalError("undeclared function or predicate " + nc->id().str()); + } + nc->type(nc->decl()->rtype(env, args, false)); + BinOp* bop = new BinOp(nc->loc(), nc, BOT_EQ, IntLit::a(0)); + bop->type(Type::varbool()); + flat_exp(env, Ctx(), bop, constants().var_true, constants().var_true); + return vd->id(); + } else { + args.resize(c->n_args()); + for (unsigned int i = static_cast(args.size()); i--;) + args[i] = c->arg(i); + args.push_back(vd->id()); + ASTString nid = c->id(); + + if (c->id() == constants().ids.exists) { + nid = constants().ids.array_bool_or; + } else if (c->id() == constants().ids.forall) { + nid = constants().ids.array_bool_and; + } else if (vd->type().isbool()) { + if (env.fopts.enable_imp && vd->ann().contains(constants().ctx.pos)) { + nid = env.halfReifyId(c->id()); + if (env.model->matchFn(env, nid, args, false) == NULL) { + nid = env.reifyId(c->id()); + } + } else { + nid = env.reifyId(c->id()); + } + } + nc = new Call(c->loc().introduce(), nid, args); + } + nc->decl(env.model->matchFn(env, nc, false)); + if (nc->decl() == NULL) { + throw InternalError("undeclared function or predicate " + nc->id().str()); + } + nc->type(nc->decl()->rtype(env, args, false)); + makeDefinedVar(vd, nc); + flat_exp(env, Ctx(), nc, constants().var_true, constants().var_true); + return vd->id(); + } break; + default: + throw InternalError("not supported yet"); + } + } else { + return e; + } + } + } +} + +KeepAlive conj(EnvI& env, VarDecl* b, Ctx ctx, const std::vector& e) { + if (!ctx.neg) { + std::vector nontrue; + for (unsigned int i = 0; i < e.size(); i++) { + if (istrue(env, e[i].b())) continue; + if (isfalse(env, e[i].b())) { + return bind(env, Ctx(), b, constants().lit_false); + } + nontrue.push_back(e[i].b()); + } + if (nontrue.empty()) { + return bind(env, Ctx(), b, constants().lit_true); + } else if (nontrue.size() == 1) { + return bind(env, ctx, b, nontrue[0]); + } else { + if (b == constants().var_true) { + for (unsigned int i = 0; i < nontrue.size(); i++) bind(env, ctx, b, nontrue[i]); + return constants().lit_true; + } else { + GC::lock(); + std::vector args; + ArrayLit* al = new ArrayLit(Location().introduce(), nontrue); + al->type(Type::varbool(1)); + args.push_back(al); + Call* ret = new Call(nontrue[0]->loc().introduce(), constants().ids.forall, args); + ret->decl(env.model->matchFn(env, ret, false)); + ret->type(ret->decl()->rtype(env, args, false)); + KeepAlive ka(ret); + GC::unlock(); + return flat_exp(env, ctx, ret, b, constants().var_true).r; + } + } + } else { + Ctx nctx = ctx; + nctx.neg = false; + // negated + std::vector nonfalse; + for (unsigned int i = 0; i < e.size(); i++) { + if (istrue(env, e[i].b())) continue; + if (isfalse(env, e[i].b())) { + return bind(env, Ctx(), b, constants().lit_true); + } + nonfalse.push_back(e[i].b()); + } + if (nonfalse.empty()) { + return bind(env, Ctx(), b, constants().lit_false); + } else if (nonfalse.size() == 1) { + GC::lock(); + UnOp* uo = new UnOp(nonfalse[0]->loc(), UOT_NOT, nonfalse[0]); + uo->type(Type::varbool()); + KeepAlive ka(uo); + GC::unlock(); + return flat_exp(env, nctx, uo, b, constants().var_true).r; + } else { + if (b == constants().var_false) { + for (unsigned int i = 0; i < nonfalse.size(); i++) bind(env, nctx, b, nonfalse[i]); + return constants().lit_false; + } else { + GC::lock(); + std::vector args; + for (unsigned int i = 0; i < nonfalse.size(); i++) { + UnOp* uo = new UnOp(nonfalse[i]->loc(), UOT_NOT, nonfalse[i]); + uo->type(Type::varbool()); + nonfalse[i] = uo; + } + ArrayLit* al = new ArrayLit(Location().introduce(), nonfalse); + al->type(Type::varbool(1)); + args.push_back(al); + Call* ret = new Call(Location().introduce(), constants().ids.exists, args); + ret->decl(env.model->matchFn(env, ret, false)); + ret->type(ret->decl()->rtype(env, args, false)); + assert(ret->decl()); + KeepAlive ka(ret); + GC::unlock(); + return flat_exp(env, nctx, ret, b, constants().var_true).r; + } + } + } +} + +TypeInst* eval_typeinst(EnvI& env, VarDecl* vd) { + bool hasTiVars = vd->ti()->domain() && vd->ti()->domain()->isa(); + for (unsigned int i = 0; i < vd->ti()->ranges().size(); i++) { + hasTiVars = hasTiVars || + (vd->ti()->ranges()[i]->domain() && vd->ti()->ranges()[i]->domain()->isa()); + } + if (hasTiVars) { + assert(vd->e()); + if (vd->e()->type().dim() == 0) return new TypeInst(Location().introduce(), vd->e()->type()); + ArrayLit* al = eval_array_lit(env, vd->e()); + std::vector dims(al->dims()); + for (unsigned int i = 0; i < dims.size(); i++) { + dims[i] = + new TypeInst(Location().introduce(), Type(), + new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i)))); + } + return new TypeInst(Location().introduce(), vd->e()->type(), dims, + eval_par(env, vd->ti()->domain())); + } else { + std::vector dims(vd->ti()->ranges().size()); + for (unsigned int i = 0; i < vd->ti()->ranges().size(); i++) { + if (vd->ti()->ranges()[i]->domain()) { + IntSetVal* isv = eval_intset(env, vd->ti()->ranges()[i]->domain()); + if (isv->size() > 1) + throw EvalError(env, vd->ti()->ranges()[i]->domain()->loc(), + "array index set must be contiguous range"); + SetLit* sl = new SetLit(vd->ti()->ranges()[i]->loc(), isv); + sl->type(Type::parsetint()); + dims[i] = new TypeInst(vd->ti()->ranges()[i]->loc(), Type(), sl); + } else { + dims[i] = new TypeInst(vd->ti()->ranges()[i]->loc(), Type(), NULL); + } + } + Type t = (vd->e() && !vd->e()->type().isbot()) ? vd->e()->type() : vd->ti()->type(); + return new TypeInst(vd->ti()->loc(), t, dims, eval_par(env, vd->ti()->domain())); + } +} + +bool isBuiltin(FunctionI* decl) { + return (decl->loc().filename() == "builtins.mzn" || + decl->loc().filename().endsWith("/builtins.mzn") || + decl->loc().filename() == "stdlib.mzn" || + decl->loc().filename().endsWith("/stdlib.mzn") || + decl->loc().filename() == "flatzinc_builtins.mzn" || + decl->loc().filename().endsWith("/flatzinc_builtins.mzn")); +} + +KeepAlive flat_cv_exp(EnvI& env, Ctx ctx, Expression* e) { + GCLock lock; + if (e->type().ispar() && !e->type().cv()) { + return eval_par(env, e); + } + if (e->type().isvar()) { + EE ee = flat_exp(env, ctx, e, NULL, NULL); + if (isfalse(env, ee.b())) + throw FlatteningError(env, e->loc(), "cannot flatten partial function in this position"); + return ee.r(); + } + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_TIID: + case Expression::E_VARDECL: + case Expression::E_TI: + case Expression::E_ANON: + assert(false); + return NULL; + case Expression::E_ID: { + Id* id = e->cast(); + return flat_cv_exp(env, ctx, id->decl()->e()); + } + case Expression::E_SETLIT: { + SetLit* sl = e->cast(); + if (sl->isv() || sl->fsv()) return sl; + std::vector es(sl->v().size()); + GCLock lock; + for (unsigned int i = 0; i < sl->v().size(); i++) { + es[i] = flat_cv_exp(env, ctx, sl->v()[i])(); + } + SetLit* sl_ret = new SetLit(Location().introduce(), es); + Type t = sl->type(); + t.cv(false); + sl_ret->type(t); + return eval_par(env, sl_ret); + } + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + std::vector es(al->size()); + GCLock lock; + for (unsigned int i = 0; i < al->size(); i++) { + es[i] = flat_cv_exp(env, ctx, (*al)[i])(); + } + std::vector > dims(al->dims()); + for (int i = 0; i < al->dims(); i++) { + dims[i] = std::make_pair(al->min(i), al->max(i)); + } + Expression* al_ret = eval_par(env, new ArrayLit(Location().introduce(), es, dims)); + Type t = al->type(); + t.cv(false); + al_ret->type(t); + return al_ret; + } + case Expression::E_ARRAYACCESS: { + ArrayAccess* aa = e->cast(); + GCLock lock; + Expression* av = flat_cv_exp(env, ctx, aa->v())(); + std::vector idx(aa->idx().size()); + for (unsigned int i = 0; i < aa->idx().size(); i++) { + idx[i] = flat_cv_exp(env, ctx, aa->idx()[i])(); + } + ArrayAccess* aa_ret = new ArrayAccess(Location().introduce(), av, idx); + Type t = aa->type(); + t.cv(false); + aa_ret->type(t); + return eval_par(env, aa_ret); + } + case Expression::E_COMP: { + Comprehension* c = e->cast(); + GCLock lock; + class EvalFlatCvExp { + public: + Ctx ctx; + EvalFlatCvExp(Ctx& ctx0) : ctx(ctx0) {} + typedef Expression* ArrayVal; + Expression* e(EnvI& env, Expression* e) { return flat_cv_exp(env, ctx, e)(); } + static Expression* exp(Expression* e) { return e; } + Expression* flatten(EnvI& env, Expression* e0) { + return flat_exp(env, Ctx(), e0, NULL, constants().var_true).r(); + } + + } eval(ctx); + std::vector a = eval_comp(env, eval, c); + + Type t = Type::bot(); + bool allPar = true; + for (unsigned int i = 0; i < a.size(); i++) { + if (t == Type::bot()) t = a[i]->type(); + if (!a[i]->type().ispar()) allPar = false; + } + if (!allPar) t.ti(Type::TI_VAR); + if (c->set()) + t.st(Type::ST_SET); + else + t.dim(c->type().dim()); + t.cv(false); + if (c->set()) { + if (c->type().ispar() && allPar) { + SetLit* sl = new SetLit(c->loc().introduce(), a); + sl->type(t); + Expression* slr = eval_par(env, sl); + slr->type(t); + return slr; + } else { + throw InternalError("var set comprehensions not supported yet"); + } + } else { + ArrayLit* alr = new ArrayLit(Location().introduce(), a); + alr->type(t); + alr->flat(true); + return alr; + } + } + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + KeepAlive ka = flat_cv_exp(env, ctx, ite->e_if(i)); + if (eval_bool(env, ka())) return flat_cv_exp(env, ctx, ite->e_then(i)); + } + return flat_cv_exp(env, ctx, ite->e_else()); + } + case Expression::E_BINOP: { + BinOp* bo = e->cast(); + if (bo->op() == BOT_AND) { + GCLock lock; + Expression* lhs = flat_cv_exp(env, ctx, bo->lhs())(); + if (!eval_bool(env, lhs)) { + return constants().lit_false; + } + return eval_par(env, flat_cv_exp(env, ctx, bo->rhs())()); + } else if (bo->op() == BOT_OR) { + GCLock lock; + Expression* lhs = flat_cv_exp(env, ctx, bo->lhs())(); + if (eval_bool(env, lhs)) { + return constants().lit_true; + } + return eval_par(env, flat_cv_exp(env, ctx, bo->rhs())()); + } + GCLock lock; + BinOp* nbo = new BinOp(bo->loc().introduce(), flat_cv_exp(env, ctx, bo->lhs())(), bo->op(), + flat_cv_exp(env, ctx, bo->rhs())()); + nbo->type(bo->type()); + nbo->decl(bo->decl()); + return eval_par(env, nbo); + } + case Expression::E_UNOP: { + UnOp* uo = e->cast(); + GCLock lock; + UnOp* nuo = new UnOp(uo->loc(), uo->op(), flat_cv_exp(env, ctx, uo->e())()); + nuo->type(uo->type()); + return eval_par(env, nuo); + } + case Expression::E_CALL: { + Call* c = e->cast(); + if (c->id() == "mzn_in_root_context") { + return constants().boollit(ctx.b == C_ROOT); + } + if (ctx.b == C_ROOT && c->decl()->e() && c->decl()->e()->isa()) { + bool allBool = true; + for (unsigned int i = 0; i < c->n_args(); i++) { + if (c->arg(i)->type().bt() != Type::BT_BOOL) { + allBool = false; + break; + } + } + if (allBool) { + return c->decl()->e(); + } + } + std::vector args(c->n_args()); + GCLock lock; + for (unsigned int i = 0; i < c->n_args(); i++) { + args[i] = flat_cv_exp(env, ctx, c->arg(i))(); + } + Call* nc = new Call(c->loc(), c->id(), args); + nc->decl(c->decl()); + nc->type(c->type()); + return eval_par(env, nc); + } + case Expression::E_LET: { + Let* l = e->cast(); + l->pushbindings(); + KeepAlive ret = flat_cv_exp(env, ctx, l->in()); + l->popbindings(); + return ret; + } + } + throw InternalError("internal error"); +} + +void flatten(Env& e, FlatteningOptions opt) { + try { + EnvI& env = e.envi(); + env.fopts = opt; + + bool onlyRangeDomains = false; + if (opt.onlyRangeDomains) { + onlyRangeDomains = true; // compulsory + } else { + GCLock lock; + Call* check_only_range = + new Call(Location(), "mzn_check_only_range_domains", std::vector()); + check_only_range->type(Type::parbool()); + check_only_range->decl(env.model->matchFn(e.envi(), check_only_range, false)); + onlyRangeDomains = eval_bool(e.envi(), check_only_range); + } + + bool hadSolveItem = false; + // Flatten main model + class FV : public ItemVisitor { + public: + EnvI& env; + bool& hadSolveItem; + FV(EnvI& env0, bool& hadSolveItem0) : env(env0), hadSolveItem(hadSolveItem0) {} + bool enter(Item* i) { return !(i->isa() && env.failed()); } + void vVarDeclI(VarDeclI* v) { + v->e()->ann().remove(constants().ann.output_var); + v->e()->ann().removeCall(constants().ann.output_array); + if (v->e()->ann().contains(constants().ann.output_only)) return; + if (v->e()->type().ispar() && !v->e()->type().isopt() && !v->e()->type().cv() && + v->e()->type().dim() > 0 && v->e()->ti()->domain() == NULL && + (v->e()->type().bt() == Type::BT_INT || v->e()->type().bt() == Type::BT_FLOAT)) { + // Compute bounds for array literals + CallStackItem csi(env, v->e()); + GCLock lock; + ArrayLit* al = eval_array_lit(env, v->e()->e()); + v->e()->e(al); + checkIndexSets(env, v->e(), v->e()->e()); + if (v->e()->type().bt() == Type::BT_INT && v->e()->type().st() == Type::ST_PLAIN) { + IntVal lb = IntVal::infinity(); + IntVal ub = -IntVal::infinity(); + for (unsigned int i = 0; i < al->size(); i++) { + IntVal vi = eval_int(env, (*al)[i]); + lb = std::min(lb, vi); + ub = std::max(ub, vi); + } + GCLock lock; + // v->e()->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(lb, ub))); + // v->e()->ti()->setComputedDomain(true); + setComputedDomain(env, v->e(), new SetLit(Location().introduce(), IntSetVal::a(lb, ub)), + true); + } else if (v->e()->type().bt() == Type::BT_FLOAT && + v->e()->type().st() == Type::ST_PLAIN) { + FloatVal lb = FloatVal::infinity(); + FloatVal ub = -FloatVal::infinity(); + for (unsigned int i = 0; i < al->size(); i++) { + FloatVal vi = eval_float(env, (*al)[i]); + lb = std::min(lb, vi); + ub = std::max(ub, vi); + } + GCLock lock; + // v->e()->ti()->domain(new SetLit(Location().introduce(), FloatSetVal::a(lb, ub))); + // v->e()->ti()->setComputedDomain(true); + setComputedDomain(env, v->e(), + new SetLit(Location().introduce(), FloatSetVal::a(lb, ub)), true); + } + } else if (v->e()->type().isvar() || v->e()->type().isann()) { + (void)flatten_id(env, Ctx(), v->e()->id(), NULL, constants().var_true, true); + } else { + if (v->e()->e() == NULL) { + if (!v->e()->type().isann()) + throw EvalError(env, v->e()->loc(), "Undefined parameter", v->e()->id()->v()); + } else { + CallStackItem csi(env, v->e()); + GCLock lock; + Location v_loc = v->e()->e()->loc(); + if (!v->e()->e()->type().cv()) { + v->e()->e(eval_par(env, v->e()->e())); + } else { + EE ee = flat_exp(env, Ctx(), v->e()->e(), NULL, constants().var_true); + v->e()->e(ee.r()); + } + if (v->e()->type().dim() > 0) { + checkIndexSets(env, v->e(), v->e()->e()); + if (v->e()->ti()->domain() != NULL) { + ArrayLit* al = eval_array_lit(env, v->e()->e()); + for (unsigned int i = 0; i < al->size(); i++) { + if (!checkParDomain(env, (*al)[i], v->e()->ti()->domain())) { + throw EvalError(env, v_loc, "parameter value out of range"); + } + } + } + } else { + if (v->e()->ti()->domain() != NULL) { + if (!checkParDomain(env, v->e()->e(), v->e()->ti()->domain())) { + throw EvalError(env, v_loc, "parameter value out of range"); + } + } + } + } + } + } + void vConstraintI(ConstraintI* ci) { + (void)flat_exp(env, Ctx(), ci->e(), constants().var_true, constants().var_true); + } + void vSolveI(SolveI* si) { + if (hadSolveItem) throw FlatteningError(env, si->loc(), "Only one solve item allowed"); + hadSolveItem = true; + GCLock lock; + SolveI* nsi = NULL; + switch (si->st()) { + case SolveI::ST_SAT: + nsi = SolveI::sat(Location()); + break; + case SolveI::ST_MIN: { + Ctx ctx; + ctx.i = C_NEG; + nsi = SolveI::min(Location().introduce(), + flat_exp(env, ctx, si->e(), NULL, constants().var_true).r()); + } break; + case SolveI::ST_MAX: { + Ctx ctx; + ctx.i = C_POS; + nsi = SolveI::max(Location().introduce(), + flat_exp(env, Ctx(), si->e(), NULL, constants().var_true).r()); + } break; + } + for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) { + nsi->ann().add(flat_exp(env, Ctx(), *it, NULL, constants().var_true).r()); + } + env.flat_addItem(nsi); + } + } _fv(env, hadSolveItem); + iterItems(_fv, e.model()); + + if (!hadSolveItem) { + GCLock lock; + e.envi().flat_addItem(SolveI::sat(Location().introduce())); + } + + std::vector deletedVarDecls; + + // Create output model + if (opt.keepOutputInFzn) { + copyOutput(env); + } else { + createOutput(env, deletedVarDecls, opt.outputMode, opt.outputObjective, opt.outputOutputItem); + } + + // Flatten remaining redefinitions + Model& m = *e.flat(); + int startItem = 0; + int endItem = m.size() - 1; + + FunctionI* int_lin_eq; + { + std::vector int_lin_eq_t(3); + int_lin_eq_t[0] = Type::parint(1); + int_lin_eq_t[1] = Type::varint(1); + int_lin_eq_t[2] = Type::parint(0); + GCLock lock; + FunctionI* fi = env.model->matchFn(env, constants().ids.int_.lin_eq, int_lin_eq_t, false); + int_lin_eq = (fi && fi->e()) ? fi : NULL; + } + FunctionI* array_bool_and; + FunctionI* array_bool_or; + FunctionI* array_bool_clause; + FunctionI* array_bool_clause_reif; + FunctionI* bool_xor; + { + std::vector array_bool_andor_t(2); + array_bool_andor_t[0] = Type::varbool(1); + array_bool_andor_t[1] = Type::varbool(0); + GCLock lock; + FunctionI* fi = + env.model->matchFn(env, ASTString("array_bool_and"), array_bool_andor_t, false); + array_bool_and = (fi && fi->e()) ? fi : NULL; + fi = env.model->matchFn(env, ASTString("array_bool_or"), array_bool_andor_t, false); + array_bool_or = (fi && fi->e()) ? fi : NULL; + + array_bool_andor_t[1] = Type::varbool(1); + fi = env.model->matchFn(env, ASTString("bool_clause"), array_bool_andor_t, false); + array_bool_clause = (fi && fi->e()) ? fi : NULL; + + array_bool_andor_t.push_back(Type::varbool()); + fi = env.model->matchFn(env, ASTString("bool_clause_reif"), array_bool_andor_t, false); + array_bool_clause_reif = (fi && fi->e()) ? fi : NULL; + + std::vector bool_xor_t(3); + bool_xor_t[0] = Type::varbool(); + bool_xor_t[1] = Type::varbool(); + bool_xor_t[2] = Type::varbool(); + fi = env.model->matchFn(env, constants().ids.bool_xor, bool_xor_t, false); + bool_xor = (fi && fi->e()) ? fi : NULL; + } + + std::vector removedItems; + env.collectVarDecls(true); + + while (startItem <= endItem || !env.modifiedVarDecls.empty()) { + if (env.failed()) return; + std::vector agenda; + for (int i = startItem; i <= endItem; i++) { + agenda.push_back(i); + } + for (unsigned int i = 0; i < env.modifiedVarDecls.size(); i++) { + agenda.push_back(env.modifiedVarDecls[i]); + } + env.modifiedVarDecls.clear(); + + for (int ai = 0; ai < agenda.size(); ai++) { + int i = agenda[ai]; + VarDeclI* vdi = m[i]->dyn_cast(); + bool keptVariable = true; + /// Look at constraints + if (vdi != NULL && !isOutput(vdi->e())) { + if (0 < env.vo.occurrences(vdi->e())) { + const auto it = env.vo._m.find(vdi->e()->id()); + if (env.vo._m.end() != it) { + bool hasRedundantOccurrenciesOnly = true; + for (const auto& c : it->second) { + if (auto constrI = c->dyn_cast()) + if (auto call = constrI->e()->dyn_cast()) + if (call->id() == "mzn_reverse_map_var") continue; // all good + hasRedundantOccurrenciesOnly = false; + break; + } + if (hasRedundantOccurrenciesOnly) { + removedItems.push_back(vdi); + env.flat_removeItem(vdi); + env.vo.removeAllOccurrences(vdi->e()); + keptVariable = false; + for (const auto& c : it->second) { + env.flat_removeItem(c); + } + } + } + } else { // 0 occurrencies + if (vdi->e()->e() && vdi->e()->ti()->domain()) { + if (vdi->e()->type().isvar() && vdi->e()->type().isbool() && + !vdi->e()->type().isopt() && + Expression::equal(vdi->e()->ti()->domain(), constants().lit_true)) { + GCLock lock; + ConstraintI* ci = new ConstraintI(vdi->loc(), vdi->e()->e()); + if (vdi->e()->introduced()) { + removedItems.push_back(vdi); + env.flat_removeItem(vdi); + keptVariable = false; + } else { + vdi->e()->e(NULL); + } + env.flat_addItem(ci); + } else if (vdi->e()->type().ispar() || vdi->e()->ti()->computedDomain()) { + removedItems.push_back(vdi); + keptVariable = false; + } + } else { + removedItems.push_back(vdi); + env.flat_removeItem(vdi); + keptVariable = false; + } + } + } + if (vdi && keptVariable && vdi->e()->type().dim() > 0 && vdi->e()->type().isvar()) { + vdi->e()->ti()->domain(NULL); + } + if (vdi && keptVariable && vdi->e()->type().isint() && vdi->e()->type().isvar() && + vdi->e()->ti()->domain() != NULL) { + GCLock lock; + IntSetVal* dom = eval_intset(env, vdi->e()->ti()->domain()); + + bool needRangeDomain = onlyRangeDomains; + if (!needRangeDomain && dom->size() > 0) { + if (dom->min(0).isMinusInfinity() || dom->max(dom->size() - 1).isPlusInfinity()) + needRangeDomain = true; + } + if (needRangeDomain) { + if (dom->min(0).isMinusInfinity() || dom->max(dom->size() - 1).isPlusInfinity()) { + TypeInst* nti = copy(env, vdi->e()->ti())->cast(); + nti->domain(NULL); + vdi->e()->ti(nti); + if (dom->min(0).isFinite()) { + std::vector args(2); + args[0] = IntLit::a(dom->min(0)); + args[1] = vdi->e()->id(); + Call* call = new Call(Location().introduce(), constants().ids.int_.le, args); + call->type(Type::varbool()); + call->decl(env.model->matchFn(env, call, false)); + env.flat_addItem(new ConstraintI(Location().introduce(), call)); + } else if (dom->max(dom->size() - 1).isFinite()) { + std::vector args(2); + args[0] = vdi->e()->id(); + args[1] = IntLit::a(dom->max(dom->size() - 1)); + Call* call = new Call(Location().introduce(), constants().ids.int_.le, args); + call->type(Type::varbool()); + call->decl(env.model->matchFn(env, call, false)); + env.flat_addItem(new ConstraintI(Location().introduce(), call)); + } + } else if (dom->size() > 1) { + SetLit* newDom = new SetLit(Location().introduce(), + IntSetVal::a(dom->min(0), dom->max(dom->size() - 1))); + TypeInst* nti = copy(env, vdi->e()->ti())->cast(); + nti->domain(newDom); + vdi->e()->ti(nti); /// TODO: WHY WAS THIS COMMENTED OUT IN DEBUG??? + } + if (dom->size() > 1) { + IntVal firstHole = dom->max(0) + 1; + IntSetRanges domr(dom); + ++domr; + for (; domr(); ++domr) { + for (IntVal i = firstHole; i < domr.min(); i++) { + std::vector args(2); + args[0] = vdi->e()->id(); + args[1] = IntLit::a(i); + Call* call = new Call(vdi->e()->loc(), constants().ids.int_.ne, args); + call->type(Type::varbool()); + call->decl(env.model->matchFn(env, call, false)); + // Give distinct call stacks for each int_ne added + CallStackItem csi(env, IntLit::a(i)); + env.flat_addItem(new ConstraintI(Location().introduce(), call)); + firstHole = domr.max().plus(1); + } + } + } + } + } + if (vdi && keptVariable && vdi->e()->type().isfloat() && vdi->e()->type().isvar() && + vdi->e()->ti()->domain() != NULL) { + GCLock lock; + FloatSetVal* vdi_dom = eval_floatset(env, vdi->e()->ti()->domain()); + FloatVal vmin = vdi_dom->min(); + FloatVal vmax = vdi_dom->max(); + if (vmin == -FloatVal::infinity() && vmax == FloatVal::infinity()) { + vdi->e()->ti()->domain(NULL); + } else if (vmin == -FloatVal::infinity()) { + vdi->e()->ti()->domain(NULL); + std::vector args(2); + args[0] = vdi->e()->id(); + args[1] = FloatLit::a(vmax); + Call* call = new Call(Location().introduce(), constants().ids.float_.le, args); + call->type(Type::varbool()); + call->decl(env.model->matchFn(env, call, false)); + env.flat_addItem(new ConstraintI(Location().introduce(), call)); + } else if (vmax == FloatVal::infinity()) { + vdi->e()->ti()->domain(NULL); + std::vector args(2); + args[0] = FloatLit::a(vmin); + args[1] = vdi->e()->id(); + Call* call = new Call(Location().introduce(), constants().ids.float_.le, args); + call->type(Type::varbool()); + call->decl(env.model->matchFn(env, call, false)); + env.flat_addItem(new ConstraintI(Location().introduce(), call)); + } else if (vdi_dom->size() > 1) { + BinOp* dom_ranges = new BinOp(vdi->e()->ti()->domain()->loc().introduce(), + FloatLit::a(vmin), BOT_DOTDOT, FloatLit::a(vmax)); + vdi->e()->ti()->domain(dom_ranges); + + std::vector ranges; + for (FloatSetRanges vdi_r(vdi_dom); vdi_r(); ++vdi_r) { + ranges.push_back(FloatLit::a(vdi_r.min())); + ranges.push_back(FloatLit::a(vdi_r.max())); + } + ArrayLit* al = new ArrayLit(Location().introduce(), ranges); + al->type(Type::parfloat(1)); + std::vector args(2); + args[0] = vdi->e()->id(); + args[1] = al; + Call* call = new Call(Location().introduce(), constants().ids.float_.dom, args); + call->type(Type::varbool()); + call->decl(env.model->matchFn(env, call, false)); + env.flat_addItem(new ConstraintI(Location().introduce(), call)); + } + } + } + + // rewrite some constraints if there are redefinitions + for (int ai = 0; ai < agenda.size(); ai++) { + int i = agenda[ai]; + if (VarDeclI* vdi = m[i]->dyn_cast()) { + VarDecl* vd = vdi->e(); + if (!vdi->removed() && vd->e()) { + bool isTrueVar = + vd->type().isbool() && Expression::equal(vd->ti()->domain(), constants().lit_true); + if (Call* c = vd->e()->dyn_cast()) { + GCLock lock; + Call* nc = NULL; + if (c->id() == constants().ids.lin_exp) { + if (int_lin_eq) { + std::vector args(c->n_args()); + ArrayLit* le_c = follow_id(c->arg(0))->cast(); + std::vector nc_c(le_c->size()); + for (unsigned int i = static_cast(nc_c.size()); i--;) + nc_c[i] = (*le_c)[i]; + nc_c.push_back(IntLit::a(-1)); + args[0] = new ArrayLit(Location().introduce(), nc_c); + args[0]->type(Type::parint(1)); + ArrayLit* le_x = follow_id(c->arg(1))->cast(); + std::vector nx(le_x->size()); + for (unsigned int i = static_cast(nx.size()); i--;) + nx[i] = (*le_x)[i]; + nx.push_back(vd->id()); + args[1] = new ArrayLit(Location().introduce(), nx); + args[1]->type(Type::varint(1)); + IntVal d = c->arg(2)->cast()->v(); + args[2] = IntLit::a(-d); + args[2]->type(Type::parint(0)); + nc = new Call(c->loc().introduce(), ASTString("int_lin_eq"), args); + nc->type(Type::varbool()); + nc->decl(int_lin_eq); + } + } else if (c->id() == constants().ids.exists) { + if (array_bool_or) { + std::vector args(2); + args[0] = c->arg(0); + args[1] = vd->id(); + nc = new Call(c->loc().introduce(), array_bool_or->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_or); + } + } else if (!isTrueVar && c->id() == constants().ids.forall) { + if (array_bool_and) { + std::vector args(2); + args[0] = c->arg(0); + args[1] = vd->id(); + nc = new Call(c->loc().introduce(), array_bool_and->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_and); + } + } else if (isTrueVar && c->id() == constants().ids.clause && array_bool_clause) { + std::vector args(2); + args[0] = c->arg(0); + args[1] = c->arg(1); + nc = new Call(c->loc().introduce(), array_bool_clause->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_clause); + } else if (c->id() == constants().ids.clause && array_bool_clause_reif) { + std::vector args(3); + args[0] = c->arg(0); + args[1] = c->arg(1); + args[2] = vd->id(); + nc = new Call(c->loc().introduce(), array_bool_clause_reif->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_clause_reif); + } else { + if (isTrueVar) { + FunctionI* decl = env.model->matchFn(env, c, false); + env.cse_map_remove(c); + if (decl->e() || c->id() == constants().ids.forall) { + if (decl->e()) addPathAnnotation(env, decl->e()); + c->decl(decl); + nc = c; + } + } else { + std::vector args(c->n_args()); + for (unsigned int i = static_cast(args.size()); i--;) + args[i] = c->arg(i); + args.push_back(vd->id()); + ASTString cid = c->id(); + if (cid == constants().ids.clause && array_bool_clause_reif) { + nc = new Call(c->loc().introduce(), array_bool_clause_reif->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_clause_reif); + } else { + if (c->type().isbool() && vd->type().isbool()) { + if (env.fopts.enable_imp && vd->ann().contains(constants().ctx.pos)) { + cid = env.halfReifyId(c->id()); + if (env.model->matchFn(env, cid, args, false) == NULL) { + cid = env.reifyId(c->id()); + } + } else { + cid = env.reifyId(c->id()); + } + } + FunctionI* decl = env.model->matchFn(env, cid, args, false); + if (decl && decl->e()) { + addPathAnnotation(env, decl->e()); + nc = new Call(c->loc().introduce(), cid, args); + nc->type(Type::varbool()); + nc->decl(decl); + } + } + } + } + if (nc != NULL) { + CollectDecls cd(env.vo, deletedVarDecls, vdi); + topDown(cd, c); + vd->e(NULL); + // Need to remove right hand side from CSE map, otherwise + // flattening of nc could assume c has already been flattened + // to vd + env.cse_map_remove(c); + /// TODO: check if removing variables here makes sense: + // if (!isOutput(vd) && env.vo.occurrences(vd)==0) { + // removedItems.push_back(vdi); + // } + if (nc != c) { + makeDefinedVar(vd, nc); + } + StringLit* vsl = getLongestMznPathAnnotation(env, vdi->e()); + StringLit* csl = getLongestMznPathAnnotation(env, c); + CallStackItem* vsi = NULL; + CallStackItem* csi = NULL; + if (vsl) vsi = new CallStackItem(env, vsl); + if (csl) csi = new CallStackItem(env, csl); + (void)flat_exp(env, Ctx(), nc, constants().var_true, constants().var_true); + if (csi) delete csi; + if (vsi) delete vsi; + } + } + } + } else if (ConstraintI* ci = m[i]->dyn_cast()) { + if (!ci->removed()) { + if (Call* c = ci->e()->dyn_cast()) { + GCLock lock; + Call* nc = NULL; + if (c->id() == constants().ids.exists) { + if (array_bool_or) { + std::vector args(2); + args[0] = c->arg(0); + args[1] = constants().lit_true; + nc = new Call(c->loc().introduce(), array_bool_or->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_or); + } + } else if (c->id() == constants().ids.forall) { + if (array_bool_and) { + std::vector args(2); + args[0] = c->arg(0); + args[1] = constants().lit_true; + nc = new Call(c->loc().introduce(), array_bool_and->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_and); + } + } else if (c->id() == constants().ids.clause) { + if (array_bool_clause) { + std::vector args(2); + args[0] = c->arg(0); + args[1] = c->arg(1); + nc = new Call(c->loc().introduce(), array_bool_clause->id(), args); + nc->type(Type::varbool()); + nc->decl(array_bool_clause); + } + } else if (c->id() == constants().ids.bool_xor) { + if (bool_xor) { + std::vector args(3); + args[0] = c->arg(0); + args[1] = c->arg(1); + args[2] = c->n_args() == 2 ? constants().lit_true : c->arg(2); + nc = new Call(c->loc().introduce(), bool_xor->id(), args); + nc->type(Type::varbool()); + nc->decl(bool_xor); + } + } else { + FunctionI* decl = env.model->matchFn(env, c, false); + if (decl && decl->e()) { + nc = c; + nc->decl(decl); + } + } + if (nc != NULL) { + CollectDecls cd(env.vo, deletedVarDecls, ci); + topDown(cd, c); + ci->e(constants().lit_true); + env.flat_removeItem(i); + StringLit* sl = getLongestMznPathAnnotation(env, c); + CallStackItem* csi = NULL; + if (sl) csi = new CallStackItem(env, sl); + (void)flat_exp(env, Ctx(), nc, constants().var_true, constants().var_true); + if (csi) delete csi; + } + } + } + } + } + + startItem = endItem + 1; + endItem = m.size() - 1; + } + + for (unsigned int i = 0; i < removedItems.size(); i++) { + if (env.vo.occurrences(removedItems[i]->e()) == 0) { + CollectDecls cd(env.vo, deletedVarDecls, removedItems[i]); + topDown(cd, removedItems[i]->e()->e()); + env.flat_removeItem(removedItems[i]); + } + } + + // Add redefinitions for output variables that may have been redefined since createOutput + for (unsigned int i = 0; i < env.output->size(); i++) { + if (VarDeclI* vdi = (*env.output)[i]->dyn_cast()) { + IdMap::iterator it; + if (vdi->e()->e() == NULL && + (it = env.reverseMappers.find(vdi->e()->id())) != env.reverseMappers.end()) { + GCLock lock; + Call* rhs = copy(env, env.cmap, it->second())->cast(); + std::vector tv(rhs->n_args()); + for (unsigned int i = rhs->n_args(); i--;) { + tv[i] = rhs->arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = env.output->matchFn(env, rhs->id(), tv, false); + Type t; + if (decl == NULL) { + FunctionI* origdecl = env.model->matchFn(env, rhs->id(), tv, false); + if (origdecl == NULL) { + throw FlatteningError( + env, rhs->loc(), + "function " + rhs->id().str() + " is used in output, par version needed"); + } + if (!isBuiltin(origdecl)) { + decl = copy(env, env.cmap, origdecl)->cast(); + CollectOccurrencesE ce(env.output_vo, decl); + topDown(ce, decl->e()); + topDown(ce, decl->ti()); + for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]); + env.output->registerFn(env, decl); + env.output->addItem(decl); + } else { + decl = origdecl; + } + } + rhs->decl(decl); + outputVarDecls(env, vdi, rhs); + + removeIsOutput(vdi->e()->flat()); + vdi->e()->e(rhs); + } + } + } + + for (unsigned int i = 0; i < m.size(); i++) { + if (ConstraintI* ci = m[i]->dyn_cast()) { + if (Call* c = ci->e()->dyn_cast()) { + if (c->decl() == constants().var_redef) { + CollectDecls cd(env.vo, deletedVarDecls, ci); + topDown(cd, c); + env.flat_removeItem(i); + } + } + } + } + + while (!deletedVarDecls.empty()) { + VarDecl* cur = deletedVarDecls.back(); + deletedVarDecls.pop_back(); + if (env.vo.occurrences(cur) == 0 && !isOutput(cur)) { + if (CollectDecls::varIsFree(cur)) { + IdMap::iterator cur_idx = env.vo.idx.find(cur->id()); + if (cur_idx != env.vo.idx.end() && !m[cur_idx->second]->removed()) { + CollectDecls cd(env.vo, deletedVarDecls, m[cur_idx->second]->cast()); + topDown(cd, cur->e()); + env.flat_removeItem(cur_idx->second); + } + } + } + } + + if (!opt.keepOutputInFzn) { + finaliseOutput(env, deletedVarDecls); + } + + while (!deletedVarDecls.empty()) { + VarDecl* cur = deletedVarDecls.back(); + deletedVarDecls.pop_back(); + if (env.vo.occurrences(cur) == 0 && !isOutput(cur)) { + if (CollectDecls::varIsFree(cur)) { + IdMap::iterator cur_idx = env.vo.idx.find(cur->id()); + if (cur_idx != env.vo.idx.end() && !m[cur_idx->second]->removed()) { + CollectDecls cd(env.vo, deletedVarDecls, m[cur_idx->second]->cast()); + topDown(cd, cur->e()); + env.flat_removeItem(cur_idx->second); + } + } + } + } + + cleanupOutput(env); + } catch (ModelInconsistent&) { + } +} + +void clearInternalAnnotations(Expression* e) { + e->ann().remove(constants().ann.promise_total); + e->ann().remove(constants().ann.maybe_partial); + e->ann().remove(constants().ann.add_to_output); + e->ann().remove(constants().ann.rhs_from_assignment); + // Remove defines_var(x) annotation where x is par + std::vector removeAnns; + for (ExpressionSetIter anns = e->ann().begin(); anns != e->ann().end(); ++anns) { + if (Call* c = (*anns)->dyn_cast()) { + if (c->id() == constants().ann.defines_var && c->arg(0)->type().ispar()) { + removeAnns.push_back(c); + } + } + } + for (unsigned int i = 0; i < removeAnns.size(); i++) { + e->ann().remove(removeAnns[i]); + } +} + +std::vector cleanup_vardecl(EnvI& env, VarDeclI* vdi, VarDecl* vd) { + std::vector added_constraints; + + // In FlatZinc par variables have RHSs, not domains + if (vd->type().ispar()) { + vd->ann().clear(); + vd->introduced(false); + vd->ti()->domain(NULL); + } + + // In FlatZinc the RHS of a VarDecl must be a literal, Id or empty + // Example: + // var 1..5: x = function(y) + // becomes: + // var 1..5: x; + // relation(x, y); + if (vd->type().isvar() && vd->type().isbool()) { + bool is_fixed = (vd->ti()->domain() != NULL); + if (Expression::equal(vd->ti()->domain(), constants().lit_true)) { + // Ex: var true: b = e() + + // Store RHS + Expression* ve = vd->e(); + vd->e(constants().lit_true); + vd->ti()->domain(NULL); + // Ex: var bool: b = true + + // If vd had a RHS + if (ve != NULL) { + if (Call* vcc = ve->dyn_cast()) { + // Convert functions to relations: + // exists([x]) => array_bool_or([x],true) + // forall([x]) => array_bool_and([x],true) + // clause([x]) => bool_clause([x]) + ASTString cid; + std::vector args; + if (vcc->id() == constants().ids.exists) { + cid = constants().ids.array_bool_or; + args.push_back(vcc->arg(0)); + args.push_back(constants().lit_true); + } else if (vcc->id() == constants().ids.forall) { + cid = constants().ids.array_bool_and; + args.push_back(vcc->arg(0)); + args.push_back(constants().lit_true); + } else if (vcc->id() == constants().ids.clause) { + cid = constants().ids.bool_clause; + args.push_back(vcc->arg(0)); + args.push_back(vcc->arg(1)); + } + + if (args.size() == 0) { + // Post original RHS as stand alone constraint + ve = vcc; + } else { + // Create new call, retain annotations from original RHS + Call* nc = new Call(vcc->loc().introduce(), cid, args); + nc->type(vcc->type()); + nc->ann().merge(vcc->ann()); + ve = nc; + } + } else if (Id* id = ve->dyn_cast()) { + if (id->decl()->ti()->domain() != constants().lit_true) { + // Inconsistent assignment: post bool_eq(y, true) + std::vector args(2); + args[0] = id; + args[1] = constants().lit_true; + GCLock lock; + ve = new Call(Location().introduce(), constants().ids.bool_eq, args); + } else { + // Don't post this + ve = constants().lit_true; + } + } + // Post new constraint + if (ve != constants().lit_true) { + clearInternalAnnotations(ve); + added_constraints.push_back(ve); + } + } + } else { + // Ex: var false: b = e() + if (vd->e() != NULL) { + if (vd->e()->eid() == Expression::E_CALL) { + // Convert functions to relations: + // var false: b = exists([x]) => array_bool_or([x], b) + // var false: b = forall([x]) => array_bool_and([x], b) + // var false: b = clause([x]) => bool_clause_reif([x], b) + const Call* c = vd->e()->cast(); + GCLock lock; + vd->e(NULL); + ASTString cid; + std::vector args(c->n_args()); + for (unsigned int i = args.size(); i--;) args[i] = c->arg(i); + if (is_fixed) { + args.push_back(constants().lit_false); + } else { + args.push_back(vd->id()); + } + if (c->id() == constants().ids.exists) { + cid = constants().ids.array_bool_or; + } else if (c->id() == constants().ids.forall) { + cid = constants().ids.array_bool_and; + } else if (c->id() == constants().ids.clause) { + cid = constants().ids.bool_clause_reif; + } else { + if (env.fopts.enable_imp && vd->ann().contains(constants().ctx.pos)) { + cid = env.halfReifyId(c->id()); + if (env.model->matchFn(env, cid, args, false) == NULL) { + cid = env.reifyId(c->id()); + } + } else { + cid = env.reifyId(c->id()); + } + } + Call* nc = new Call(c->loc().introduce(), cid, args); + nc->type(c->type()); + FunctionI* decl = env.model->matchFn(env, nc, false); + if (decl == NULL) { + throw FlatteningError( + env, c->loc(), + "'" + c->id().str() + + "' is used in a reified context but no reified version is available"); + } + nc->decl(decl); + if (!is_fixed) { + makeDefinedVar(vd, nc); + } + nc->ann().merge(c->ann()); + clearInternalAnnotations(nc); + added_constraints.push_back(nc); + } else { + assert(vd->e()->eid() == Expression::E_ID || vd->e()->eid() == Expression::E_BOOLLIT); + } + } + if (Expression::equal(vd->ti()->domain(), constants().lit_false)) { + vd->ti()->domain(NULL); + vd->e(constants().lit_false); + } + } + if (vdi != NULL && is_fixed && env.vo.occurrences(vd) == 0) { + if (isOutput(vd)) { + VarDecl* vd_output = (*env.output)[env.output_vo_flat.find(vd)]->cast()->e(); + if (vd_output->e() == NULL) { + vd_output->e(vd->e()); + } + } + env.flat_removeItem(vdi); + } + } else if (vd->type().isvar() && vd->type().dim() == 0) { + // Int or Float var + if (vd->e() != NULL) { + if (const Call* cc = vd->e()->dyn_cast()) { + // Remove RHS from vd + vd->e(NULL); + + std::vector args(cc->n_args()); + ASTString cid; + if (cc->id() == constants().ids.lin_exp) { + // a = lin_exp([1],[b],5) => int_lin_eq([1,-1],[b,a],-5):: defines_var(a) + ArrayLit* le_c = follow_id(cc->arg(0))->cast(); + std::vector nc(le_c->size()); + for (unsigned int i = static_cast(nc.size()); i--;) nc[i] = (*le_c)[i]; + if (le_c->type().bt() == Type::BT_INT) { + cid = constants().ids.int_.lin_eq; + nc.push_back(IntLit::a(-1)); + args[0] = new ArrayLit(Location().introduce(), nc); + args[0]->type(Type::parint(1)); + ArrayLit* le_x = follow_id(cc->arg(1))->cast(); + std::vector nx(le_x->size()); + for (unsigned int i = static_cast(nx.size()); i--;) nx[i] = (*le_x)[i]; + nx.push_back(vd->id()); + args[1] = new ArrayLit(Location().introduce(), nx); + args[1]->type(le_x->type()); + IntVal d = cc->arg(2)->cast()->v(); + args[2] = IntLit::a(-d); + } else { + // float + cid = constants().ids.float_.lin_eq; + nc.push_back(FloatLit::a(-1.0)); + args[0] = new ArrayLit(Location().introduce(), nc); + args[0]->type(Type::parfloat(1)); + ArrayLit* le_x = follow_id(cc->arg(1))->cast(); + std::vector nx(le_x->size()); + for (unsigned int i = static_cast(nx.size()); i--;) nx[i] = (*le_x)[i]; + nx.push_back(vd->id()); + args[1] = new ArrayLit(Location().introduce(), nx); + args[1]->type(le_x->type()); + FloatVal d = cc->arg(2)->cast()->v(); + args[2] = FloatLit::a(-d); + } + } else { + if (cc->id() == "card") { + // card is 'set_card' in old FlatZinc + cid = constants().ids.set_card; + } else { + cid = cc->id(); + } + for (unsigned int i = static_cast(args.size()); i--;) args[i] = cc->arg(i); + args.push_back(vd->id()); + } + Call* nc = new Call(cc->loc().introduce(), cid, args); + nc->type(cc->type()); + makeDefinedVar(vd, nc); + nc->ann().merge(cc->ann()); + + clearInternalAnnotations(nc); + added_constraints.push_back(nc); + } else { + // RHS must be literal or Id + assert(vd->e()->eid() == Expression::E_ID || vd->e()->eid() == Expression::E_INTLIT || + vd->e()->eid() == Expression::E_FLOATLIT || + vd->e()->eid() == Expression::E_BOOLLIT || vd->e()->eid() == Expression::E_SETLIT); + } + } + } else if (vd->type().dim() > 0) { + // vd is an array + + // If RHS is an Id, follow id to RHS + // a = [1,2,3]; b = a; + // vd = b => vd = [1,2,3] + if (!vd->e()->isa()) { + vd->e(follow_id(vd->e())); + } + + // If empty array or 1 indexed, continue + if (vd->ti()->ranges().size() == 1 && vd->ti()->ranges()[0]->domain() != NULL && + vd->ti()->ranges()[0]->domain()->isa()) { + IntSetVal* isv = vd->ti()->ranges()[0]->domain()->cast()->isv(); + if (isv && (isv->size() == 0 || isv->min(0) == 1)) return added_constraints; + } + + // Array should be 1 indexed since ArrayLit is 1 indexed + assert(vd->e() != NULL); + ArrayLit* al = NULL; + Expression* e = vd->e(); + while (al == NULL) { + switch (e->eid()) { + case Expression::E_ARRAYLIT: + al = e->cast(); + break; + case Expression::E_ID: + e = e->cast()->decl()->e(); + break; + default: + assert(false); + } + } + al->make1d(); + IntSetVal* isv = IntSetVal::a(1, al->length()); + if (vd->ti()->ranges().size() == 1) { + vd->ti()->ranges()[0]->domain(new SetLit(Location().introduce(), isv)); + } else { + std::vector r(1); + r[0] = new TypeInst(vd->ti()->ranges()[0]->loc(), vd->ti()->ranges()[0]->type(), + new SetLit(Location().introduce(), isv)); + ASTExprVec ranges(r); + TypeInst* ti = new TypeInst(vd->ti()->loc(), vd->ti()->type(), ranges, vd->ti()->domain()); + vd->ti(ti); + } + } + + // Remove boolean context annotations used only on compilation + vd->ann().remove(constants().ctx.mix); + vd->ann().remove(constants().ctx.pos); + vd->ann().remove(constants().ctx.neg); + vd->ann().remove(constants().ctx.root); + vd->ann().remove(constants().ann.promise_total); + vd->ann().remove(constants().ann.add_to_output); + vd->ann().remove(constants().ann.mzn_check_var); + vd->ann().remove(constants().ann.rhs_from_assignment); + vd->ann().removeCall(constants().ann.mzn_check_enum_var); + + return added_constraints; +} + +Expression* cleanup_constraint(EnvI& env, std::unordered_set& globals, Expression* ce) { + clearInternalAnnotations(ce); + + if (Call* vc = ce->dyn_cast()) { + for (unsigned int i = 0; i < vc->n_args(); i++) { + // Change array indicies to be 1 indexed + if (ArrayLit* al = vc->arg(i)->dyn_cast()) { + if (al->dims() > 1 || al->min(0) != 1) { + al->make1d(); + } + } + } + // Convert functions to relations: + // exists([x]) => array_bool_or([x],true) + // forall([x]) => array_bool_and([x],true) + // clause([x]) => bool_clause([x]) + // bool_xor([x],[y]) => bool_xor([x],[y],true) + if (vc->id() == constants().ids.exists) { + GCLock lock; + vc->id(constants().ids.array_bool_or); + std::vector args(2); + args[0] = vc->arg(0); + args[1] = constants().lit_true; + ASTExprVec argsv(args); + vc->args(argsv); + vc->decl(env.model->matchFn(env, vc, false)); + } else if (vc->id() == constants().ids.forall) { + GCLock lock; + vc->id(constants().ids.array_bool_and); + std::vector args(2); + args[0] = vc->arg(0); + args[1] = constants().lit_true; + ASTExprVec argsv(args); + vc->args(argsv); + vc->decl(env.model->matchFn(env, vc, false)); + } else if (vc->id() == constants().ids.clause) { + GCLock lock; + vc->id(constants().ids.bool_clause); + vc->decl(env.model->matchFn(env, vc, false)); + } else if (vc->id() == constants().ids.bool_xor && vc->n_args() == 2) { + GCLock lock; + std::vector args(3); + args[0] = vc->arg(0); + args[1] = vc->arg(1); + args[2] = constants().lit_true; + ASTExprVec argsv(args); + vc->args(argsv); + vc->decl(env.model->matchFn(env, vc, false)); + } + + // If vc->decl() is a solver builtin and has not been added to the + // FlatZinc, add it + if (vc->decl() && vc->decl() != constants().var_redef && !vc->decl()->from_stdlib() && + globals.find(vc->decl()) == globals.end()) { + env.flat_addItem(vc->decl()); + globals.insert(vc->decl()); + } + return ce; + } else if (Id* id = ce->dyn_cast()) { + // Ex: constraint b; => constraint bool_eq(b, true); + std::vector args(2); + args[0] = id; + args[1] = constants().lit_true; + GCLock lock; + return new Call(Location().introduce(), constants().ids.bool_eq, args); + } else if (BoolLit* bl = ce->dyn_cast()) { + // Ex: true => delete; false => bool_eq(false, true); + if (!bl->v()) { + GCLock lock; + std::vector args(2); + args[0] = constants().lit_false; + args[1] = constants().lit_true; + Call* neq = new Call(Location().introduce(), constants().ids.bool_eq, args); + return neq; + } else { + return NULL; + } + } else { + return ce; + } +} + +void oldflatzinc(Env& e) { + Model* m = e.flat(); + + // Mark annotations and optional variables for removal + for (unsigned int i = 0; i < m->size(); i++) { + Item* item = (*m)[i]; + if (VarDeclI* vdi = item->dyn_cast()) { + if (item->cast()->e()->type().ot() == Type::OT_OPTIONAL || + item->cast()->e()->type().bt() == Type::BT_ANN) { + e.envi().flat_removeItem(i); + } + } + } + + EnvI& env = e.envi(); + + int msize = m->size(); + + // Predicate declarations of solver builtins + std::unordered_set globals; + + // Record indices of VarDeclIs with Id RHS for sorting & unification + std::vector declsWithIds; + for (int i = 0; i < msize; i++) { + if ((*m)[i]->removed()) continue; + if (VarDeclI* vdi = (*m)[i]->dyn_cast()) { + GCLock lock; + VarDecl* vd = vdi->e(); + std::vector added_constraints = cleanup_vardecl(e.envi(), vdi, vd); + // Record whether this VarDecl is equal to an Id (aliasing) + if (vd->e() && vd->e()->isa()) { + declsWithIds.push_back(i); + vdi->e()->payload(-static_cast(i) - 1); + } else { + vdi->e()->payload(i); + } + for (auto nc : added_constraints) { + Expression* new_ce = cleanup_constraint(e.envi(), globals, nc); + if (new_ce) { + e.envi().flat_addItem(new ConstraintI(Location().introduce(), new_ce)); + } + } + } else if (ConstraintI* ci = (*m)[i]->dyn_cast()) { + Expression* new_ce = cleanup_constraint(e.envi(), globals, ci->e()); + if (new_ce) { + ci->e(new_ce); + } else { + ci->remove(); + } + } else if (FunctionI* fi = (*m)[i]->dyn_cast()) { + if (Let* let = Expression::dyn_cast(fi->e())) { + GCLock lock; + std::vector new_let; + for (unsigned int i = 0; i < let->let().size(); i++) { + Expression* let_e = let->let()[i]; + if (VarDecl* vd = let_e->dyn_cast()) { + std::vector added_constraints = cleanup_vardecl(e.envi(), NULL, vd); + new_let.push_back(vd); + for (auto nc : added_constraints) new_let.push_back(nc); + } else { + Expression* new_ce = cleanup_constraint(e.envi(), globals, let_e); + if (new_ce) { + new_let.push_back(new_ce); + } + } + } + fi->e(new Let(let->loc(), new_let, let->in())); + } + } else if (SolveI* si = (*m)[i]->dyn_cast()) { + if (si->e() && si->e()->type().ispar()) { + // Introduce VarDecl if objective expression is par + GCLock lock; + TypeInst* ti = new TypeInst(Location().introduce(), si->e()->type(), NULL); + VarDecl* constantobj = new VarDecl(Location().introduce(), ti, e.envi().genId(), si->e()); + si->e(constantobj->id()); + e.envi().flat_addItem(new VarDeclI(Location().introduce(), constantobj)); + } + } + } + + // Sort VarDecls in FlatZinc so that VarDecls are declared before use + std::vector sortedVarDecls(declsWithIds.size()); + int vdCount = 0; + for (unsigned int i = 0; i < declsWithIds.size(); i++) { + VarDecl* cur = (*m)[declsWithIds[i]]->cast()->e(); + std::vector stack; + while (cur && cur->payload() < 0) { + stack.push_back(cur->payload()); + if (Id* id = cur->e()->dyn_cast()) { + cur = id->decl(); + } else { + cur = NULL; + } + } + for (unsigned int i = static_cast(stack.size()); i--;) { + VarDeclI* vdi = (*m)[-stack[i] - 1]->cast(); + vdi->e()->payload(-vdi->e()->payload() - 1); + sortedVarDecls[vdCount++] = vdi; + } + } + for (unsigned int i = 0; i < declsWithIds.size(); i++) { + (*m)[declsWithIds[i]] = sortedVarDecls[i]; + } + + // Remove marked items + m->compact(); + e.envi().output->compact(); + + for (IdMap::iterator it = env.vo._m.begin(); it != env.vo._m.end(); ++it) { + std::vector toRemove; + for (VarOccurrences::Items::iterator iit = it->second.begin(); iit != it->second.end(); ++iit) { + if ((*iit)->removed()) { + toRemove.push_back(*iit); + } + } + for (unsigned int i = 0; i < toRemove.size(); i++) { + it->second.erase(toRemove[i]); + } + } + + class Cmp { + public: + bool operator()(Item* i, Item* j) { + if (i->iid() == Item::II_FUN || j->iid() == Item::II_FUN) { + if (i->iid() == j->iid()) return false; + return i->iid() == Item::II_FUN; + } + if (i->iid() == Item::II_SOL) { + assert(j->iid() != i->iid()); + return false; + } + if (j->iid() == Item::II_SOL) { + assert(j->iid() != i->iid()); + return true; + } + if (i->iid() == Item::II_VD) { + if (j->iid() != i->iid()) return true; + if (i->cast()->e()->type().ispar() && j->cast()->e()->type().isvar()) + return true; + if (j->cast()->e()->type().ispar() && i->cast()->e()->type().isvar()) + return false; + if (i->cast()->e()->type().dim() == 0 && + j->cast()->e()->type().dim() != 0) + return true; + if (i->cast()->e()->type().dim() != 0 && + j->cast()->e()->type().dim() == 0) + return false; + if (i->cast()->e()->e() == NULL && j->cast()->e()->e() != NULL) + return true; + if (i->cast()->e()->e() && j->cast()->e()->e() && + !i->cast()->e()->e()->isa() && j->cast()->e()->e()->isa()) + return true; + } + return false; + } + } _cmp; + // Perform final sorting + std::stable_sort(m->begin(), m->end(), _cmp); +} + +FlatModelStatistics statistics(Model* m) { + Model* flat = m; + FlatModelStatistics stats; + for (unsigned int i = 0; i < flat->size(); i++) { + if (!(*flat)[i]->removed()) { + if (VarDeclI* vdi = (*flat)[i]->dyn_cast()) { + Type t = vdi->e()->type(); + if (t.isvar() && t.dim() == 0) { + if (t.is_set()) + stats.n_set_vars++; + else if (t.isint()) + stats.n_int_vars++; + else if (t.isbool()) + stats.n_bool_vars++; + else if (t.isfloat()) + stats.n_float_vars++; + } + } else if (ConstraintI* ci = (*flat)[i]->dyn_cast()) { + if (Call* call = ci->e()->dyn_cast()) { + if (call->id().endsWith("_reif")) { + stats.n_reif_ct++; + } else if (call->id().endsWith("_imp")) { + stats.n_imp_ct++; + } + if (call->n_args() > 0) { + Type all_t; + for (unsigned int i = 0; i < call->n_args(); i++) { + Type t = call->arg(i)->type(); + if (t.isvar()) { + if (t.st() == Type::ST_SET) + all_t = t; + else if (t.bt() == Type::BT_FLOAT && all_t.st() != Type::ST_SET) + all_t = t; + else if (t.bt() == Type::BT_INT && all_t.bt() != Type::BT_FLOAT && + all_t.st() != Type::ST_SET) + all_t = t; + else if (t.bt() == Type::BT_BOOL && all_t.bt() != Type::BT_INT && + all_t.bt() != Type::BT_FLOAT && all_t.st() != Type::ST_SET) + all_t = t; + } + } + if (all_t.isvar()) { + if (all_t.st() == Type::ST_SET) + stats.n_set_ct++; + else if (all_t.bt() == Type::BT_INT) + stats.n_int_ct++; + else if (all_t.bt() == Type::BT_BOOL) + stats.n_bool_ct++; + else if (all_t.bt() == Type::BT_FLOAT) + stats.n_float_ct++; + } + } + } + } + } + } + return stats; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flat_exp.cpp b/lib/flatten/flat_exp.cpp new file mode 100644 index 0000000..6679a40 --- /dev/null +++ b/lib/flatten/flat_exp.cpp @@ -0,0 +1,117 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +CallArgItem::CallArgItem(EnvI& env0) : env(env0) { + env.idStack.push_back(static_cast(env.callStack.size())); +} +CallArgItem::~CallArgItem(void) { env.idStack.pop_back(); } + +Expression* createDummyValue(EnvI& env, const Type& t) { + if (t.dim() > 0) { + Expression* ret = new ArrayLit(Location().introduce(), std::vector()); + Type ret_t = t; + ret_t.ti(Type::TI_PAR); + ret->type(ret_t); + return ret; + } + if (t.st() == Type::ST_SET) { + Expression* ret = new SetLit(Location().introduce(), std::vector()); + Type ret_t = t; + ret_t.ti(Type::TI_PAR); + ret->type(ret_t); + return ret; + } + switch (t.bt()) { + case Type::BT_INT: + return IntLit::a(0); + case Type::BT_BOOL: + return constants().boollit(false); + case Type::BT_FLOAT: + return FloatLit::a(0); + case Type::BT_STRING: + return new StringLit(Location().introduce(), ""); + case Type::BT_ANN: + return constants().ann.promise_total; + default: + return NULL; + } +} + +EE flatten_error(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + throw InternalError("invalid expression encountered during compilation"); +} + +#ifndef NDEBUG +void mzn_break_here(Expression* e) { std::cerr << "% mzn_break_here: " << *e << "\n"; } +#endif + +typedef EE (*ExprFlattener)(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); + +EE flatten_setlit(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_id(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_anon(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_arraylit(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_arrayaccess(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_comp(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_ite(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_binop(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_unop(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_call(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_vardecl(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_let(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_par(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); +EE flatten_error(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b); + +EE flat_exp(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + if (e == NULL) return EE(); + +#ifndef NDEBUG + Annotation& e_ann = e->ann(); + if (e_ann.contains(constants().ann.mzn_break_here)) mzn_break_here(e); +#endif + + assert(!e->type().isunknown()); + + static const ExprFlattener flattener_dispatch[] = { + &flatten_par, // par expressions + &flatten_error, // E_INTLIT + &flatten_error, // E_FLOATLIT + &flatten_setlit, // E_SETLIT + &flatten_error, // E_BOOLLIT + &flatten_error, // E_STRINGLIT + &flatten_id, // E_ID + &flatten_anon, // E_ANON + &flatten_arraylit, // E_ARRAYLIT + &flatten_arrayaccess, // E_ARRAYACCESS + &flatten_comp, // E_COMP + &flatten_ite, // E_ITE + &flatten_binop, // E_BINOP + &flatten_unop, // E_UNOP + &flatten_call, // E_CALL + &flatten_vardecl, // E_VARDECL + &flatten_let, // E_LET + &flatten_error, // E_TI + &flatten_error // E_TIID + }; + + int dispatch = + (e->type().ispar() && !e->isa() && !e->isa() && e->type().bt() != Type::BT_ANN) + ? 0 + : e->eid() - Expression::E_INTLIT + 1; + + return flattener_dispatch[dispatch](env, ctx, e, r, b); +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_anon.cpp b/lib/flatten/flatten_anon.cpp new file mode 100644 index 0000000..7a26a39 --- /dev/null +++ b/lib/flatten/flatten_anon.cpp @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_anon(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + AnonVar* av = e->cast(); + if (av->type().isbot()) { + throw InternalError("type of anonymous variable could not be inferred"); + } + GCLock lock; + VarDecl* vd = + newVarDecl(env, Ctx(), new TypeInst(Location().introduce(), av->type()), NULL, NULL, NULL); + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, vd->id()); + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_arrayaccess.cpp b/lib/flatten/flatten_arrayaccess.cpp new file mode 100644 index 0000000..e290601 --- /dev/null +++ b/lib/flatten/flatten_arrayaccess.cpp @@ -0,0 +1,278 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_arrayaccess(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + ArrayAccess* aa = e->cast(); + KeepAlive aa_ka = aa; + + Ctx nctx = ctx; + nctx.b = +nctx.b; + nctx.neg = false; + EE eev = flat_exp(env, nctx, aa->v(), NULL, NULL); + std::vector ees; + +start_flatten_arrayaccess: + for (unsigned int i = 0; i < aa->idx().size(); i++) { + Expression* tmp = follow_id_to_decl(aa->idx()[i]); + if (VarDecl* vd = tmp->dyn_cast()) tmp = vd->id(); + if (tmp->type().ispar()) { + ArrayLit* al; + if (eev.r()->isa()) { + al = eev.r()->cast(); + } else { + Id* id = eev.r()->cast(); + if (id->decl() == NULL) { + throw InternalError("undefined identifier"); + } + if (id->decl()->e() == NULL) { + throw InternalError("array without initialiser not supported"); + } + Expression* id_e = follow_id(id); + if (id_e->isa()) { + al = id_e->cast(); + } else { + throw InternalError("builtin function returning array not supported"); + } + } + + std::vector elems; + std::vector idx(aa->idx().size()); + std::vector > dims; + std::vector newaccess; + std::vector nonpar; + std::vector stack; + for (unsigned int j = 0; j < aa->idx().size(); j++) { + Expression* tmp = follow_id_to_decl(aa->idx()[j]); + if (VarDecl* vd = tmp->dyn_cast()) tmp = vd->id(); + if (tmp->type().ispar()) { + GCLock lock; + idx[j] = eval_int(env, tmp).toInt(); + } else { + idx[j] = al->min(j); + stack.push_back(static_cast(nonpar.size())); + nonpar.push_back(j); + dims.push_back(std::make_pair(al->min(j), al->max(j))); + newaccess.push_back(aa->idx()[j]); + } + } + if (stack.empty()) { + bool success; + KeepAlive ka; + { + GCLock lock; + ka = eval_arrayaccess(env, al, idx, success); + if (!success && env.in_maybe_partial == 0) { + ResultUndefinedError e(env, al->loc(), "array access out of bounds"); + } + } + ees.push_back(EE(NULL, constants().boollit(success))); + ees.push_back(EE(NULL, eev.b())); + if (aa->type().isbool() && !aa->type().isopt()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ees.push_back(EE(NULL, ka())); + ret.r = conj(env, r, ctx, ees); + } else { + ret.b = conj(env, b, ctx, ees); + ret.r = bind(env, ctx, r, ka()); + } + return ret; + } + while (!stack.empty()) { + int cur = stack.back(); + if (cur == nonpar.size() - 1) { + stack.pop_back(); + for (int i = al->min(nonpar[cur]); i <= al->max(nonpar[cur]); i++) { + idx[nonpar[cur]] = i; + bool success; + GCLock lock; + Expression* al_idx = eval_arrayaccess(env, al, idx, success); + if (!success) { + if (env.in_maybe_partial == 0) { + ResultUndefinedError e(env, al->loc(), "array access out of bounds"); + } + ees.push_back(EE(NULL, constants().lit_false)); + ees.push_back(EE(NULL, eev.b())); + if (aa->type().isbool() && !aa->type().isopt()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = conj(env, r, ctx, ees); + } else { + ret.b = conj(env, b, ctx, ees); + ret.r = bind(env, ctx, r, al_idx); + } + return ret; + } + elems.push_back(al_idx); + } + } else { + if (idx[nonpar[cur]].toInt() == al->max(nonpar[cur])) { + idx[nonpar[cur]] = al->min(nonpar[cur]); + stack.pop_back(); + } else { + idx[nonpar[cur]]++; + for (unsigned int j = cur + 1; j < nonpar.size(); j++) stack.push_back(j); + } + } + } + std::vector elems_e(elems.size()); + for (unsigned int i = 0; i < elems.size(); i++) elems_e[i] = elems[i](); + { + GCLock lock; + Expression* newal = new ArrayLit(al->loc(), elems_e, dims); + Type t = al->type(); + t.dim(static_cast(dims.size())); + newal->type(t); + eev.r = newal; + ArrayAccess* n_aa = new ArrayAccess(aa->loc(), newal, newaccess); + n_aa->type(aa->type()); + aa = n_aa; + aa_ka = aa; + } + } + } + + if (aa->idx().size() == 1 && aa->idx()[0]->isa()) { + ArrayAccess* aa_inner = aa->idx()[0]->cast(); + ArrayLit* al; + if (eev.r()->isa()) { + al = eev.r()->cast(); + } else { + Id* id = eev.r()->cast(); + if (id->decl() == NULL) { + throw InternalError("undefined identifier"); + } + if (id->decl()->e() == NULL) { + throw InternalError("array without initialiser not supported"); + } + al = follow_id(id)->cast(); + } + if (aa_inner->v()->type().ispar()) { + KeepAlive ka_al_inner = flat_cv_exp(env, ctx, aa_inner->v()); + ArrayLit* al_inner = ka_al_inner()->cast(); + std::vector composed_e(al_inner->size()); + for (unsigned int i = 0; i < al_inner->size(); i++) { + GCLock lock; + IntVal inner_idx = eval_int(env, (*al_inner)[i]); + if (inner_idx < al->min(0) || inner_idx > al->max(0)) goto flatten_arrayaccess; + composed_e[i] = (*al)[static_cast(inner_idx.toInt()) - al->min(0)]; + } + std::vector > dims(al_inner->dims()); + for (int i = 0; i < al_inner->dims(); i++) { + dims[i] = std::make_pair(al_inner->min(i), al_inner->max(i)); + } + { + GCLock lock; + Expression* newal = new ArrayLit(al->loc(), composed_e, dims); + Type t = al->type(); + t.dim(static_cast(dims.size())); + newal->type(t); + eev.r = newal; + ArrayAccess* n_aa = new ArrayAccess(aa->loc(), newal, aa_inner->idx()); + n_aa->type(aa->type()); + aa = n_aa; + aa_ka = aa; + goto start_flatten_arrayaccess; + } + } + } +flatten_arrayaccess: + Ctx dimctx = ctx; + dimctx.neg = false; + for (unsigned int i = 0; i < aa->idx().size(); i++) { + Expression* tmp = follow_id_to_decl(aa->idx()[i]); + if (VarDecl* vd = tmp->dyn_cast()) tmp = vd->id(); + ees.push_back(flat_exp(env, dimctx, tmp, NULL, NULL)); + } + ees.push_back(EE(NULL, eev.b())); + + bool parAccess = true; + for (unsigned int i = 0; i < aa->idx().size(); i++) { + if (!ees[i].r()->type().ispar()) { + parAccess = false; + break; + } + } + + if (parAccess) { + ArrayLit* al; + if (eev.r()->isa()) { + al = eev.r()->cast(); + } else { + Id* id = eev.r()->cast(); + if (id->decl() == NULL) { + throw InternalError("undefined identifier"); + } + if (id->decl()->e() == NULL) { + throw InternalError("array without initialiser not supported"); + } + al = follow_id(id)->cast(); + } + KeepAlive ka; + bool success; + { + GCLock lock; + std::vector dims(aa->idx().size()); + for (unsigned int i = aa->idx().size(); i--;) dims[i] = eval_int(env, ees[i].r()); + ka = eval_arrayaccess(env, al, dims, success); + } + if (!success && env.in_maybe_partial == 0) { + ResultUndefinedError e(env, al->loc(), "array access out of bounds"); + } + ees.push_back(EE(NULL, constants().boollit(success))); + if (aa->type().isbool() && !aa->type().isopt()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ees.push_back(EE(NULL, ka())); + ret.r = conj(env, r, ctx, ees); + } else { + ret.b = conj(env, b, ctx, ees); + ret.r = bind(env, ctx, r, ka()); + } + } else { + std::vector args(aa->idx().size() + 1); + for (unsigned int i = aa->idx().size(); i--;) args[i] = ees[i].r(); + args[aa->idx().size()] = eev.r(); + KeepAlive ka; + { + GCLock lock; + Call* cc = new Call(e->loc().introduce(), constants().ids.element, args); + cc->type(aa->type()); + FunctionI* fi = env.model->matchFn(env, cc->id(), args, false); + if (fi == NULL) { + throw FlatteningError(env, cc->loc(), "cannot find matching declaration"); + } + assert(fi); + assert(env.isSubtype(fi->rtype(env, args, false), cc->type(), false)); + cc->decl(fi); + ka = cc; + } + Ctx elemctx = ctx; + elemctx.neg = false; + EE ee = flat_exp(env, elemctx, ka(), NULL, NULL); + ees.push_back(ee); + if (aa->type().isbool() && !aa->type().isopt()) { + ee.b = ee.r; + ees.push_back(ee); + ret.r = conj(env, r, ctx, ees); + ret.b = bind(env, ctx, b, constants().boollit(!ctx.neg)); + } else { + ret.r = bind(env, ctx, r, ee.r()); + ret.b = conj(env, b, ctx, ees); + } + } + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_arraylit.cpp b/lib/flatten/flatten_arraylit.cpp new file mode 100644 index 0000000..b0ccd1e --- /dev/null +++ b/lib/flatten/flatten_arraylit.cpp @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_arraylit(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + ArrayLit* al = e->cast(); + if (al->flat()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, Ctx(), r, al); + } else { + std::vector elems_ee(al->size()); + for (unsigned int i = al->size(); i--;) elems_ee[i] = flat_exp(env, ctx, (*al)[i], NULL, NULL); + std::vector elems(elems_ee.size()); + for (unsigned int i = static_cast(elems.size()); i--;) elems[i] = elems_ee[i].r(); + std::vector > dims(al->dims()); + for (unsigned int i = al->dims(); i--;) dims[i] = std::pair(al->min(i), al->max(i)); + KeepAlive ka; + { + GCLock lock; + ArrayLit* alr = new ArrayLit(Location().introduce(), elems, dims); + alr->type(al->type()); + alr->flat(true); + ka = alr; + } + ret.b = conj(env, b, Ctx(), elems_ee); + ret.r = bind(env, Ctx(), r, ka()); + } + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_binop.cpp b/lib/flatten/flatten_binop.cpp new file mode 100644 index 0000000..0d4f3cf --- /dev/null +++ b/lib/flatten/flatten_binop.cpp @@ -0,0 +1,1536 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +ASTString opToBuiltin(BinOp* op, BinOpType bot) { + std::string builtin; + if (op->rhs()->type().isint()) { + switch (bot) { + case BOT_PLUS: + return constants().ids.int_.plus; + case BOT_MINUS: + return constants().ids.int_.minus; + case BOT_MULT: + return constants().ids.int_.times; + case BOT_POW: + return constants().ids.pow; + case BOT_IDIV: + return constants().ids.int_.div; + case BOT_MOD: + return constants().ids.int_.mod; + case BOT_LE: + return constants().ids.int_.lt; + case BOT_LQ: + return constants().ids.int_.le; + case BOT_GR: + return constants().ids.int_.gt; + case BOT_GQ: + return constants().ids.int_.ge; + case BOT_EQ: + return constants().ids.int_.eq; + case BOT_NQ: + return constants().ids.int_.ne; + default: + throw InternalError("not yet implemented"); + } + } else if (op->rhs()->type().isbool()) { + if (bot == BOT_EQ || bot == BOT_EQUIV) return constants().ids.bool_eq; + builtin = "bool_"; + } else if (op->rhs()->type().is_set()) { + builtin = "set_"; + } else if (op->rhs()->type().isfloat()) { + switch (bot) { + case BOT_PLUS: + return constants().ids.float_.plus; + case BOT_MINUS: + return constants().ids.float_.minus; + case BOT_MULT: + return constants().ids.float_.times; + case BOT_POW: + return constants().ids.pow; + case BOT_DIV: + return constants().ids.float_.div; + case BOT_MOD: + return constants().ids.float_.mod; + case BOT_LE: + return constants().ids.float_.lt; + case BOT_LQ: + return constants().ids.float_.le; + case BOT_GR: + return constants().ids.float_.gt; + case BOT_GQ: + return constants().ids.float_.ge; + case BOT_EQ: + return constants().ids.float_.eq; + case BOT_NQ: + return constants().ids.float_.ne; + default: + throw InternalError("not yet implemented"); + } + } else if (op->rhs()->type().isopt() && (bot == BOT_EQUIV || bot == BOT_EQ)) { + /// TODO: extend to all option type operators + switch (op->lhs()->type().bt()) { + case Type::BT_BOOL: + return constants().ids.bool_eq; + case Type::BT_FLOAT: + return constants().ids.float_.eq; + case Type::BT_INT: + if (op->lhs()->type().st() == Type::ST_PLAIN) + return constants().ids.int_.eq; + else + return constants().ids.set_eq; + default: + throw InternalError("not yet implemented"); + } + + } else { + throw InternalError(op->opToString().str() + " not yet implemented"); + } + switch (bot) { + case BOT_PLUS: + return builtin + "plus"; + case BOT_MINUS: + return builtin + "minus"; + case BOT_MULT: + return builtin + "times"; + case BOT_DIV: + return builtin + "div"; + case BOT_IDIV: + return builtin + "div"; + case BOT_MOD: + return builtin + "mod"; + case BOT_LE: + return builtin + "lt"; + case BOT_LQ: + return builtin + "le"; + case BOT_GR: + return builtin + "gt"; + case BOT_GQ: + return builtin + "ge"; + case BOT_EQ: + return builtin + "eq"; + case BOT_NQ: + return builtin + "ne"; + case BOT_IN: + return constants().ids.set_in; + case BOT_SUBSET: + return builtin + "subset"; + case BOT_SUPERSET: + return builtin + "superset"; + case BOT_UNION: + return builtin + "union"; + case BOT_DIFF: + return builtin + "diff"; + case BOT_SYMDIFF: + return builtin + "symdiff"; + case BOT_INTERSECT: + return builtin + "intersect"; + case BOT_PLUSPLUS: + case BOT_DOTDOT: + throw InternalError("not yet implemented"); + case BOT_EQUIV: + return builtin + "eq"; + case BOT_IMPL: + return builtin + "le"; + case BOT_RIMPL: + return builtin + "ge"; + case BOT_OR: + return builtin + "or"; + case BOT_AND: + return builtin + "and"; + case BOT_XOR: + return constants().ids.bool_xor; + default: + assert(false); + return ASTString(""); + } +} + +bool isReverseMap(BinOp* e) { return e->ann().contains(constants().ann.is_reverse_map); } + +template +void collectLinExps(EnvI& env, typename LinearTraits::Val c, Expression* exp, + std::vector::Val>& coeffs, + std::vector& vars, typename LinearTraits::Val& constval) { + typedef typename LinearTraits::Val Val; + struct StackItem { + Expression* e; + Val c; + StackItem(Expression* e0, Val c0) : e(e0), c(c0) {} + }; + std::vector stack; + stack.push_back(StackItem(exp, c)); + while (!stack.empty()) { + Expression* e = stack.back().e; + Val c = stack.back().c; + stack.pop_back(); + if (e == NULL) continue; + if (e->type().ispar()) { + constval += c * LinearTraits::eval(env, e); + } else if (Lit* l = e->dyn_cast()) { + constval += c * l->v(); + } else if (BinOp* bo = e->dyn_cast()) { + switch (bo->op()) { + case BOT_PLUS: + stack.push_back(StackItem(bo->lhs(), c)); + stack.push_back(StackItem(bo->rhs(), c)); + break; + case BOT_MINUS: + stack.push_back(StackItem(bo->lhs(), c)); + stack.push_back(StackItem(bo->rhs(), -c)); + break; + case BOT_MULT: + if (bo->lhs()->type().ispar()) { + stack.push_back(StackItem(bo->rhs(), c * LinearTraits::eval(env, bo->lhs()))); + } else if (bo->rhs()->type().ispar()) { + stack.push_back(StackItem(bo->lhs(), c * LinearTraits::eval(env, bo->rhs()))); + } else { + coeffs.push_back(c); + vars.push_back(e); + } + break; + case BOT_DIV: + if (bo->rhs()->isa() && bo->rhs()->cast()->v() == 1.0) { + stack.push_back(StackItem(bo->lhs(), c)); + } else { + coeffs.push_back(c); + vars.push_back(e); + } + break; + case BOT_IDIV: + if (bo->rhs()->isa() && bo->rhs()->cast()->v() == 1) { + stack.push_back(StackItem(bo->lhs(), c)); + } else { + coeffs.push_back(c); + vars.push_back(e); + } + break; + default: + coeffs.push_back(c); + vars.push_back(e); + break; + } + // } else if (Call* call = e->dyn_cast()) { + // /// TODO! Handle sum, lin_exp (maybe not that important?) + } else { + coeffs.push_back(c); + vars.push_back(e); + } + } +} + +template +KeepAlive mklinexp(EnvI& env, typename LinearTraits::Val c0, + typename LinearTraits::Val c1, Expression* e0, Expression* e1) { + typedef typename LinearTraits::Val Val; + GCLock lock; + + std::vector coeffs; + std::vector vars; + Val constval = 0; + collectLinExps(env, c0, e0, coeffs, vars, constval); + collectLinExps(env, c1, e1, coeffs, vars, constval); + simplify_lin(coeffs, vars, constval); + KeepAlive ka; + if (coeffs.size() == 0) { + ka = LinearTraits::newLit(constval); + } else if (coeffs.size() == 1 && coeffs[0] == 1 && constval == 0) { + ka = vars[0]; + } else { + std::vector coeffs_e(coeffs.size()); + for (unsigned int i = static_cast(coeffs.size()); i--;) { + if (!LinearTraits::finite(coeffs[i])) { + throw FlatteningError( + env, e0->loc(), + "unbounded coefficient in linear expression." + " Make sure variables involved in non-linear/logical expressions have finite bounds" + " in their definition or via constraints"); + } + coeffs_e[i] = LinearTraits::newLit(coeffs[i]); + } + std::vector vars_e(vars.size()); + for (unsigned int i = static_cast(vars.size()); i--;) vars_e[i] = vars[i](); + + std::vector args(3); + args[0] = new ArrayLit(e0->loc(), coeffs_e); + Type t = coeffs_e[0]->type(); + t.dim(1); + args[0]->type(t); + args[1] = new ArrayLit(e0->loc(), vars_e); + Type tt = vars_e[0]->type(); + tt.dim(1); + args[1]->type(tt); + args[2] = LinearTraits::newLit(constval); + Call* c = new Call(e0->loc().introduce(), constants().ids.lin_exp, args); + addPathAnnotation(env, c); + tt = args[1]->type(); + tt.dim(0); + c->decl(env.model->matchFn(env, c, false)); + if (c->decl() == NULL) { + throw FlatteningError(env, c->loc(), "cannot find matching declaration"); + } + c->type(c->decl()->rtype(env, args, false)); + ka = c; + } + assert(ka()); + return ka; +} + +Call* aggregateAndOrOps(EnvI& env, BinOp* bo, bool negateArgs, BinOpType bot) { + assert(bot == BOT_AND || bot == BOT_OR); + BinOpType negbot = (bot == BOT_AND ? BOT_OR : BOT_AND); + typedef std::pair arg_literal; + std::vector bo_args(2); + bo_args[0] = arg_literal(bo->lhs(), !negateArgs); + bo_args[1] = arg_literal(bo->rhs(), !negateArgs); + std::vector output_pos; + std::vector output_neg; + unsigned int processed = 0; + while (processed < bo_args.size()) { + BinOp* bo_arg = bo_args[processed].first->dyn_cast(); + UnOp* uo_arg = bo_args[processed].first->dyn_cast(); + bool positive = bo_args[processed].second; + if (bo_arg && positive && bo_arg->op() == bot) { + bo_args[processed].first = bo_arg->lhs(); + bo_args.push_back(arg_literal(bo_arg->rhs(), true)); + } else if (bo_arg && !positive && bo_arg->op() == negbot) { + bo_args[processed].first = bo_arg->lhs(); + bo_args.push_back(arg_literal(bo_arg->rhs(), false)); + } else if (uo_arg && !positive && uo_arg->op() == UOT_NOT) { + bo_args[processed].first = uo_arg->e(); + bo_args[processed].second = true; + } else if (bot == BOT_OR && uo_arg && positive && uo_arg->op() == UOT_NOT) { + output_neg.push_back(uo_arg->e()); + processed++; + } else { + if (positive) { + output_pos.push_back(bo_args[processed].first); + } else { + output_neg.push_back(bo_args[processed].first); + } + processed++; + } + } + Call* c; + std::vector c_args(1); + if (bot == BOT_AND) { + for (unsigned int i = 0; i < output_neg.size(); i++) { + UnOp* neg_arg = new UnOp(output_neg[i]->loc(), UOT_NOT, output_neg[i]); + neg_arg->type(output_neg[i]->type()); + output_pos.push_back(neg_arg); + } + ArrayLit* al = new ArrayLit(bo->loc().introduce(), output_pos); + Type al_t = bo->type(); + al_t.dim(1); + al->type(al_t); + env.annotateFromCallStack(al); + c_args[0] = al; + c = new Call(bo->loc().introduce(), + bot == BOT_AND ? constants().ids.forall : constants().ids.exists, c_args); + } else { + ArrayLit* al_pos = new ArrayLit(bo->loc().introduce(), output_pos); + Type al_t = bo->type(); + al_t.dim(1); + al_pos->type(al_t); + env.annotateFromCallStack(al_pos); + c_args[0] = al_pos; + if (output_neg.size() > 0) { + ArrayLit* al_neg = new ArrayLit(bo->loc().introduce(), output_neg); + al_neg->type(al_t); + env.annotateFromCallStack(al_neg); + c_args.push_back(al_neg); + } + c = new Call(bo->loc().introduce(), + output_neg.empty() ? constants().ids.exists : constants().ids.clause, c_args); + } + c->decl(env.model->matchFn(env, c, false)); + assert(c->decl()); + Type t = c->decl()->rtype(env, c_args, false); + t.cv(bo->type().cv()); + c->type(t); + return c; +} + +/// Return a lin_exp or id if \a e is a lin_exp or id +template +Expression* get_linexp(Expression* e) { + for (;;) { + if (e && e->eid() == Expression::E_ID && e != constants().absent) { + if (e->cast()->decl()->e()) { + e = e->cast()->decl()->e(); + } else { + break; + } + } else { + break; + } + } + if (e && (e->isa() || e->isa() || + (e->isa() && e->cast()->id() == constants().ids.lin_exp))) + return e; + return NULL; +} + +template +void flatten_linexp_binop(EnvI& env, Ctx ctx, VarDecl* r, VarDecl* b, EE& ret, Expression* le0, + Expression* le1, BinOpType& bot, bool doubleNeg, std::vector& ees, + std::vector& args, ASTString& callid) { + typedef typename LinearTraits::Val Val; + std::vector coeffv; + std::vector alv; + Val d = 0; + Expression* le[2] = {le0, le1}; + + Id* assignTo = NULL; + if (bot == BOT_EQ && ctx.b == C_ROOT) { + if (le0->isa()) { + assignTo = le0->cast(); + } else if (le1->isa()) { + assignTo = le1->cast(); + } + } + + for (unsigned int i = 0; i < 2; i++) { + Val sign = (i == 0 ? 1 : -1); + if (Lit* l = le[i]->dyn_cast()) { + try { + d += sign * l->v(); + } catch (ArithmeticError& e) { + throw EvalError(env, l->loc(), e.msg()); + } + } else if (le[i]->isa()) { + coeffv.push_back(sign); + alv.push_back(le[i]); + } else if (Call* sc = le[i]->dyn_cast()) { + GCLock lock; + ArrayLit* sc_coeff = eval_array_lit(env, sc->arg(0)); + ArrayLit* sc_al = eval_array_lit(env, sc->arg(1)); + try { + d += sign * LinearTraits::eval(env, sc->arg(2)); + for (unsigned int j = 0; j < sc_coeff->size(); j++) { + coeffv.push_back(sign * LinearTraits::eval(env, (*sc_coeff)[j])); + alv.push_back((*sc_al)[j]); + } + } catch (ArithmeticError& e) { + throw EvalError(env, sc->loc(), e.msg()); + } + + } else { + throw EvalError(env, le[i]->loc(), + "Internal error, unexpected expression inside linear expression"); + } + } + simplify_lin(coeffv, alv, d); + if (coeffv.size() == 0) { + bool result; + switch (bot) { + case BOT_LE: + result = (0 < -d); + break; + case BOT_LQ: + result = (0 <= -d); + break; + case BOT_GR: + result = (0 > -d); + break; + case BOT_GQ: + result = (0 >= -d); + break; + case BOT_EQ: + result = (0 == -d); + break; + case BOT_NQ: + result = (0 != -d); + break; + default: + assert(false); + break; + } + if (doubleNeg) result = !result; + ees[2].b = constants().boollit(result); + ret.r = conj(env, r, ctx, ees); + return; + } else if (coeffv.size() == 1 && std::abs(coeffv[0]) == 1) { + if (coeffv[0] == -1) { + switch (bot) { + case BOT_LE: + bot = BOT_GR; + break; + case BOT_LQ: + bot = BOT_GQ; + break; + case BOT_GR: + bot = BOT_LE; + break; + case BOT_GQ: + bot = BOT_LQ; + break; + default: + break; + } + } else { + d = -d; + } + typename LinearTraits::Bounds ib = LinearTraits::compute_bounds(env, alv[0]()); + if (ib.valid) { + bool failed = false; + bool subsumed = false; + switch (bot) { + case BOT_LE: + subsumed = ib.u < d; + failed = ib.l >= d; + break; + case BOT_LQ: + subsumed = ib.u <= d; + failed = ib.l > d; + break; + case BOT_GR: + subsumed = ib.l > d; + failed = ib.u <= d; + break; + case BOT_GQ: + subsumed = ib.l >= d; + failed = ib.u < d; + break; + case BOT_EQ: + subsumed = ib.l == d && ib.u == d; + failed = ib.u < d || ib.l > d; + break; + case BOT_NQ: + subsumed = ib.u < d || ib.l > d; + failed = ib.l == d && ib.u == d; + break; + default: + break; + } + if (doubleNeg) { + std::swap(subsumed, failed); + } + if (subsumed) { + ees[2].b = constants().lit_true; + ret.r = conj(env, r, ctx, ees); + return; + } else if (failed) { + ees[2].b = constants().lit_false; + ret.r = conj(env, r, ctx, ees); + return; + } + } + + if (ctx.b == C_ROOT && alv[0]()->isa() && bot == BOT_EQ) { + GCLock lock; + VarDecl* vd = alv[0]()->cast()->decl(); + if (vd->ti()->domain()) { + typename LinearTraits::Domain domain = + LinearTraits::eval_domain(env, vd->ti()->domain()); + if (LinearTraits::domain_contains(domain, d)) { + if (!LinearTraits::domain_equals(domain, d)) { + // vd->ti()->setComputedDomain(false); + // vd->ti()->domain(LinearTraits::new_domain(d)); + setComputedDomain(env, vd, LinearTraits::new_domain(d), false); + } + ret.r = bind(env, ctx, r, constants().lit_true); + } else { + ret.r = bind(env, ctx, r, constants().lit_false); + } + } else { + // vd->ti()->setComputedDomain(false); + // vd->ti()->domain(LinearTraits::new_domain(d)); + setComputedDomain(env, vd, LinearTraits::new_domain(d), false); + ret.r = bind(env, ctx, r, constants().lit_true); + } + } else { + GCLock lock; + Expression* e0; + Expression* e1; + BinOpType old_bot = bot; + Val old_d = d; + switch (bot) { + case BOT_LE: + e0 = alv[0](); + if (e0->type().isint()) { + d--; + bot = BOT_LQ; + } + e1 = LinearTraits::newLit(d); + break; + case BOT_GR: + e1 = alv[0](); + if (e1->type().isint()) { + d++; + bot = BOT_LQ; + } else { + bot = BOT_LE; + } + e0 = LinearTraits::newLit(d); + break; + case BOT_GQ: + e0 = LinearTraits::newLit(d); + e1 = alv[0](); + bot = BOT_LQ; + break; + default: + e0 = alv[0](); + e1 = LinearTraits::newLit(d); + } + if (ctx.b == C_ROOT && alv[0]()->isa() && alv[0]()->cast()->decl()->ti()->domain()) { + VarDecl* vd = alv[0]()->cast()->decl(); + typename LinearTraits::Domain domain = + LinearTraits::eval_domain(env, vd->ti()->domain()); + typename LinearTraits::Domain ndomain = + LinearTraits::limit_domain(old_bot, domain, old_d); + if (domain && ndomain) { + if (LinearTraits::domain_empty(ndomain)) { + ret.r = bind(env, ctx, r, constants().lit_false); + return; + } else if (!LinearTraits::domain_equals(domain, ndomain)) { + ret.r = bind(env, ctx, r, constants().lit_true); + // vd->ti()->setComputedDomain(false); + // vd->ti()->domain(LinearTraits::new_domain(ndomain)); + setComputedDomain(env, vd, LinearTraits::new_domain(ndomain), false); + + if (r == constants().var_true) { + BinOp* bo = new BinOp(Location().introduce(), e0, bot, e1); + bo->type(Type::varbool()); + std::vector boargs(2); + boargs[0] = e0; + boargs[1] = e1; + Call* c = new Call(Location(), opToBuiltin(bo, bot), boargs); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + EnvI::CSEMap::iterator it = env.cse_map_find(c); + if (it != env.cse_map_end()) { + if (Id* ident = it->second.r()->template dyn_cast()) { + bind(env, Ctx(), ident->decl(), constants().lit_true); + it->second.r = constants().lit_true; + } + if (Id* ident = it->second.b()->template dyn_cast()) { + bind(env, Ctx(), ident->decl(), constants().lit_true); + it->second.b = constants().lit_true; + } + } + } + } + return; + } + } + args.push_back(e0); + args.push_back(e1); + } + } else if (bot == BOT_EQ && coeffv.size() == 2 && coeffv[0] == -coeffv[1] && d == 0) { + Id* id0 = alv[0]()->cast(); + Id* id1 = alv[1]()->cast(); + if (ctx.b == C_ROOT && r == constants().var_true && + (id0->decl()->e() == NULL || id1->decl()->e() == NULL)) { + if (id0->decl()->e()) + (void)bind(env, ctx, id1->decl(), id0); + else + (void)bind(env, ctx, id0->decl(), id1); + } else { + callid = LinearTraits::id_eq(); + args.push_back(alv[0]()); + args.push_back(alv[1]()); + } + } else { + GCLock lock; + if (assignTo != NULL) { + Val resultCoeff = 0; + typename LinearTraits::Bounds bounds(d, d, true); + for (unsigned int i = static_cast(coeffv.size()); i--;) { + if (alv[i]() == assignTo) { + resultCoeff = coeffv[i]; + continue; + } + typename LinearTraits::Bounds b = LinearTraits::compute_bounds(env, alv[i]()); + + if (b.valid && LinearTraits::finite(b)) { + if (coeffv[i] > 0) { + bounds.l += coeffv[i] * b.l; + bounds.u += coeffv[i] * b.u; + } else { + bounds.l += coeffv[i] * b.u; + bounds.u += coeffv[i] * b.l; + } + } else { + bounds.valid = false; + break; + } + } + if (bounds.valid && resultCoeff != 0) { + if (resultCoeff < 0) { + bounds.l = LinearTraits::floor_div(bounds.l, -resultCoeff); + bounds.u = LinearTraits::ceil_div(bounds.u, -resultCoeff); + } else { + Val bl = bounds.l; + bounds.l = LinearTraits::ceil_div(bounds.u, -resultCoeff); + bounds.u = LinearTraits::floor_div(bl, -resultCoeff); + } + VarDecl* vd = assignTo->decl(); + if (vd->ti()->domain()) { + typename LinearTraits::Domain domain = + LinearTraits::eval_domain(env, vd->ti()->domain()); + if (LinearTraits::domain_intersects(domain, bounds.l, bounds.u)) { + typename LinearTraits::Domain new_domain = + LinearTraits::intersect_domain(domain, bounds.l, bounds.u); + if (!LinearTraits::domain_equals(domain, new_domain)) { + // vd->ti()->setComputedDomain(false); + // vd->ti()->domain(LinearTraits::new_domain(new_domain)); + setComputedDomain(env, vd, LinearTraits::new_domain(new_domain), false); + } + } else { + ret.r = bind(env, ctx, r, constants().lit_false); + } + } else { + // vd->ti()->setComputedDomain(true); + // vd->ti()->domain(LinearTraits::new_domain(bounds.l,bounds.u)); + setComputedDomain(env, vd, LinearTraits::new_domain(bounds.l, bounds.u), true); + } + } + } + + int coeff_sign; + LinearTraits::constructLinBuiltin(bot, callid, coeff_sign, d); + std::vector coeff_ev(coeffv.size()); + for (unsigned int i = static_cast(coeff_ev.size()); i--;) + coeff_ev[i] = LinearTraits::newLit(coeff_sign * coeffv[i]); + ArrayLit* ncoeff = new ArrayLit(Location().introduce(), coeff_ev); + Type t = coeff_ev[0]->type(); + t.dim(1); + ncoeff->type(t); + args.push_back(ncoeff); + std::vector alv_e(alv.size()); + Type tt = alv[0]()->type(); + tt.dim(1); + for (unsigned int i = static_cast(alv.size()); i--;) { + if (alv[i]()->type().isvar()) tt.ti(Type::TI_VAR); + alv_e[i] = alv[i](); + } + ArrayLit* nal = new ArrayLit(Location().introduce(), alv_e); + nal->type(tt); + args.push_back(nal); + Lit* il = LinearTraits::newLit(-d); + args.push_back(il); + } +} + +EE flatten_binop(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + BinOp* bo = e->cast(); + if (isReverseMap(bo)) { + CallArgItem cai(env); + Id* id = bo->lhs()->dyn_cast(); + if (id == NULL) + throw EvalError(env, bo->lhs()->loc(), "Reverse mappers are only defined for identifiers"); + if (bo->op() != BOT_EQ && bo->op() != BOT_EQUIV) + throw EvalError(env, bo->loc(), "Reverse mappers have to use `=` as the operator"); + Call* c = bo->rhs()->dyn_cast(); + if (c == NULL) + throw EvalError(env, bo->rhs()->loc(), "Reverse mappers require call on right hand side"); + + std::vector args(c->n_args()); + for (unsigned int i = 0; i < c->n_args(); i++) { + Id* idi = c->arg(i)->dyn_cast(); + if (idi == NULL) + throw EvalError(env, c->arg(i)->loc(), + "Reverse mapper calls require identifiers as arguments"); + EE ee = flat_exp(env, Ctx(), idi, NULL, constants().var_true); + args[i] = ee.r(); + } + + EE ee = flat_exp(env, Ctx(), id, NULL, constants().var_true); + + GCLock lock; + Call* revMap = new Call(Location().introduce(), c->id(), args); + + args.push_back(ee.r()); + Call* keepAlive = new Call(Location().introduce(), constants().var_redef->id(), args); + keepAlive->type(Type::varbool()); + keepAlive->decl(constants().var_redef); + ret = flat_exp(env, Ctx(), keepAlive, constants().var_true, constants().var_true); + + if (ee.r()->isa()) { + env.reverseMappers.insert(ee.r()->cast(), revMap); + } + return ret; + } + if ((bo->op() == BOT_EQ || bo->op() == BOT_EQUIV) && + (bo->lhs() == constants().absent || bo->rhs() == constants().absent)) { + GCLock lock; + std::vector args(1); + args[0] = bo->lhs() == constants().absent ? bo->rhs() : bo->lhs(); + if (args[0] != constants().absent) { + Call* cr = new Call(bo->loc().introduce(), "absent", args); + cr->decl(env.model->matchFn(env, cr, false)); + cr->type(cr->decl()->rtype(env, args, false)); + ret = flat_exp(env, ctx, cr, r, b); + } else { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, constants().lit_true); + } + return ret; + } + Ctx ctx0 = ctx; + ctx0.neg = false; + Ctx ctx1 = ctx; + ctx1.neg = false; + BinOpType bot = bo->op(); + if (bo->lhs()->type().isbool()) { + switch (bot) { + case BOT_EQ: + bot = BOT_EQUIV; + break; + case BOT_NQ: + bot = BOT_XOR; + break; + case BOT_LQ: + bot = BOT_IMPL; + break; + case BOT_GQ: + bot = BOT_RIMPL; + break; + default: + break; + } + } + bool negArgs = false; + bool doubleNeg = false; + if (ctx.neg) { + switch (bot) { + case BOT_AND: + ctx.b = -ctx.b; + ctx.neg = false; + negArgs = true; + bot = BOT_OR; + break; + case BOT_OR: + ctx.b = -ctx.b; + ctx.neg = false; + negArgs = true; + bot = BOT_AND; + break; + default: + break; + } + } + Expression* boe0 = bo->lhs(); + Expression* boe1 = bo->rhs(); + bool isBuiltin = bo->decl() == NULL || bo->decl()->e() == NULL; + switch (bot) { + case BOT_PLUS: + if (isBuiltin) { + KeepAlive ka; + if (boe0->type().isint()) { + ka = mklinexp(env, 1, 1, boe0, boe1); + } else { + ka = mklinexp(env, 1.0, 1.0, boe0, boe1); + } + ret = flat_exp(env, ctx, ka(), r, b); + break; + } + case BOT_MINUS: + if (isBuiltin) { + KeepAlive ka; + if (boe0->type().isint()) { + ka = mklinexp(env, 1, -1, boe0, boe1); + } else { + ka = mklinexp(env, 1.0, -1.0, boe0, boe1); + } + ret = flat_exp(env, ctx, ka(), r, b); + break; + } + case BOT_MULT: + case BOT_IDIV: + case BOT_MOD: + case BOT_POW: + case BOT_DIV: + case BOT_UNION: + case BOT_DIFF: + case BOT_SYMDIFF: + case BOT_INTERSECT: + case BOT_DOTDOT: { + assert(!ctx0.neg); + assert(!ctx1.neg); + EE e0 = flat_exp(env, ctx0, boe0, NULL, b); + EE e1 = flat_exp(env, ctx1, boe1, NULL, b); + + if (e0.r()->type().ispar() && e1.r()->type().ispar()) { + GCLock lock; + BinOp* parbo = new BinOp(bo->loc(), e0.r(), bo->op(), e1.r()); + Type tt = bo->type(); + tt.ti(Type::TI_PAR); + parbo->type(tt); + try { + Expression* res = eval_par(env, parbo); + assert(!res->type().isunknown()); + ret.r = bind(env, ctx, r, res); + std::vector ees(2); + ees[0].b = e0.b; + ees[1].b = e1.b; + ret.b = conj(env, b, Ctx(), ees); + } catch (ResultUndefinedError&) { + ret.r = createDummyValue(env, e->type()); + ret.b = bind(env, Ctx(), b, constants().lit_false); + } + break; + } + + if (isBuiltin && bot == BOT_MULT) { + Expression* e0r = e0.r(); + Expression* e1r = e1.r(); + if (e0r->type().ispar()) std::swap(e0r, e1r); + if (e1r->type().ispar() && e1r->type().isint()) { + IntVal coeff = eval_int(env, e1r); + KeepAlive ka = mklinexp(env, coeff, 0, e0r, NULL); + ret = flat_exp(env, ctx, ka(), r, b); + break; + } else if (e1r->type().ispar() && e1r->type().isfloat()) { + FloatVal coeff = eval_float(env, e1r); + KeepAlive ka = mklinexp(env, coeff, 0.0, e0r, NULL); + ret = flat_exp(env, ctx, ka(), r, b); + break; + } + } else if (isBuiltin && (bot == BOT_DIV || bot == BOT_IDIV)) { + Expression* e0r = e0.r(); + Expression* e1r = e1.r(); + if (e1r->type().ispar() && e1r->type().isint()) { + IntVal coeff = eval_int(env, e1r); + if (coeff == 1) { + ret = flat_exp(env, ctx, e0r, r, b); + break; + } + } else if (e1r->type().ispar() && e1r->type().isfloat()) { + FloatVal coeff = eval_float(env, e1r); + if (coeff == 1.0) { + ret = flat_exp(env, ctx, e0r, r, b); + break; + } else { + KeepAlive ka = mklinexp(env, 1.0 / coeff, 0.0, e0r, NULL); + ret = flat_exp(env, ctx, ka(), r, b); + break; + } + } + } + + GC::lock(); + std::vector args(2); + args[0] = e0.r(); + args[1] = e1.r(); + Call* cc; + if (bo->decl()) { + cc = new Call(bo->loc().introduce(), bo->opToString(), args); + } else { + cc = new Call(bo->loc().introduce(), opToBuiltin(bo, bot), args); + } + cc->type(bo->type()); + EnvI::CSEMap::iterator cit; + if ((cit = env.cse_map_find(cc)) != env.cse_map_end()) { + ret.b = bind(env, Ctx(), b, env.ignorePartial ? constants().lit_true : cit->second.b()); + ret.r = bind(env, ctx, r, cit->second.r()); + } else { + if (FunctionI* fi = env.model->matchFn(env, cc->id(), args, false)) { + assert(cc->type() == fi->rtype(env, args, false)); + cc->decl(fi); + cc->type(cc->decl()->rtype(env, args, false)); + KeepAlive ka(cc); + GC::unlock(); + EE ee = flat_exp(env, ctx, cc, r, NULL); + GC::lock(); + ret.r = ee.r; + std::vector ees(3); + ees[0].b = e0.b; + ees[1].b = e1.b; + ees[2].b = ee.b; + ret.b = conj(env, b, Ctx(), ees); + } else { + addPathAnnotation(env, cc); + ret.r = bind(env, ctx, r, cc); + std::vector ees(2); + ees[0].b = e0.b; + ees[1].b = e1.b; + ret.b = conj(env, b, Ctx(), ees); + if (!ctx.neg) env.cse_map_insert(cc, ret); + } + } + } + GC::unlock(); + break; + + case BOT_AND: { + if (r == constants().var_true) { + Ctx nctx; + nctx.neg = negArgs; + nctx.b = negArgs ? C_NEG : C_ROOT; + std::vector todo; + todo.push_back(boe1); + todo.push_back(boe0); + while (!todo.empty()) { + Expression* e_todo = todo.back(); + todo.pop_back(); + BinOp* e_bo = e_todo->dyn_cast(); + if (e_bo && e_bo->op() == BOT_AND) { + todo.push_back(e_bo->rhs()); + todo.push_back(e_bo->lhs()); + } else { + (void)flat_exp(env, nctx, e_todo, constants().var_true, constants().var_true); + } + } + ret.r = bind(env, ctx, r, constants().lit_true); + break; + } else { + GC::lock(); + Call* c = aggregateAndOrOps(env, bo, negArgs, bot); + KeepAlive ka(c); + GC::unlock(); + ret = flat_exp(env, ctx, c, r, b); + if (Id* id = ret.r()->dyn_cast()) { + addCtxAnn(id->decl(), ctx.b); + } + } + break; + } + case BOT_OR: { + GC::lock(); + Call* c = aggregateAndOrOps(env, bo, negArgs, bot); + KeepAlive ka(c); + GC::unlock(); + ret = flat_exp(env, ctx, c, r, b); + if (Id* id = ret.r()->dyn_cast()) { + addCtxAnn(id->decl(), ctx.b); + } + } break; + case BOT_RIMPL: { + std::swap(boe0, boe1); + } + // fall through + case BOT_IMPL: { + if (ctx.b == C_ROOT && r == constants().var_true && boe0->type().ispar()) { + bool b; + { + GCLock lock; + b = eval_bool(env, boe0); + } + if (b) { + Ctx nctx = ctx; + nctx.neg = negArgs; + nctx.b = negArgs ? C_NEG : C_ROOT; + ret = flat_exp(env, nctx, boe1, constants().var_true, constants().var_true); + } else { + Ctx nctx = ctx; + nctx.neg = negArgs; + nctx.b = negArgs ? C_NEG : C_ROOT; + ret = + flat_exp(env, nctx, constants().lit_true, constants().var_true, constants().var_true); + } + break; + } + if (ctx.b == C_ROOT && r == constants().var_true && boe1->type().ispar()) { + bool b; + { + GCLock lock; + b = eval_bool(env, boe1); + } + if (b) { + Ctx nctx = ctx; + nctx.neg = negArgs; + nctx.b = negArgs ? C_NEG : C_ROOT; + ret = + flat_exp(env, nctx, constants().lit_true, constants().var_true, constants().var_true); + break; + } else { + Ctx nctx = ctx; + nctx.neg = !negArgs; + nctx.b = !negArgs ? C_NEG : C_ROOT; + ret = flat_exp(env, nctx, boe0, constants().var_true, constants().var_true); + break; + } + } + GC::lock(); + std::vector args; + ASTString id; + if (ctx.neg) { + std::vector bo_args(2); + bo_args[0] = boe0; + bo_args[1] = new UnOp(bo->loc(), UOT_NOT, boe1); + bo_args[1]->type(boe1->type()); + id = constants().ids.forall; + args.push_back(new ArrayLit(bo->loc(), bo_args)); + args[0]->type(Type::varbool(1)); + } else { + std::vector clause_pos(1); + clause_pos[0] = boe1; + std::vector clause_neg(1); + clause_neg[0] = boe0; + args.push_back(new ArrayLit(boe1->loc().introduce(), clause_pos)); + Type t0 = boe1->type(); + t0.dim(1); + args[0]->type(t0); + args.push_back(new ArrayLit(boe0->loc().introduce(), clause_neg)); + Type t1 = boe0->type(); + t1.dim(1); + args[1]->type(t1); + id = constants().ids.clause; + } + ctx.neg = false; + Call* c = new Call(bo->loc().introduce(), id, args); + c->decl(env.model->matchFn(env, c, false)); + if (c->decl() == NULL) { + throw FlatteningError(env, c->loc(), "cannot find matching declaration"); + } + c->type(c->decl()->rtype(env, args, false)); + KeepAlive ka(c); + GC::unlock(); + ret = flat_exp(env, ctx, c, r, b); + if (Id* id = ret.r()->dyn_cast()) { + addCtxAnn(id->decl(), ctx.b); + } + } break; + case BOT_EQUIV: + if (ctx.neg) { + ctx.neg = false; + ctx.b = -ctx.b; + bot = BOT_XOR; + ctx0.b = ctx1.b = C_MIX; + goto flatten_bool_op; + } else { + if (!boe1->type().isopt() && istrue(env, boe0)) { + return flat_exp(env, ctx, boe1, r, b); + } + if (!boe0->type().isopt() && istrue(env, boe1)) { + return flat_exp(env, ctx, boe0, r, b); + } + if (r && r == constants().var_true) { + if (boe1->type().ispar() || boe1->isa()) std::swap(boe0, boe1); + if (istrue(env, boe0)) { + return flat_exp(env, ctx1, boe1, r, b); + } else if (isfalse(env, boe0)) { + ctx1.neg = true; + ctx1.b = -ctx1.b; + return flat_exp(env, ctx1, boe1, r, b); + } else { + ctx0.b = C_MIX; + EE e0 = flat_exp(env, ctx0, boe0, NULL, NULL); + if (istrue(env, e0.r())) { + return flat_exp(env, ctx1, boe1, r, b); + } else if (isfalse(env, e0.r())) { + ctx1.neg = true; + ctx1.b = -ctx1.b; + return flat_exp(env, ctx1, boe1, r, b); + } else { + Id* id = e0.r()->cast(); + ctx1.b = C_MIX; + (void)flat_exp(env, ctx1, boe1, id->decl(), constants().var_true); + addCtxAnn(id->decl(), ctx1.b); + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, Ctx(), r, constants().lit_true); + } + } + break; + } else { + ctx0.b = ctx1.b = C_MIX; + goto flatten_bool_op; + } + } + case BOT_XOR: + if (ctx.neg) { + ctx.neg = false; + ctx.b = -ctx.b; + bot = BOT_EQUIV; + } + ctx0.b = ctx1.b = C_MIX; + goto flatten_bool_op; + case BOT_LE: + if (ctx.neg) { + doubleNeg = true; + bot = BOT_GQ; + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = +ctx0.b; + ctx1.b = -ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = +ctx0.b; + ctx1.i = -ctx1.b; + } + } else { + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = -ctx0.b; + ctx1.b = +ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = -ctx0.b; + ctx1.i = +ctx1.b; + } + } + goto flatten_bool_op; + case BOT_LQ: + if (ctx.neg) { + doubleNeg = true; + bot = BOT_GR; + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = +ctx0.b; + ctx1.b = -ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = +ctx0.b; + ctx1.i = -ctx1.b; + } + } else { + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = -ctx0.b; + ctx1.b = +ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = -ctx0.b; + ctx1.i = +ctx1.b; + } + } + goto flatten_bool_op; + case BOT_GR: + if (ctx.neg) { + doubleNeg = true; + bot = BOT_LQ; + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = -ctx0.b; + ctx1.b = +ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = -ctx0.b; + ctx1.i = +ctx1.b; + } + } else { + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = +ctx0.b; + ctx1.b = -ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = +ctx0.b; + ctx1.i = -ctx1.b; + } + } + goto flatten_bool_op; + case BOT_GQ: + if (ctx.neg) { + doubleNeg = true; + bot = BOT_LE; + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = -ctx0.b; + ctx1.b = +ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = -ctx0.b; + ctx1.i = +ctx1.b; + } + } else { + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = +ctx0.b; + ctx1.b = -ctx1.b; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = +ctx0.b; + ctx1.i = -ctx1.b; + } + } + goto flatten_bool_op; + case BOT_EQ: + if (ctx.neg) { + doubleNeg = true; + bot = BOT_NQ; + } + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = ctx1.b = C_MIX; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = ctx1.i = C_MIX; + } + goto flatten_bool_op; + case BOT_NQ: + if (ctx.neg) { + doubleNeg = true; + bot = BOT_EQ; + } + if (boe0->type().bt() == Type::BT_BOOL) { + ctx0.b = ctx1.b = C_MIX; + } else if (boe0->type().bt() == Type::BT_INT) { + ctx0.i = ctx1.i = C_MIX; + } + goto flatten_bool_op; + case BOT_IN: + case BOT_SUBSET: + case BOT_SUPERSET: + ctx0.i = ctx1.i = C_MIX; + flatten_bool_op : { + bool inRootCtx = (ctx0.b == ctx1.b && ctx0.b == C_ROOT && b == constants().var_true); + EE e0 = flat_exp(env, ctx0, boe0, NULL, inRootCtx ? b : NULL); + EE e1 = flat_exp(env, ctx1, boe1, NULL, inRootCtx ? b : NULL); + + ret.b = bind(env, Ctx(), b, constants().lit_true); + + std::vector ees(3); + ees[0].b = e0.b; + ees[1].b = e1.b; + + if (isfalse(env, e0.b()) || isfalse(env, e1.b())) { + ees.resize(2); + ret.r = conj(env, r, ctx, ees); + break; + } + + if (e0.r()->type().ispar() && e1.r()->type().ispar()) { + GCLock lock; + BinOp* bo_par = new BinOp(e->loc(), e0.r(), bot, e1.r()); + std::vector args({e0.r(), e1.r()}); + bo_par->decl(env.model->matchFn(env, bo_par->opToString(), args, false)); + if (bo_par->decl() == NULL) { + throw FlatteningError(env, bo_par->loc(), "cannot find matching declaration"); + } + bo_par->type(Type::parbool()); + bool bo_val = eval_bool(env, bo_par); + if (doubleNeg) bo_val = !bo_val; + ees[2].b = constants().boollit(bo_val); + ret.r = conj(env, r, ctx, ees); + break; + } + + if (e0.r()->type().bt() == Type::BT_INT && e1.r()->type().ispar() && e0.r()->isa() && + (bot == BOT_IN || bot == BOT_SUBSET)) { + VarDecl* vd = e0.r()->cast()->decl(); + Id* ident = vd->id(); + if (ctx.b == C_ROOT && r == constants().var_true) { + if (vd->ti()->domain() == NULL) { + vd->ti()->domain(e1.r()); + } else { + GCLock lock; + IntSetVal* newdom = eval_intset(env, e1.r()); + while (ident != NULL) { + bool changeDom = false; + if (ident->decl()->ti()->domain()) { + IntSetVal* domain = eval_intset(env, ident->decl()->ti()->domain()); + IntSetRanges dr(domain); + IntSetRanges ibr(newdom); + Ranges::Inter i(dr, ibr); + IntSetVal* newibv = IntSetVal::ai(i); + if (domain->card() != newibv->card()) { + newdom = newibv; + changeDom = true; + } + } else { + changeDom = true; + } + if (ident->type().st() == Type::ST_PLAIN && newdom->size() == 0) { + env.fail(); + } else if (changeDom) { + // ident->decl()->ti()->setComputedDomain(false); + // ident->decl()->ti()->domain(new SetLit(Location().introduce(),newdom)); + setComputedDomain(env, ident->decl(), new SetLit(Location().introduce(), newdom), + false); + if (ident->decl()->e() == NULL && newdom->min() == newdom->max()) { + ident->decl()->e(IntLit::a(newdom->min())); + } + } + ident = ident->decl()->e() ? ident->decl()->e()->dyn_cast() : NULL; + } + } + ret.r = bind(env, ctx, r, constants().lit_true); + break; + } else if (vd->ti()->domain() != NULL) { + // check if current domain is already subsumed or falsified by this constraint + GCLock lock; + IntSetVal* check_dom = eval_intset(env, e1.r()); + IntSetVal* domain = eval_intset(env, ident->decl()->ti()->domain()); + { + IntSetRanges cdr(check_dom); + IntSetRanges dr(domain); + if (Ranges::subset(dr, cdr)) { + // the constraint is subsumed + ret.r = bind(env, ctx, r, constants().lit_true); + break; + } + } + if (vd->type().st() == Type::ST_PLAIN) { + // only for var int (for var set of int, subset can never fail because of the domain) + IntSetRanges cdr(check_dom); + IntSetRanges dr(domain); + if (Ranges::disjoint(cdr, dr)) { + // the constraint is false + ret.r = bind(env, ctx, r, constants().lit_false); + break; + } + } + } + } + + std::vector args; + ASTString callid; + + Expression* le0 = NULL; + Expression* le1 = NULL; + + if (boe0->type().isint() && !boe0->type().isopt() && bot != BOT_IN) { + le0 = get_linexp(e0.r()); + } else if (boe0->type().isfloat() && !boe0->type().isopt() && bot != BOT_IN) { + le0 = get_linexp(e0.r()); + } + if (le0) { + if (boe0->type().isint() && boe1->type().isint() && !boe1->type().isopt()) { + le1 = get_linexp(e1.r()); + } else if (boe0->type().isfloat() && boe1->type().isfloat() && !boe1->type().isopt()) { + le1 = get_linexp(e1.r()); + } + } + if (le1) { + if (boe0->type().isint()) { + flatten_linexp_binop(env, ctx, r, b, ret, le0, le1, bot, doubleNeg, ees, args, + callid); + } else { + flatten_linexp_binop(env, ctx, r, b, ret, le0, le1, bot, doubleNeg, ees, args, + callid); + } + } else { + if (bo->decl() == NULL) { + switch (bot) { + case BOT_GR: + std::swap(e0, e1); + bot = BOT_LE; + break; + case BOT_GQ: + std::swap(e0, e1); + bot = BOT_LQ; + break; + default: + break; + } + } + args.push_back(e0.r); + args.push_back(e1.r); + } + + if (args.size() > 0) { + GC::lock(); + + if (callid == "") { + if (bo->decl()) { + callid = bo->decl()->id(); + } else { + callid = opToBuiltin(bo, bot); + } + } + + std::vector args_e(args.size()); + for (unsigned int i = static_cast(args.size()); i--;) args_e[i] = args[i](); + Call* cc = new Call(e->loc().introduce(), callid, args_e); + cc->decl(env.model->matchFn(env, cc->id(), args_e, false)); + if (cc->decl() == NULL) { + throw FlatteningError(env, cc->loc(), "cannot find matching declaration"); + } + cc->type(cc->decl()->rtype(env, args_e, false)); + + // add defines_var annotation if applicable + Id* assignTo = NULL; + if (bot == BOT_EQ && ctx.b == C_ROOT) { + if (le0 && le0->isa()) { + assignTo = le0->cast(); + } else if (le1 && le1->isa()) { + assignTo = le1->cast(); + } + if (assignTo) { + makeDefinedVar(assignTo->decl()->flat(), cc); + } + } + + EnvI::CSEMap::iterator cit = env.cse_map_find(cc); + if (cit != env.cse_map_end()) { + ees[2].b = cit->second.r(); + if (doubleNeg) { + Type t = ees[2].b()->type(); + ees[2].b = new UnOp(Location().introduce(), UOT_NOT, ees[2].b()); + ees[2].b()->type(t); + } + if (Id* id = ees[2].b()->dyn_cast()) { + addCtxAnn(id->decl(), ctx.b); + } + ret.r = conj(env, r, ctx, ees); + GC::unlock(); + } else { + bool singleExp = true; + for (unsigned int i = 0; i < ees.size(); i++) { + if (!istrue(env, ees[i].b())) { + singleExp = false; + break; + } + } + KeepAlive ka(cc); + GC::unlock(); + if (singleExp) { + if (doubleNeg) { + ctx.b = -ctx.b; + ctx.neg = !ctx.neg; + } + ret.r = flat_exp(env, ctx, cc, r, NULL).r; + } else { + ees[2].b = flat_exp(env, Ctx(), cc, NULL, NULL).r; + if (doubleNeg) { + GCLock lock; + Type t = ees[2].b()->type(); + ees[2].b = new UnOp(Location().introduce(), UOT_NOT, ees[2].b()); + ees[2].b()->type(t); + } + if (Id* id = ees[2].b()->dyn_cast()) { + addCtxAnn(id->decl(), ctx.b); + } + ret.r = conj(env, r, ctx, ees); + } + } + } else { + ret.r = conj(env, r, ctx, ees); + } + } break; + + case BOT_PLUSPLUS: { + std::vector ee(2); + EE eev = flat_exp(env, ctx, boe0, NULL, NULL); + ee[0] = eev; + ArrayLit* al; + if (eev.r()->isa()) { + al = eev.r()->cast(); + } else { + Id* id = eev.r()->cast(); + if (id->decl() == NULL) { + throw InternalError("undefined identifier"); + } + if (id->decl()->e() == NULL) { + throw InternalError("array without initialiser not supported"); + } + al = follow_id(id)->cast(); + } + ArrayLit* al0 = al; + eev = flat_exp(env, ctx, boe1, NULL, NULL); + ee[1] = eev; + if (eev.r()->isa()) { + al = eev.r()->cast(); + } else { + Id* id = eev.r()->cast(); + if (id->decl() == NULL) { + throw InternalError("undefined identifier"); + } + if (id->decl()->e() == NULL) { + throw InternalError("array without initialiser not supported"); + } + al = follow_id(id)->cast(); + } + ArrayLit* al1 = al; + std::vector v(al0->size() + al1->size()); + for (unsigned int i = al0->size(); i--;) v[i] = (*al0)[i]; + for (unsigned int i = al1->size(); i--;) v[al0->size() + i] = (*al1)[i]; + GCLock lock; + ArrayLit* alret = new ArrayLit(e->loc(), v); + alret->type(e->type()); + ret.b = conj(env, b, Ctx(), ee); + ret.r = bind(env, ctx, r, alret); + } break; + } + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_call.cpp b/lib/flatten/flatten_call.cpp new file mode 100644 index 0000000..a3148cb --- /dev/null +++ b/lib/flatten/flatten_call.cpp @@ -0,0 +1,1050 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +std::vector toExpVec(std::vector& v) { + std::vector r(v.size()); + for (unsigned int i = static_cast(v.size()); i--;) r[i] = v[i](); + return r; +} + +bool isTotal(FunctionI* fi) { return fi->ann().contains(constants().ann.promise_total); } + +Call* same_call(EnvI& env, Expression* e, const ASTString& id) { + assert(GC::locked()); + Expression* ce = follow_id(e); + Call* c = Expression::dyn_cast(ce); + if (c) { + if (c->id() == id) { + return ce->cast(); + } else if (c->id() == constants().ids.int2float) { + Expression* i2f = follow_id(c->arg(0)); + Call* i2fc = Expression::dyn_cast(i2f); + if (i2fc && i2fc->id() == id && id == constants().ids.lin_exp) { + ArrayLit* coeffs = eval_array_lit(env, i2fc->arg(0)); + std::vector ncoeff_v(coeffs->size()); + for (unsigned int i = 0; i < coeffs->size(); i++) { + ncoeff_v[i] = FloatLit::a(eval_int(env, (*coeffs)[i])); + } + ArrayLit* ncoeff = new ArrayLit(coeffs->loc().introduce(), ncoeff_v); + ncoeff->type(Type::parfloat(1)); + ArrayLit* vars = eval_array_lit(env, i2fc->arg(1)); + std::vector n_vars_v(vars->size()); + for (unsigned int i = 0; i < vars->size(); i++) { + Call* f2i = + new Call((*vars)[i]->loc().introduce(), constants().ids.int2float, {(*vars)[i]}); + f2i->decl(env.model->matchFn(env, f2i, false)); + assert(f2i->decl()); + f2i->type(Type::varfloat()); + EE ee = flat_exp(env, Ctx(), f2i, NULL, constants().var_true); + n_vars_v[i] = ee.r(); + } + ArrayLit* nvars = new ArrayLit(vars->loc().introduce(), n_vars_v); + nvars->type(Type::varfloat(1)); + FloatVal c = eval_int(env, i2fc->arg(2)); + Call* nlinexp = new Call(i2fc->loc().introduce(), constants().ids.lin_exp, + {ncoeff, nvars, FloatLit::a(c)}); + nlinexp->decl(env.model->matchFn(env, nlinexp, false)); + assert(nlinexp->decl()); + nlinexp->type(Type::varfloat()); + return nlinexp; + } + } + } + return NULL; +} + +class CmpExp { +public: + bool operator()(const KeepAlive& i, const KeepAlive& j) const { + if (Expression::equal(i(), j())) return false; + return i() < j(); + } +}; + +bool remove_dups(std::vector& x, bool identity) { + for (unsigned int i = 0; i < x.size(); i++) { + x[i] = follow_id_to_value(x[i]()); + } + std::sort(x.begin(), x.end(), CmpExp()); + int ci = 0; + Expression* prev = NULL; + for (unsigned int i = 0; i < x.size(); i++) { + if (!Expression::equal(x[i](), prev)) { + prev = x[i](); + if (x[i]()->isa()) { + if (x[i]()->cast()->v() == identity) { + // skip + } else { + return true; + } + } else { + x[ci++] = x[i]; + } + } + } + x.resize(ci); + return false; +} +bool contains_dups(std::vector& x, std::vector& y) { + if (x.size() == 0 || y.size() == 0) return false; + unsigned int ix = 0; + unsigned int iy = 0; + for (;;) { + if (x[ix]() == y[iy]()) return true; + if (x[ix]() < y[iy]()) { + ix++; + } else { + iy++; + } + if (ix == x.size() || iy == y.size()) return false; + } +} + +template +void flatten_linexp_call(EnvI& env, Ctx ctx, Ctx nctx, ASTString& cid, Call* c, EE& ret, VarDecl* b, + VarDecl* r, std::vector& args_ee, std::vector& args) { + typedef typename LinearTraits::Val Val; + Expression* al_arg = (cid == constants().ids.sum ? args_ee[0].r() : args_ee[1].r()); + EE flat_al = flat_exp(env, nctx, al_arg, NULL, NULL); + ArrayLit* al = follow_id(flat_al.r())->template cast(); + KeepAlive al_ka = al; + if (al->dims() > 1) { + Type alt = al->type(); + alt.dim(1); + GCLock lock; + al = new ArrayLit(al->loc(), *al); + al->type(alt); + al_ka = al; + } + Val d = (cid == constants().ids.sum ? Val(0) : LinearTraits::eval(env, args_ee[2].r())); + + std::vector c_coeff(al->size()); + if (cid == constants().ids.sum) { + for (unsigned int i = al->size(); i--;) c_coeff[i] = 1; + } else { + EE flat_coeff = flat_exp(env, nctx, args_ee[0].r(), NULL, NULL); + ArrayLit* coeff = follow_id(flat_coeff.r())->template cast(); + for (unsigned int i = coeff->size(); i--;) + c_coeff[i] = LinearTraits::eval(env, (*coeff)[i]); + } + cid = constants().ids.lin_exp; + std::vector coeffv; + std::vector alv; + for (unsigned int i = 0; i < al->size(); i++) { + GCLock lock; + if (Call* sc = Expression::dyn_cast(same_call(env, (*al)[i], cid))) { + if (VarDecl* alvi_decl = follow_id_to_decl((*al)[i])->dyn_cast()) { + if (alvi_decl->ti()->domain()) { + // Test if the variable has tighter declared bounds than what can be inferred + // from its RHS. If yes, keep the variable (don't aggregate), because the tighter + // bounds are actually a constraint + typename LinearTraits::Domain sc_dom = + LinearTraits::eval_domain(env, alvi_decl->ti()->domain()); + typename LinearTraits::Bounds sc_bounds = LinearTraits::compute_bounds(env, sc); + if (LinearTraits::domain_tighter(sc_dom, sc_bounds)) { + coeffv.push_back(c_coeff[i]); + alv.push_back((*al)[i]); + continue; + } + } + } + + Val cd = c_coeff[i]; + ArrayLit* sc_coeff = eval_array_lit(env, sc->arg(0)); + ArrayLit* sc_al = eval_array_lit(env, sc->arg(1)); + Val sc_d = LinearTraits::eval(env, sc->arg(2)); + assert(sc_coeff->size() == sc_al->size()); + for (unsigned int j = 0; j < sc_coeff->size(); j++) { + coeffv.push_back(cd * LinearTraits::eval(env, (*sc_coeff)[j])); + alv.push_back((*sc_al)[j]); + } + d += cd * sc_d; + } else { + coeffv.push_back(c_coeff[i]); + alv.push_back((*al)[i]); + } + } + simplify_lin(coeffv, alv, d); + if (coeffv.size() == 0) { + GCLock lock; + ret.b = conj(env, b, Ctx(), args_ee); + ret.r = bind(env, ctx, r, LinearTraits::newLit(d)); + return; + } else if (coeffv.size() == 1 && coeffv[0] == 1 && d == 0) { + ret.b = conj(env, b, Ctx(), args_ee); + ret.r = bind(env, ctx, r, alv[0]()); + return; + } + GCLock lock; + std::vector coeff_ev(coeffv.size()); + for (unsigned int i = static_cast(coeff_ev.size()); i--;) + coeff_ev[i] = LinearTraits::newLit(coeffv[i]); + ArrayLit* ncoeff = new ArrayLit(Location().introduce(), coeff_ev); + Type t = coeff_ev[0]->type(); + t.dim(1); + ncoeff->type(t); + args.push_back(ncoeff); + std::vector alv_e(alv.size()); + bool al_same_as_before = alv.size() == al->size(); + for (unsigned int i = static_cast(alv.size()); i--;) { + alv_e[i] = alv[i](); + al_same_as_before = al_same_as_before && Expression::equal(alv_e[i], (*al)[i]); + } + if (al_same_as_before) { + Expression* rd = follow_id_to_decl(flat_al.r()); + if (rd->isa()) rd = rd->cast()->id(); + if (rd->type().dim() > 1) { + ArrayLit* al = eval_array_lit(env, rd); + std::vector > dims(1); + dims[0].first = 1; + dims[0].second = al->size(); + rd = new ArrayLit(al->loc(), *al, dims); + Type t = al->type(); + t.dim(1); + rd->type(t); + } + args.push_back(rd); + } else { + ArrayLit* nal = new ArrayLit(al->loc(), alv_e); + nal->type(al->type()); + args.push_back(nal); + } + Lit* il = LinearTraits::newLit(d); + args.push_back(il); +} + +/// Special form of disjunction for SCIP +bool addBoundsDisj(EnvI& env, Expression* arg, Call* c_orig) { + auto pArrayLit = arg->dyn_cast(); + if (nullptr == pArrayLit) return false; + std::vector isUBI, bndI, varI, // integer bounds and vars + isUBF, bndF, varF; // float bounds and vars + for (int i = pArrayLit->size(); i--;) { + auto pId = pArrayLit->operator[](i)->dyn_cast(); + if (nullptr == pId) return false; + auto pDecl = follow_id_to_decl(pId)->dyn_cast(); + /// Checking the rhs + auto pRhs = pDecl->e(); + if (nullptr == pRhs) return false; // not checking this boolean + auto pCall = pRhs->dyn_cast(); + if (nullptr == pCall) return false; + if (constants().ids.int_.le != pCall->id() && constants().ids.float_.le != pCall->id()) + return false; + /// See if one is a constant and one a variable + Expression *pConst = nullptr, *pVar = nullptr; + bool fFloat = false; + bool isUB = false; + for (int j = pCall->n_args(); j--;) { + if (auto pF = pCall->arg(j)->dyn_cast()) { + pConst = pF; + fFloat = true; + isUB = (1 == j); + } else if (auto pF = pCall->arg(j)->dyn_cast()) { + pConst = pF; + fFloat = false; + isUB = (1 == j); + } else if (auto pId = pCall->arg(j)->dyn_cast()) { + if (nullptr != pVar) return false; // 2 variables, exit + pVar = pId; + } + } + /// All good, add them + if (fFloat) { + isUBF.push_back(constants().boollit(isUB)); + bndF.push_back(pConst); + varF.push_back(pVar); + } else { + isUBI.push_back(constants().boollit(isUB)); + bndI.push_back(pConst); + varI.push_back(pVar); + } + } + /// Create new call + GCLock lock; + auto loc = c_orig->loc().introduce(); + std::vector args = {new ArrayLit(loc, isUBI), new ArrayLit(loc, bndI), + new ArrayLit(loc, varI), new ArrayLit(loc, isUBF), + new ArrayLit(loc, bndF), new ArrayLit(loc, varF)}; + + Call* c = + new Call(c_orig->loc().introduce(), env.model->getFnDecls().bounds_disj.second->id(), args); + c->type(Type::varbool()); + c->decl(env.model->getFnDecls().bounds_disj.second); + env.flat_addItem(new ConstraintI(c_orig->loc().introduce(), c)); + return true; +} + +class IgnorePartial { +public: + EnvI& env; + bool ignorePartial; + IgnorePartial(EnvI& env0, Call* c) : env(env0), ignorePartial(env.ignorePartial) { + if (c->id().endsWith("_reif") || c->id().endsWith("_imp")) { + env.ignorePartial = true; + } + } + ~IgnorePartial(void) { env.ignorePartial = ignorePartial; } +}; + +EE flatten_call(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + EE ret; + Call* c = e->cast(); + IgnorePartial ignorePartial(env, c); + if (c->id().endsWith("_reif")) { + env.n_reif_ct++; + } else if (c->id().endsWith("_imp")) { + env.n_imp_ct++; + } + FunctionI* decl = env.model->matchFn(env, c, false); + if (decl == NULL) { + throw InternalError("undeclared function or predicate " + c->id().str()); + } + + if (decl->params().size() == 1) { + if (Call* call_body = Expression::dyn_cast(decl->e())) { + if (call_body->n_args() == 1 && + Expression::equal(call_body->arg(0), decl->params()[0]->id())) { + c->id(call_body->id()); + c->decl(call_body->decl()); + decl = c->decl(); + for (ExpressionSetIter esi = call_body->ann().begin(); esi != call_body->ann().end(); + ++esi) { + c->addAnnotation(*esi); + } + } + } + } + + Ctx nctx = ctx; + nctx.neg = false; + ASTString cid = c->id(); + CallStackItem _csi(env, e); + + if (cid == constants().ids.bool2int && c->type().dim() == 0) { + if (ctx.neg) { + ctx.neg = false; + nctx.neg = true; + nctx.b = -ctx.i; + } else { + nctx.b = ctx.i; + } + } else if (cid == constants().ids.forall) { + nctx.b = +nctx.b; + if (ctx.neg) { + ctx.neg = false; + nctx.neg = true; + cid = constants().ids.exists; + } + } else if (cid == constants().ids.exists) { + nctx.b = +nctx.b; + if (ctx.neg) { + ctx.neg = false; + nctx.neg = true; + cid = constants().ids.forall; + } + } else if (decl->e() == NULL && (cid == constants().ids.assert || cid == constants().ids.trace)) { + if (cid == constants().ids.assert && c->n_args() == 2) { + (void)decl->_builtins.b(env, c); + ret = flat_exp(env, ctx, constants().lit_true, r, b); + } else { + KeepAlive callres = decl->_builtins.e(env, c); + ret = flat_exp(env, ctx, callres(), r, b); + // This is all we need to do for assert, so break out of the E_CALL + } + return ret; + } else if (decl->e() && ctx.b == C_ROOT && decl->e()->isa() && + eval_bool(env, decl->e())) { + bool allBool = true; + for (unsigned int i = 0; i < c->n_args(); i++) { + if (c->arg(i)->type().bt() != Type::BT_BOOL) { + allBool = false; + break; + } + } + if (allBool) { + ret.r = bind(env, ctx, r, constants().lit_true); + ret.b = bind(env, ctx, b, constants().lit_true); + return ret; + } + } + + if (ctx.b == C_ROOT && decl->e() == NULL && cid == constants().ids.forall && + r == constants().var_true) { + ret.b = bind(env, ctx, b, constants().lit_true); + ArrayLit* al; + if (c->arg(0)->isa()) { + al = c->arg(0)->cast(); + } else { + EE flat_al = flat_exp(env, Ctx(), c->arg(0), constants().var_ignore, constants().var_true); + al = follow_id(flat_al.r())->cast(); + } + nctx.b = C_ROOT; + for (unsigned int i = 0; i < al->size(); i++) (void)flat_exp(env, nctx, (*al)[i], r, b); + ret.r = bind(env, ctx, r, constants().lit_true); + } else { + if (decl->e() && decl->params().size() == 1 && decl->e()->isa() && + decl->params()[0]->ti()->domain() == NULL && + decl->e()->cast()->decl() == decl->params()[0]) { + Expression* arg = c->arg(0); + for (ExpressionSetIter esi = decl->e()->ann().begin(); esi != decl->e()->ann().end(); ++esi) { + arg->addAnnotation(*esi); + } + for (ExpressionSetIter esi = c->ann().begin(); esi != c->ann().end(); ++esi) { + arg->addAnnotation(*esi); + } + ret = flat_exp(env, ctx, c->arg(0), r, b); + return ret; + } + + std::vector args_ee(c->n_args()); + bool isPartial = false; + + if (cid == constants().ids.lin_exp && c->type().isint()) { + // Linear expressions need special context handling: + // the context of a variable expression depends on the corresponding coefficient + + // flatten the coefficient array + Expression* tmp = follow_id_to_decl(c->arg(0)); + ArrayLit* coeffs; + if (VarDecl* vd = tmp->dyn_cast()) tmp = vd->id(); + { + CallArgItem cai(env); + args_ee[0] = flat_exp(env, nctx, tmp, NULL, NULL); + isPartial |= isfalse(env, args_ee[0].b()); + coeffs = eval_array_lit(env, args_ee[0].r()); + } + + ArrayLit* vars = eval_array_lit(env, c->arg(1)); + if (vars->flat()) { + args_ee[1].r = vars; + args_ee[1].b = constants().var_true; + } else { + CallArgItem cai(env); + CallStackItem _csi(env, c->arg(1)); + std::vector elems_ee(vars->size()); + for (unsigned int i = vars->size(); i--;) { + Ctx argctx = nctx; + argctx.i = eval_int(env, (*coeffs)[i]) < 0 ? -nctx.i : +nctx.i; + elems_ee[i] = flat_exp(env, argctx, (*vars)[i], NULL, NULL); + } + std::vector elems(elems_ee.size()); + for (unsigned int i = static_cast(elems.size()); i--;) + elems[i] = elems_ee[i].r(); + KeepAlive ka; + { + GCLock lock; + ArrayLit* alr = new ArrayLit(Location().introduce(), elems); + alr->type(vars->type()); + alr->flat(true); + ka = alr; + } + args_ee[1].r = ka(); + args_ee[1].b = conj(env, b, Ctx(), elems_ee); + } + + { + Expression* constant = follow_id_to_decl(c->arg(2)); + if (VarDecl* vd = constant->dyn_cast()) constant = vd->id(); + CallArgItem cai(env); + args_ee[2] = flat_exp(env, nctx, constant, NULL, NULL); + isPartial |= isfalse(env, args_ee[2].b()); + } + + } else { + bool mixContext = + (cid != constants().ids.forall && cid != constants().ids.exists && + (cid != constants().ids.bool2int || c->type().dim() > 0) && cid != constants().ids.sum && + cid != "assert" && cid != constants().var_redef->id() && cid != "mzn_reverse_map_var"); + if (cid == "mzn_reverse_map_var") { + env.in_reverse_map_var = true; + } + if (cid == constants().ids.clause && c->arg(0)->isa() && + c->arg(1)->isa()) { + GCLock lock; + // try to make negative arguments positive + std::vector newPositives; + std::vector newNegatives; + ArrayLit* al_neg = c->arg(1)->cast(); + for (unsigned int i = 0; i < al_neg->size(); i++) { + BinOp* bo = (*al_neg)[i]->dyn_cast(); + Call* co = (*al_neg)[i]->dyn_cast(); + if (bo || + (co && (co->id() == constants().ids.forall || co->id() == constants().ids.exists || + co->id() == constants().ids.clause))) { + UnOp* notBoe0 = new UnOp(Location().introduce(), UOT_NOT, (*al_neg)[i]); + notBoe0->type(Type::varbool()); + newPositives.push_back(notBoe0); + } else { + newNegatives.push_back((*al_neg)[i]); + } + } + if (!newPositives.empty()) { + ArrayLit* al_pos = c->arg(0)->cast(); + for (unsigned int i = 0; i < al_pos->size(); i++) { + newPositives.push_back((*al_pos)[i]); + } + c->arg(0, new ArrayLit(Location().introduce(), newPositives)); + c->arg(1, new ArrayLit(Location().introduce(), newNegatives)); + c->arg(0)->type(Type::varbool(1)); + c->arg(1)->type(Type::varbool(1)); + } + } + for (unsigned int i = c->n_args(); i--;) { + Ctx argctx = nctx; + if (mixContext) { + if (cid == constants().ids.clause) { + argctx.b = (i == 0 ? +nctx.b : -nctx.b); + } else if (c->arg(i)->type().bt() == Type::BT_BOOL) { + argctx.b = C_MIX; + } else if (c->arg(i)->type().bt() == Type::BT_INT) { + argctx.i = C_MIX; + } + } + Expression* tmp = follow_id_to_decl(c->arg(i)); + if (VarDecl* vd = tmp->dyn_cast()) tmp = vd->id(); + CallArgItem cai(env); + args_ee[i] = flat_exp(env, argctx, tmp, NULL, NULL); + isPartial |= isfalse(env, args_ee[i].b()); + } + } + if (isPartial && c->type().isbool() && !c->type().isopt()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + args_ee.resize(1); + args_ee[0] = EE(NULL, constants().lit_false); + ret.r = conj(env, r, ctx, args_ee); + return ret; + } + + std::vector args; + if (decl->e() == NULL && (cid == constants().ids.exists || cid == constants().ids.clause)) { + std::vector pos_alv; + std::vector neg_alv; + + std::vector pos_stack; + std::vector neg_stack; + + ArrayLit* al_pos = follow_id(args_ee[0].r())->cast(); + for (unsigned int i = 0; i < al_pos->size(); i++) { + pos_stack.push_back((*al_pos)[i]); + } + if (cid == constants().ids.clause) { + ArrayLit* al_neg = follow_id(args_ee[1].r())->cast(); + for (unsigned int i = 0; i < al_neg->size(); i++) { + neg_stack.push_back((*al_neg)[i]); + } + } + + while (!pos_stack.empty() || !neg_stack.empty()) { + while (!pos_stack.empty()) { + Expression* cur = pos_stack.back(); + pos_stack.pop_back(); + GCLock lock; + if (Call* sc = Expression::dyn_cast(same_call(env, cur, constants().ids.exists))) { + GCLock lock; + ArrayLit* sc_c = eval_array_lit(env, sc->arg(0)); + for (unsigned int j = 0; j < sc_c->size(); j++) { + pos_stack.push_back((*sc_c)[j]); + } + } else if (Call* sc = + Expression::dyn_cast(same_call(env, cur, constants().ids.clause))) { + GCLock lock; + ArrayLit* sc_c = eval_array_lit(env, sc->arg(0)); + for (unsigned int j = 0; j < sc_c->size(); j++) { + pos_stack.push_back((*sc_c)[j]); + } + sc_c = eval_array_lit(env, sc->arg(1)); + for (unsigned int j = 0; j < sc_c->size(); j++) { + neg_stack.push_back((*sc_c)[j]); + } + } else { + Call* eq_call = + Expression::dyn_cast(same_call(env, cur, constants().ids.bool_eq)); + if (eq_call && Expression::equal(eq_call->arg(1), constants().lit_false)) { + neg_stack.push_back(eq_call->arg(0)); + } else if (eq_call && Expression::equal(eq_call->arg(0), constants().lit_false)) { + neg_stack.push_back(eq_call->arg(1)); + } else if (eq_call && Expression::equal(eq_call->arg(1), constants().lit_true)) { + pos_stack.push_back(eq_call->arg(0)); + } else if (eq_call && Expression::equal(eq_call->arg(0), constants().lit_true)) { + pos_stack.push_back(eq_call->arg(1)); + } else if (Id* ident = cur->dyn_cast()) { + if (ident->decl()->ti()->domain() != constants().lit_false) { + pos_alv.push_back(ident); + } + } else { + pos_alv.push_back(cur); + } + } + } + + while (!neg_stack.empty()) { + GCLock lock; + Expression* cur = neg_stack.back(); + neg_stack.pop_back(); + if (Call* sc = Expression::dyn_cast(same_call(env, cur, constants().ids.forall))) { + GCLock lock; + ArrayLit* sc_c = eval_array_lit(env, sc->arg(0)); + for (unsigned int j = 0; j < sc_c->size(); j++) { + neg_stack.push_back((*sc_c)[j]); + } + } else { + Call* eq_call = + Expression::dyn_cast(same_call(env, cur, constants().ids.bool_eq)); + if (eq_call && Expression::equal(eq_call->arg(1), constants().lit_false)) { + pos_stack.push_back(eq_call->arg(0)); + } else if (eq_call && Expression::equal(eq_call->arg(0), constants().lit_false)) { + pos_stack.push_back(eq_call->arg(1)); + } else if (eq_call && Expression::equal(eq_call->arg(1), constants().lit_true)) { + neg_stack.push_back(eq_call->arg(0)); + } else if (eq_call && Expression::equal(eq_call->arg(0), constants().lit_true)) { + neg_stack.push_back(eq_call->arg(1)); + } else if (Id* ident = cur->dyn_cast()) { + if (ident->decl()->ti()->domain() != constants().lit_true) { + neg_alv.push_back(ident); + } + } else { + neg_alv.push_back(cur); + } + } + } + } + + bool subsumed = remove_dups(pos_alv, false); + subsumed = subsumed || remove_dups(neg_alv, true); + subsumed = subsumed || contains_dups(pos_alv, neg_alv); + if (subsumed) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, constants().lit_true); + return ret; + } + if (neg_alv.empty()) { + if (pos_alv.size() == 0) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, constants().lit_false); + return ret; + } else if (pos_alv.size() == 1) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, pos_alv[0]()); + return ret; + } + GCLock lock; + ArrayLit* nal = new ArrayLit(Location().introduce(), toExpVec(pos_alv)); + nal->type(Type::varbool(1)); + args.push_back(nal); + cid = constants().ids.exists; + } else { + GCLock lock; + ArrayLit* pos_al = new ArrayLit(Location().introduce(), toExpVec(pos_alv)); + pos_al->type(Type::varbool(1)); + ArrayLit* neg_al = new ArrayLit(Location().introduce(), toExpVec(neg_alv)); + neg_al->type(Type::varbool(1)); + cid = constants().ids.clause; + args.push_back(pos_al); + args.push_back(neg_al); + } + if (C_ROOT == ctx.b && cid == constants().ids.exists) { + /// Check the special bounds disjunction for SCIP + /// Only in root context + if (!env.model->getFnDecls().bounds_disj.first) { + env.model->getFnDecls().bounds_disj.first = true; + std::vector bj_t = {Type::parbool(1), Type::parint(1), Type::varint(1), + Type::parbool(1), Type::parfloat(1), Type::varfloat(1)}; + GCLock lock; + env.model->getFnDecls().bounds_disj.second = + env.model->matchFn(env, ASTString("bounds_disj"), bj_t, false); + } + /// When the SCIP predicate is declared only + bool fBoundsDisj_Maybe = (nullptr != env.model->getFnDecls().bounds_disj.second); + if (fBoundsDisj_Maybe) { + if (addBoundsDisj(env, args[0](), c)) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, constants().lit_true); + return ret; + } + } + } + + } else if (decl->e() == NULL && cid == constants().ids.forall) { + ArrayLit* al = follow_id(args_ee[0].r())->cast(); + std::vector alv; + for (unsigned int i = 0; i < al->size(); i++) { + GCLock lock; + if (Call* sc = Expression::dyn_cast(same_call(env, (*al)[i], cid))) { + GCLock lock; + ArrayLit* sc_c = eval_array_lit(env, sc->arg(0)); + for (unsigned int j = 0; j < sc_c->size(); j++) { + alv.push_back((*sc_c)[j]); + } + } else { + alv.push_back((*al)[i]); + } + } + bool subsumed = remove_dups(alv, true); + if (subsumed) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, constants().lit_false); + return ret; + } + if (alv.size() == 0) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, constants().lit_true); + return ret; + } else if (alv.size() == 1) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, alv[0]()); + return ret; + } + GCLock lock; + ArrayLit* nal = new ArrayLit(al->loc(), toExpVec(alv)); + nal->type(al->type()); + args.push_back(nal); + } else if (decl->e() == NULL && + (cid == constants().ids.lin_exp || cid == constants().ids.sum)) { + if (e->type().isint()) { + flatten_linexp_call(env, ctx, nctx, cid, c, ret, b, r, args_ee, args); + } else { + flatten_linexp_call(env, ctx, nctx, cid, c, ret, b, r, args_ee, args); + } + if (args.size() == 0) return ret; + } else { + for (unsigned int i = 0; i < args_ee.size(); i++) args.push_back(args_ee[i].r()); + } + KeepAlive cr; + { + GCLock lock; + std::vector e_args = toExpVec(args); + Call* cr_c = new Call(c->loc().introduce(), cid, e_args); + decl = env.model->matchFn(env, cr_c, false); + if (decl == NULL) throw FlatteningError(env, cr_c->loc(), "cannot find matching declaration"); + cr_c->type(decl->rtype(env, e_args, false)); + assert(decl); + cr_c->decl(decl); + cr = cr_c; + } + EnvI::CSEMap::iterator cit = env.cse_map_find(cr()); + if (cit != env.cse_map_end()) { + ret.b = bind(env, Ctx(), b, env.ignorePartial ? constants().lit_true : cit->second.b()); + ret.r = bind(env, ctx, r, cit->second.r()); + } else { + for (unsigned int i = 0; i < decl->params().size(); i++) { + if (decl->params()[i]->type().dim() > 0) { + // Check array index sets + ArrayLit* al = follow_id(args[i]())->cast(); + VarDecl* pi = decl->params()[i]; + for (unsigned int j = 0; j < pi->ti()->ranges().size(); j++) { + TypeInst* range_ti = pi->ti()->ranges()[j]; + if (range_ti->domain() && !range_ti->domain()->isa()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, range_ti->domain()); + if (isv->min() != al->min(j) || isv->max() != al->max(j)) { + std::ostringstream oss; + oss << "array index set " << (j + 1) << " of argument " << (i + 1) + << " does not match declared index set"; + throw FlatteningError(env, e->loc(), oss.str()); + } + } + } + } + if (Expression* dom = decl->params()[i]->ti()->domain()) { + if (!dom->isa()) { + // May have to constrain actual argument + if (args[i]()->type().bt() == Type::BT_INT) { + GCLock lock; + IntSetVal* isv = eval_intset(env, dom); + BinOpType bot; + bool needToConstrain; + if (args[i]()->type().st() == Type::ST_SET) { + bot = BOT_SUBSET; + needToConstrain = true; + } else { + bot = BOT_IN; + if (args[i]()->type().dim() > 0) { + needToConstrain = true; + } else { + IntBounds ib = compute_int_bounds(env, args[i]()); + needToConstrain = !ib.valid || isv->size() == 0 || ib.l < isv->min(0) || + ib.u > isv->max(isv->size() - 1); + } + } + if (needToConstrain) { + GCLock lock; + Expression* domconstraint; + if (args[i]()->type().dim() > 0) { + std::vector domargs(2); + domargs[0] = args[i](); + domargs[1] = dom; + Call* c = new Call(Location().introduce(), "var_dom", domargs); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + if (c->decl() == NULL) + throw InternalError("no matching declaration found for var_dom"); + domconstraint = c; + } else { + domconstraint = new BinOp(Location().introduce(), args[i](), bot, dom); + } + domconstraint->type(args[i]()->type().ispar() ? Type::parbool() : Type::varbool()); + if (ctx.b == C_ROOT) { + (void)flat_exp(env, Ctx(), domconstraint, constants().var_true, + constants().var_true); + } else { + EE ee = flat_exp(env, Ctx(), domconstraint, NULL, constants().var_true); + ee.b = ee.r; + args_ee.push_back(ee); + } + } + } else if (args[i]()->type().bt() == Type::BT_FLOAT) { + GCLock lock; + + FloatSetVal* fsv = eval_floatset(env, dom); + bool needToConstrain; + if (args[i]()->type().dim() > 0) { + needToConstrain = true; + } else { + FloatBounds fb = compute_float_bounds(env, args[i]()); + needToConstrain = !fb.valid || fsv->size() == 0 || fb.l < fsv->min(0) || + fb.u > fsv->max(fsv->size() - 1); + } + + if (needToConstrain) { + GCLock lock; + Expression* domconstraint; + if (args[i]()->type().dim() > 0) { + std::vector domargs(2); + domargs[0] = args[i](); + domargs[1] = dom; + Call* c = new Call(Location().introduce(), "var_dom", domargs); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + if (c->decl() == NULL) + throw InternalError("no matching declaration found for var_dom"); + domconstraint = c; + } else { + domconstraint = new BinOp(Location().introduce(), args[i](), BOT_IN, dom); + } + domconstraint->type(args[i]()->type().ispar() ? Type::parbool() : Type::varbool()); + if (ctx.b == C_ROOT) { + (void)flat_exp(env, Ctx(), domconstraint, constants().var_true, + constants().var_true); + } else { + EE ee = flat_exp(env, Ctx(), domconstraint, NULL, constants().var_true); + ee.b = ee.r; + args_ee.push_back(ee); + } + } + } else if (args[i]()->type().bt() == Type::BT_BOT) { + // Nothing to be done for empty arrays/sets + } else { + throw EvalError(env, decl->params()[i]->loc(), + "domain restrictions other than int and float not supported yet"); + } + } + } + } + if (cr()->type().isbool() && !cr()->type().ispar() && !cr()->type().isopt() && + (ctx.b != C_ROOT || r != constants().var_true)) { + std::vector argtypes(args.size()); + for (unsigned int i = 0; i < args.size(); i++) argtypes[i] = args[i]()->type(); + argtypes.push_back(Type::varbool()); + GCLock lock; + ASTString r_cid = env.reifyId(cid); + FunctionI* reif_decl = env.model->matchFn(env, r_cid, argtypes, false); + if (reif_decl && reif_decl->e()) { + addPathAnnotation(env, reif_decl->e()); + VarDecl* reif_b; + if (r == NULL || (r != NULL && r->e() != NULL)) { + reif_b = newVarDecl(env, Ctx(), new TypeInst(Location().introduce(), Type::varbool()), + NULL, NULL, NULL); + addCtxAnn(reif_b, ctx.b); + if (reif_b->ti()->domain()) { + if (reif_b->ti()->domain() == constants().lit_true) { + bind(env, ctx, r, constants().lit_true); + r = constants().var_true; + ctx.b = C_ROOT; + goto call_nonreif; + } else { + std::vector args_e(args.size() + 1); + for (unsigned int i = 0; i < args.size(); i++) args_e[i] = args[i](); + args_e[args.size()] = constants().lit_false; + Call* reif_call = new Call(Location().introduce(), r_cid, args_e); + reif_call->type(Type::varbool()); + reif_call->decl(reif_decl); + flat_exp(env, Ctx(), reif_call, constants().var_true, constants().var_true); + args_ee.push_back(EE(NULL, constants().lit_false)); + ret.r = conj(env, r, ctx, args_ee); + ret.b = bind(env, ctx, b, constants().lit_true); + return ret; + } + } + } else { + reif_b = r; + } + // Annotate cr() with getPath() + addPathAnnotation(env, cr()); + reif_b->e(cr()); + if (r != NULL && r->e() != NULL) { + Ctx reif_ctx; + reif_ctx.neg = ctx.neg; + bind(env, reif_ctx, r, reif_b->id()); + } + env.vo_add_exp(reif_b); + ret.b = bind(env, Ctx(), b, constants().lit_true); + args_ee.push_back(EE(NULL, reif_b->id())); + ret.r = conj(env, NULL, ctx, args_ee); + if (!ctx.neg && !cr()->type().isann()) { + env.cse_map_insert(cr(), ret); + } + return ret; + } + } + call_nonreif: + if ((cr()->type().ispar() && !cr()->type().isann()) || decl->e() == NULL) { + Call* cr_c = cr()->cast(); + /// All builtins are total + std::vector argt(cr_c->n_args()); + for (unsigned int i = static_cast(argt.size()); i--;) + argt[i] = cr_c->arg(i)->type(); + Type callt = decl->rtype(env, argt, false); + if (callt.ispar() && callt.bt() != Type::BT_ANN) { + GCLock lock; + try { + ret.r = bind(env, ctx, r, eval_par(env, cr_c)); + ret.b = conj(env, b, Ctx(), args_ee); + } catch (ResultUndefinedError&) { + ret.r = createDummyValue(env, cr_c->type()); + ret.b = bind(env, Ctx(), b, constants().lit_false); + return ret; + } + // Do not insert into map, since par results will quickly become + // garbage anyway and then disappear from the map + } else if (decl->_builtins.e) { + KeepAlive callres; + { + GCLock lock; + callres = decl->_builtins.e(env, cr_c); + } + EE res = flat_exp(env, ctx, callres(), r, b); + args_ee.push_back(res); + ret.b = conj(env, b, Ctx(), args_ee); + addPathAnnotation(env, res.r()); + ret.r = bind(env, ctx, r, res.r()); + if (!ctx.neg && !cr_c->type().isann()) env.cse_map_insert(cr_c, ret); + } else { + ret.b = conj(env, b, Ctx(), args_ee); + addPathAnnotation(env, cr_c); + ret.r = bind(env, ctx, r, cr_c); + if (!ctx.neg && !cr_c->type().isann()) env.cse_map_insert(cr_c, ret); + } + } else { + std::vector previousParameters(decl->params().size()); + for (unsigned int i = decl->params().size(); i--;) { + VarDecl* vd = decl->params()[i]; + previousParameters[i] = vd->e(); + vd->flat(vd); + vd->e(args[i]()); + } + + if (decl->e()->type().isbool() && !decl->e()->type().isopt()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + if (ctx.b == C_ROOT && r == constants().var_true) { + (void)flat_exp(env, Ctx(), decl->e(), r, constants().var_true); + } else { + Ctx nctx; + if (!isTotal(decl)) { + nctx = ctx; + nctx.neg = false; + } + EE ee = flat_exp(env, nctx, decl->e(), NULL, constants().var_true); + ee.b = ee.r; + args_ee.push_back(ee); + } + ret.r = conj(env, r, ctx, args_ee); + } else { + if (isTotal(decl)) { + EE ee = flat_exp(env, Ctx(), decl->e(), r, constants().var_true); + ret.r = bind(env, ctx, r, ee.r()); + } else { + ret = flat_exp(env, ctx, decl->e(), r, NULL); + args_ee.push_back(ret); + if (decl->e()->type().dim() > 0) { + ArrayLit* al = follow_id(ret.r())->cast(); + assert(al->dims() == decl->e()->type().dim()); + for (unsigned int i = 0; i < decl->ti()->ranges().size(); i++) { + if (decl->ti()->ranges()[i]->domain() && + !decl->ti()->ranges()[i]->domain()->isa()) { + GCLock lock; + IntSetVal* isv = eval_intset(env, decl->ti()->ranges()[i]->domain()); + if (al->min(i) != isv->min() || al->max(i) != isv->max()) { + EE ee; + ee.b = constants().lit_false; + args_ee.push_back(ee); + } + } + } + } + if (decl->ti()->domain() && !decl->ti()->domain()->isa()) { + BinOpType bot; + if (ret.r()->type().st() == Type::ST_SET) { + bot = BOT_SUBSET; + } else { + bot = BOT_IN; + } + + KeepAlive domconstraint; + if (decl->e()->type().dim() > 0) { + GCLock lock; + std::vector domargs(2); + domargs[0] = ret.r(); + domargs[1] = decl->ti()->domain(); + Call* c = new Call(Location().introduce(), "var_dom", domargs); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + if (c->decl() == NULL) + throw InternalError("no matching declaration found for var_dom"); + domconstraint = c; + } else { + GCLock lock; + domconstraint = + new BinOp(Location().introduce(), ret.r(), bot, decl->ti()->domain()); + } + domconstraint()->type(ret.r()->type().ispar() ? Type::parbool() : Type::varbool()); + if (ctx.b == C_ROOT) { + (void)flat_exp(env, Ctx(), domconstraint(), constants().var_true, + constants().var_true); + } else { + EE ee = flat_exp(env, Ctx(), domconstraint(), NULL, constants().var_true); + ee.b = ee.r; + args_ee.push_back(ee); + } + } + } + ret.b = conj(env, b, Ctx(), args_ee); + } + if (!ctx.neg && !cr()->type().isann()) env.cse_map_insert(cr(), ret); + + // Restore previous mapping + for (unsigned int i = decl->params().size(); i--;) { + VarDecl* vd = decl->params()[i]; + vd->e(previousParameters[i]()); + vd->flat(vd->e() ? vd : NULL); + } + } + } + } + if (cid == "mzn_reverse_map_var") { + env.in_reverse_map_var = false; + } + return ret; +} +} // namespace MiniZinc diff --git a/lib/flatten/flatten_comp.cpp b/lib/flatten/flatten_comp.cpp new file mode 100644 index 0000000..846a88a --- /dev/null +++ b/lib/flatten/flatten_comp.cpp @@ -0,0 +1,257 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_comp(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + Comprehension* c = e->cast(); + KeepAlive c_ka(c); + + if (c->type().isopt()) { + std::vector in(c->n_generators()); + std::vector orig_where(c->n_generators()); + std::vector where; + GCLock lock; + for (int i = 0; i < c->n_generators(); i++) { + if (c->in(i) == NULL) { + in[i] = NULL; + orig_where[i] = c->where(i); + } else { + if (c->in(i)->type().isvar() && c->in(i)->type().dim() == 0) { + std::vector args(1); + args[0] = c->in(i); + Call* ub = new Call(Location().introduce(), "ub", args); + ub->type(Type::parsetint()); + ub->decl(env.model->matchFn(env, ub, false)); + in[i] = ub; + for (int j = 0; j < c->n_decls(i); j++) { + BinOp* bo = new BinOp(Location().introduce(), c->decl(i, j)->id(), BOT_IN, c->in(i)); + bo->type(Type::varbool()); + where.push_back(bo); + } + } else { + in[i] = c->in(i); + } + if (c->where(i) && c->where(i)->type().isvar()) { + // This is a generalised where clause. Split into par and var part. + // The par parts can remain in where clause. The var parts are translated + // into optionality constraints. + if (c->where(i)->isa() && c->where(i)->cast()->op() == BOT_AND) { + std::vector parWhere; + std::vector todo; + todo.push_back(c->where(i)->cast()); + while (!todo.empty()) { + BinOp* bo = todo.back(); + todo.pop_back(); + if (bo->rhs()->type().ispar()) { + parWhere.push_back(bo->rhs()); + } else if (bo->rhs()->isa() && bo->rhs()->cast()->op() == BOT_AND) { + todo.push_back(bo->rhs()->cast()); + } else { + where.push_back(bo->rhs()); + } + if (bo->lhs()->type().ispar()) { + parWhere.push_back(bo->lhs()); + } else if (bo->lhs()->isa() && bo->lhs()->cast()->op() == BOT_AND) { + todo.push_back(bo->lhs()->cast()); + } else { + where.push_back(bo->lhs()); + } + } + switch (parWhere.size()) { + case 0: + orig_where[i] = NULL; + break; + case 1: + orig_where[i] = parWhere[0]; + break; + case 2: + orig_where[i] = new BinOp(c->where(i)->loc(), parWhere[0], BOT_AND, parWhere[1]); + orig_where[i]->type(Type::parbool()); + break; + default: { + ArrayLit* parWhereAl = new ArrayLit(c->where(i)->loc(), parWhere); + parWhereAl->type(Type::parbool(1)); + Call* forall = new Call(c->where(i)->loc(), constants().ids.forall, {parWhereAl}); + forall->type(Type::parbool()); + forall->decl(env.model->matchFn(env, forall, false)); + orig_where[i] = forall; + break; + } + } + } else { + orig_where[i] = NULL; + where.push_back(c->where(i)); + } + } else { + orig_where[i] = c->where(i); + } + } + } + if (where.size() > 0) { + Generators gs; + for (int i = 0; i < c->n_generators(); i++) { + std::vector vds(c->n_decls(i)); + for (int j = 0; j < c->n_decls(i); j++) vds[j] = c->decl(i, j); + gs._g.push_back(Generator(vds, in[i], orig_where[i])); + } + Expression* cond; + if (where.size() > 1) { + ArrayLit* al = new ArrayLit(Location().introduce(), where); + al->type(Type::varbool(1)); + std::vector args(1); + args[0] = al; + Call* forall = new Call(Location().introduce(), constants().ids.forall, args); + forall->type(Type::varbool()); + forall->decl(env.model->matchFn(env, forall, false)); + cond = forall; + } else { + cond = where[0]; + } + + Expression* new_e; + + Call* surround = env.surroundingCall(); + + Type ntype = c->type(); + if (surround && surround->id() == constants().ids.forall) { + new_e = new BinOp(Location().introduce(), cond, BOT_IMPL, c->e()); + new_e->type(Type::varbool()); + ntype.ot(Type::OT_PRESENT); + } else if (surround && surround->id() == constants().ids.exists) { + new_e = new BinOp(Location().introduce(), cond, BOT_AND, c->e()); + new_e->type(Type::varbool()); + ntype.ot(Type::OT_PRESENT); + } else { + Expression* r_bounds = NULL; + if (c->e()->type().bt() == Type::BT_INT && c->e()->type().dim() == 0) { + std::vector ubargs(1); + ubargs[0] = c->e(); + if (c->e()->type().st() == Type::ST_SET) { + Call* bc = new Call(Location().introduce(), "ub", ubargs); + bc->type(Type::parsetint()); + bc->decl(env.model->matchFn(env, bc, false)); + r_bounds = bc; + } else { + Call* lbc = new Call(Location().introduce(), "lb", ubargs); + lbc->type(Type::parint()); + lbc->decl(env.model->matchFn(env, lbc, false)); + Call* ubc = new Call(Location().introduce(), "ub", ubargs); + ubc->type(Type::parint()); + ubc->decl(env.model->matchFn(env, ubc, false)); + r_bounds = new BinOp(Location().introduce(), lbc, BOT_DOTDOT, ubc); + r_bounds->type(Type::parsetint()); + } + r_bounds->addAnnotation(constants().ann.maybe_partial); + } + Type tt; + tt = c->e()->type(); + tt.ti(Type::TI_VAR); + tt.ot(Type::OT_OPTIONAL); + + TypeInst* ti = new TypeInst(Location().introduce(), tt, r_bounds); + VarDecl* r = new VarDecl(c->loc(), ti, env.genId()); + r->addAnnotation(constants().ann.promise_total); + r->introduced(true); + r->flat(r); + + std::vector let_exprs(3); + let_exprs[0] = r; + BinOp* r_eq_e = new BinOp(Location().introduce(), r->id(), BOT_EQ, c->e()); + r_eq_e->type(Type::varbool()); + let_exprs[1] = new BinOp(Location().introduce(), cond, BOT_IMPL, r_eq_e); + let_exprs[1]->type(Type::varbool()); + let_exprs[1]->addAnnotation(constants().ann.promise_total); + let_exprs[1]->addAnnotation(constants().ann.maybe_partial); + std::vector absent_r_args(1); + absent_r_args[0] = r->id(); + Call* absent_r = new Call(Location().introduce(), "absent", absent_r_args); + absent_r->type(Type::varbool()); + absent_r->decl(env.model->matchFn(env, absent_r, false)); + let_exprs[2] = new BinOp(Location().introduce(), cond, BOT_OR, absent_r); + let_exprs[2]->type(Type::varbool()); + let_exprs[2]->addAnnotation(constants().ann.promise_total); + Let* let = new Let(Location().introduce(), let_exprs, r->id()); + let->type(r->type()); + new_e = let; + } + Comprehension* nc = new Comprehension(c->loc(), new_e, gs, c->set()); + nc->type(ntype); + c = nc; + c_ka = c; + } + } + + class EvalF { + public: + Ctx ctx; + EvalF(Ctx ctx0) : ctx(ctx0) {} + typedef EE ArrayVal; + EE e(EnvI& env, Expression* e0) { + VarDecl* b = ctx.b == C_ROOT ? constants().var_true : NULL; + VarDecl* r = (ctx.b == C_ROOT && e0->type().isbool() && !e0->type().isopt()) + ? constants().var_true + : NULL; + return flat_exp(env, ctx, e0, r, b); + } + Expression* flatten(EnvI& env, Expression* e0) { + return flat_exp(env, Ctx(), e0, NULL, constants().var_true).r(); + } + } _evalf(ctx); + std::vector elems_ee = eval_comp(env, _evalf, c); + std::vector elems(elems_ee.size()); + Type elemType = Type::bot(); + bool allPar = true; + for (unsigned int i = static_cast(elems.size()); i--;) { + elems[i] = elems_ee[i].r(); + if (elemType == Type::bot()) elemType = elems[i]->type(); + if (!elems[i]->type().ispar()) allPar = false; + } + if (elemType.isbot()) { + elemType = c->type(); + elemType.ti(Type::TI_PAR); + } + if (!allPar) elemType.ti(Type::TI_VAR); + if (c->set()) + elemType.st(Type::ST_SET); + else + elemType.dim(c->type().dim()); + KeepAlive ka; + { + GCLock lock; + if (c->set()) { + if (c->type().ispar() && allPar) { + SetLit* sl = new SetLit(c->loc(), elems); + sl->type(elemType); + Expression* slr = eval_par(env, sl); + slr->type(elemType); + ka = slr; + } else { + throw InternalError("var set comprehensions not supported yet"); + } + } else { + ArrayLit* alr = new ArrayLit(Location().introduce(), elems); + alr->type(elemType); + alr->flat(true); + ka = alr; + } + } + assert(!ka()->type().isbot()); + ret.b = conj(env, b, Ctx(), elems_ee); + ret.r = bind(env, Ctx(), r, ka()); + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_id.cpp b/lib/flatten/flatten_id.cpp new file mode 100644 index 0000000..9209dad --- /dev/null +++ b/lib/flatten/flatten_id.cpp @@ -0,0 +1,186 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_id(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b, bool doNotFollowChains) { + CallStackItem _csi(env, e); + EE ret; + Id* id = e->cast(); + if (id->decl() == NULL) { + if (id->type().isann()) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + ret.r = bind(env, ctx, r, e); + return ret; + } else { + throw FlatteningError(env, e->loc(), "undefined identifier"); + } + } + if (!doNotFollowChains) { + id = follow_id_to_decl(id)->cast()->id(); + } + if (ctx.neg && id->type().dim() > 0) { + if (id->type().dim() > 1) + throw InternalError("multi-dim arrays in negative positions not supported yet"); + KeepAlive ka; + { + GCLock lock; + std::vector gen_id(1); + gen_id[0] = new VarDecl(id->loc(), new TypeInst(id->loc(), Type::parint()), env.genId(), + IntLit::a(0)); + + /// TODO: support arbitrary dimensions + std::vector idxsetargs(1); + idxsetargs[0] = id; + Call* idxset = new Call(id->loc().introduce(), "index_set", idxsetargs); + idxset->decl(env.model->matchFn(env, idxset, false)); + idxset->type(idxset->decl()->rtype(env, idxsetargs, false)); + Generator gen(gen_id, idxset, NULL); + std::vector idx(1); + Generators gens; + gens._g.push_back(gen); + UnOp* aanot = new UnOp(id->loc(), UOT_NOT, NULL); + Comprehension* cp = new Comprehension(id->loc(), aanot, gens, false); + Id* bodyidx = cp->decl(0, 0)->id(); + idx[0] = bodyidx; + ArrayAccess* aa = new ArrayAccess(id->loc(), id, idx); + aanot->e(aa); + Type tt = id->type(); + tt.dim(0); + aa->type(tt); + aanot->type(aa->type()); + cp->type(id->type()); + ctx.neg = false; + ka = cp; + } + ret = flat_exp(env, ctx, ka(), r, b); + } else { + GCLock lock; + VarDecl* vd = id->decl()->flat(); + Expression* rete = NULL; + if (vd == NULL) { + if (id->decl()->e() == NULL || id->decl()->e()->type().isann() || + id->decl()->e()->type().isvar() || id->decl()->e()->type().cv() || + id->decl()->e()->type().dim() > 0) { + // New top-level id, need to copy into env.m + vd = flat_exp(env, Ctx(), id->decl(), NULL, constants().var_true).r()->cast()->decl(); + } else { + vd = id->decl(); + } + } + ret.b = bind(env, Ctx(), b, constants().lit_true); + if (vd->e() != NULL) { + if (vd->e()->type().ispar() && vd->e()->type().dim() == 0) { + rete = eval_par(env, vd->e()); + } else if (vd->e()->isa()) { + rete = vd->e(); + } + } else if (vd->ti()->ranges().size() > 0) { + // create fresh variables and array literal + std::vector > dims; + IntVal asize = 1; + for (unsigned int i = 0; i < vd->ti()->ranges().size(); i++) { + TypeInst* ti = vd->ti()->ranges()[i]; + if (ti->domain() == NULL) throw FlatteningError(env, ti->loc(), "array dimensions unknown"); + IntSetVal* isv = eval_intset(env, ti->domain()); + if (isv->size() == 0) { + dims.push_back(std::pair(1, 0)); + asize = 0; + } else { + if (isv->size() != 1) throw FlatteningError(env, ti->loc(), "invalid array index set"); + asize *= (isv->max(0) - isv->min(0) + 1); + dims.push_back(std::pair(static_cast(isv->min(0).toInt()), + static_cast(isv->max(0).toInt()))); + } + } + Type tt = vd->ti()->type(); + tt.dim(0); + + if (asize > Constants::max_array_size) { + std::ostringstream oss; + oss << "array size (" << asize << ") exceeds maximum allowed size (" + << Constants::max_array_size << ")"; + throw FlatteningError(env, vd->loc(), oss.str()); + } + + std::vector elems(static_cast(asize.toInt())); + for (int i = 0; i < static_cast(asize.toInt()); i++) { + CallStackItem csi(env, IntLit::a(i)); + TypeInst* vti = new TypeInst(Location().introduce(), tt, vd->ti()->domain()); + VarDecl* nvd = newVarDecl(env, Ctx(), vti, NULL, vd, NULL); + elems[i] = nvd->id(); + } + // After introducing variables for each array element, the original domain can be + // set to "computed" (since it is a consequence of the individual variable domains) + vd->ti()->setComputedDomain(true); + + ArrayLit* al = new ArrayLit(Location().introduce(), elems, dims); + al->type(vd->type()); + vd->e(al); + env.vo_add_exp(vd); + EE ee; + ee.r = vd; + env.cse_map_insert(vd->e(), ee); + } + if (rete == NULL) { + if (!vd->toplevel()) { + // create new VarDecl in toplevel, if decl doesnt exist yet + EnvI::CSEMap::iterator it = env.cse_map_find(vd->e()); + if (it == env.cse_map_end()) { + Expression* vde = follow_id(vd->e()); + ArrayLit* vdea = vde ? vde->dyn_cast() : NULL; + if (vdea && vdea->size() == 0) { + // Do not create names for empty arrays but return array literal directly + rete = vdea; + } else { + VarDecl* nvd = newVarDecl(env, ctx, eval_typeinst(env, vd), NULL, vd, NULL); + + if (vd->e()) { + (void)flat_exp(env, Ctx(), vd->e(), nvd, constants().var_true); + } + vd = nvd; + EE ee(vd, NULL); + if (vd->e()) env.cse_map_insert(vd->e(), ee); + } + } else { + if (it->second.r()->isa()) { + vd = it->second.r()->cast(); + } else { + rete = it->second.r(); + } + } + } + if (rete == NULL) { + if (id->type().bt() == Type::BT_ANN && vd->e()) { + rete = vd->e(); + } else { + ArrayLit* vda = vd->dyn_cast(); + if (vda && vda->size() == 0) { + // Do not create names for empty arrays but return array literal directly + rete = vda; + } else { + rete = vd->id(); + } + } + } + } + ret.r = bind(env, ctx, r, rete); + } + return ret; +} + +EE flatten_id(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + return flatten_id(env, ctx, e, r, b, false); +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_ite.cpp b/lib/flatten/flatten_ite.cpp new file mode 100644 index 0000000..8a65e85 --- /dev/null +++ b/lib/flatten/flatten_ite.cpp @@ -0,0 +1,473 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +std::vector get_conjuncts(Expression* e) { + std::vector conj_stack; + std::vector conjuncts; + conj_stack.push_back(e); + while (!conj_stack.empty()) { + Expression* e = conj_stack.back(); + conj_stack.pop_back(); + if (BinOp* bo = e->dyn_cast()) { + if (bo->op() == BOT_AND) { + conj_stack.push_back(bo->rhs()); + conj_stack.push_back(bo->lhs()); + } else { + conjuncts.push_back(e); + } + } else { + conjuncts.push_back(e); + } + } + return conjuncts; +} + +void classify_conjunct(Expression* e, IdMap& eq_occurrences, + IdMap>& eq_branches, + std::vector& other_branches) { + if (BinOp* bo = e->dyn_cast()) { + if (bo->op() == BOT_EQ) { + if (Id* ident = bo->lhs()->dyn_cast()) { + if (eq_branches.find(ident) == eq_branches.end()) { + IdMap::iterator it = eq_occurrences.find(ident); + if (it == eq_occurrences.end()) { + eq_occurrences.insert(ident, 1); + } else { + eq_occurrences.get(ident)++; + } + eq_branches.insert(ident, std::make_pair(bo->rhs(), bo)); + return; + } + } else if (Id* ident = bo->rhs()->dyn_cast()) { + if (eq_branches.find(ident) == eq_branches.end()) { + IdMap::iterator it = eq_occurrences.find(ident); + if (it == eq_occurrences.end()) { + eq_occurrences.insert(ident, 1); + } else { + eq_occurrences.get(ident)++; + } + eq_branches.insert(ident, std::make_pair(bo->lhs(), bo)); + return; + } + } + } + } + other_branches.push_back(e); +} + +EE flatten_ite(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + ITE* ite = e->cast(); + + // The conditions of each branch of the if-then-else + std::vector conditions; + // Whether the right hand side of each branch is defined + std::vector> defined; + // The right hand side of each branch + std::vector> branches; + // Whether all branches are fixed + std::vector allBranchesPar; + + // Compute bounds of result as union bounds of all branches + std::vector> r_bounds_int; + std::vector r_bounds_valid_int; + std::vector> r_bounds_set; + std::vector r_bounds_valid_set; + std::vector> r_bounds_float; + std::vector r_bounds_valid_float; + + bool allConditionsPar = true; + bool allDefined = true; + + // The result variables of each generated conditional + std::vector results; + // The then-expressions of each generated conditional + std::vector> e_then; + // The else-expressions of each generated conditional + std::vector e_else; + + bool noOtherBranches = true; + if (ite->type() == Type::varbool() && ctx.b == C_ROOT && r == constants().var_true) { + // Check if all branches are of the form x1=e1 /\ ... /\ xn=en + IdMap eq_occurrences; + std::vector>> eq_branches(ite->size() + 1); + std::vector> other_branches(ite->size() + 1); + for (int i = 0; i < ite->size(); i++) { + auto conjuncts = get_conjuncts(ite->e_then(i)); + for (auto c : conjuncts) { + classify_conjunct(c, eq_occurrences, eq_branches[i], other_branches[i]); + } + noOtherBranches = noOtherBranches && other_branches[i].empty(); + } + { + auto conjuncts = get_conjuncts(ite->e_else()); + for (auto c : conjuncts) { + classify_conjunct(c, eq_occurrences, eq_branches[ite->size()], other_branches[ite->size()]); + } + noOtherBranches = noOtherBranches && other_branches[ite->size()].empty(); + } + for (auto& e : eq_occurrences) { + if (e.second >= ite->size()) { + // Any identifier that occurs in all or all but one branch gets its own conditional + results.push_back(e.first->decl()); + e_then.push_back(std::vector()); + for (int i = 0; i < ite->size(); i++) { + IdMap>::iterator it = eq_branches[i].find(e.first); + if (it == eq_branches[i].end()) { + // not found, simply push x=x + e_then.back().push_back(e.first); + } else { + e_then.back().push_back(it->second.first); + } + } + { + IdMap>::iterator it = + eq_branches[ite->size()].find(e.first); + if (it == eq_branches[ite->size()].end()) { + // not found, simply push x=x + e_else.push_back(e.first); + } else { + e_else.push_back(it->second.first); + } + } + } else { + // All other identifiers are put in the vector of "other" branches + for (int i = 0; i <= ite->size(); i++) { + IdMap>::iterator it = eq_branches[i].find(e.first); + if (it != eq_branches[i].end()) { + other_branches[i].push_back(it->second.second); + noOtherBranches = false; + eq_branches[i].remove(e.first); + } + } + } + } + if (!noOtherBranches) { + results.push_back(r); + e_then.push_back(std::vector()); + for (int i = 0; i < ite->size(); i++) { + if (eq_branches[i].size() == 0) { + e_then.back().push_back(ite->e_then(i)); + } else if (other_branches[i].size() == 0) { + e_then.back().push_back(constants().lit_true); + } else if (other_branches[i].size() == 1) { + e_then.back().push_back(other_branches[i][0]); + } else { + ArrayLit* al = new ArrayLit(Location().introduce(), other_branches[i]); + al->type(Type::varbool(1)); + Call* forall = new Call(Location().introduce(), constants().ids.forall, {al}); + forall->decl(env.model->matchFn(env, forall, false)); + forall->type(forall->decl()->rtype(env, {al}, false)); + e_then.back().push_back(forall); + } + } + { + if (eq_branches[ite->size()].size() == 0) { + e_else.push_back(ite->e_else()); + } else if (other_branches[ite->size()].size() == 0) { + e_else.push_back(constants().lit_true); + } else if (other_branches[ite->size()].size() == 1) { + e_else.push_back(other_branches[ite->size()][0]); + } else { + ArrayLit* al = new ArrayLit(Location().introduce(), other_branches[ite->size()]); + al->type(Type::varbool(1)); + Call* forall = new Call(Location().introduce(), constants().ids.forall, {al}); + forall->decl(env.model->matchFn(env, forall, false)); + forall->type(forall->decl()->rtype(env, {al}, false)); + e_else.push_back(forall); + } + } + } + } else { + noOtherBranches = false; + results.push_back(r); + e_then.push_back(std::vector()); + for (int i = 0; i < ite->size(); i++) { + e_then.back().push_back(ite->e_then(i)); + } + e_else.push_back(ite->e_else()); + } + allBranchesPar.resize(results.size()); + r_bounds_valid_int.resize(results.size()); + r_bounds_int.resize(results.size()); + r_bounds_valid_float.resize(results.size()); + r_bounds_float.resize(results.size()); + r_bounds_valid_set.resize(results.size()); + r_bounds_set.resize(results.size()); + defined.resize(results.size()); + branches.resize(results.size()); + for (unsigned int i = 0; i < results.size(); i++) { + allBranchesPar[i] = true; + r_bounds_valid_int[i] = true; + r_bounds_valid_float[i] = true; + r_bounds_valid_set[i] = true; + } + + Ctx cmix; + cmix.b = C_MIX; + cmix.i = C_MIX; + + for (int i = 0; i < ite->size(); i++) { + bool cond = true; + EE e_if; + if (ite->e_if(i)->isa() && ite->e_if(i)->cast()->id() == "mzn_in_root_context") { + e_if = EE(constants().boollit(ctx.b == C_ROOT), constants().lit_true); + } else { + e_if = flat_exp(env, cmix, ite->e_if(i), NULL, constants().var_true); + } + if (e_if.r()->type() == Type::parbool()) { + { + GCLock lock; + cond = eval_bool(env, e_if.r()); + } + if (cond) { + if (allConditionsPar || conditions.size() == 0) { + // no var conditions before this one, so we can simply emit + // the then branch + return flat_exp(env, ctx, ite->e_then(i), r, b); + } + // had var conditions, so we have to take them into account + // and emit new conditional clause + Ctx cmix; + cmix.b = C_MIX; + cmix.i = C_MIX; + // add another condition and definedness variable + conditions.push_back(constants().lit_true); + for (unsigned int j = 0; j < results.size(); j++) { + EE ethen = flat_exp(env, cmix, e_then[j][i], NULL, NULL); + assert(ethen.b()); + defined[j].push_back(ethen.b); + allDefined = allDefined && (ethen.b() == constants().lit_true); + branches[j].push_back(ethen.r); + if (ethen.r()->type().isvar()) { + allBranchesPar[j] = false; + } + } + break; + } + } else { + allConditionsPar = false; + // add current condition and definedness variable + conditions.push_back(e_if.r); + + for (unsigned int j = 0; j < results.size(); j++) { + // flatten the then branch + EE ethen = flat_exp(env, cmix, e_then[j][i], NULL, NULL); + + assert(ethen.b()); + defined[j].push_back(ethen.b); + allDefined = allDefined && (ethen.b() == constants().lit_true); + branches[j].push_back(ethen.r); + if (ethen.r()->type().isvar()) { + allBranchesPar[j] = false; + } + } + } + // update bounds + + if (cond) { + for (unsigned int j = 0; j < results.size(); j++) { + if (r_bounds_valid_int[j] && e_then[j][i]->type().isint()) { + GCLock lock; + IntBounds ib_then = compute_int_bounds(env, e_then[j][i]); + if (ib_then.valid) r_bounds_int[j].push_back(ib_then); + r_bounds_valid_int[j] = r_bounds_valid_int[j] && ib_then.valid; + } else if (r_bounds_valid_set[j] && e_then[j][i]->type().isintset()) { + GCLock lock; + IntSetVal* isv = compute_intset_bounds(env, e_then[j][i]); + if (isv) r_bounds_set[j].push_back(isv); + r_bounds_valid_set[j] = r_bounds_valid_set[j] && isv; + } else if (r_bounds_valid_float[j] && e_then[j][i]->type().isfloat()) { + GCLock lock; + FloatBounds fb_then = compute_float_bounds(env, e_then[j][i]); + if (fb_then.valid) r_bounds_float[j].push_back(fb_then); + r_bounds_valid_float[j] = r_bounds_valid_float[j] && fb_then.valid; + } + } + } + } + + if (allConditionsPar) { + // no var condition, and all par conditions were false, + // so simply emit else branch + return flat_exp(env, ctx, ite->e_else(), r, b); + } + + for (unsigned int j = 0; j < results.size(); j++) { + if (results[j] == NULL) { + // need to introduce new result variable + GCLock lock; + TypeInst* ti = new TypeInst(Location().introduce(), ite->type(), NULL); + results[j] = newVarDecl(env, Ctx(), ti, NULL, NULL, NULL); + } + } + + if (conditions.back()() != constants().lit_true) { + // The last condition wasn't fixed to true, we need to look at the else branch + conditions.push_back(constants().lit_true); + + for (unsigned int j = 0; j < results.size(); j++) { + VarDecl* nr = results[j]; + + // update bounds of result with bounds of else branch + + if (r_bounds_valid_int[j] && e_else[j]->type().isint()) { + GCLock lock; + IntBounds ib_else = compute_int_bounds(env, e_else[j]); + if (ib_else.valid) { + r_bounds_int[j].push_back(ib_else); + IntVal lb = IntVal::infinity(); + IntVal ub = -IntVal::infinity(); + for (unsigned int i = 0; i < r_bounds_int[j].size(); i++) { + lb = std::min(lb, r_bounds_int[j][i].l); + ub = std::max(ub, r_bounds_int[j][i].u); + } + if (results[j]) { + IntBounds orig_r_bounds = compute_int_bounds(env, results[j]->id()); + if (orig_r_bounds.valid) { + lb = std::max(lb, orig_r_bounds.l); + ub = std::min(ub, orig_r_bounds.u); + } + } + SetLit* r_dom = new SetLit(Location().introduce(), IntSetVal::a(lb, ub)); + nr->ti()->domain(r_dom); + } + } else if (r_bounds_valid_set[j] && e_else[j]->type().isintset()) { + GCLock lock; + IntSetVal* isv_else = compute_intset_bounds(env, e_else[j]); + if (isv_else) { + IntSetVal* isv = isv_else; + for (unsigned int i = 0; i < r_bounds_set[j].size(); i++) { + IntSetRanges i0(isv); + IntSetRanges i1(r_bounds_set[j][i]); + Ranges::Union u(i0, i1); + isv = IntSetVal::ai(u); + } + if (results[j]) { + IntSetVal* orig_r_bounds = compute_intset_bounds(env, results[j]->id()); + if (orig_r_bounds) { + IntSetRanges i0(isv); + IntSetRanges i1(orig_r_bounds); + Ranges::Inter inter(i0, i1); + isv = IntSetVal::ai(inter); + } + } + SetLit* r_dom = new SetLit(Location().introduce(), isv); + nr->ti()->domain(r_dom); + } + } else if (r_bounds_valid_float[j] && e_else[j]->type().isfloat()) { + GCLock lock; + FloatBounds fb_else = compute_float_bounds(env, e_else[j]); + if (fb_else.valid) { + FloatVal lb = fb_else.l; + FloatVal ub = fb_else.u; + for (unsigned int i = 0; i < r_bounds_float[j].size(); i++) { + lb = std::min(lb, r_bounds_float[j][i].l); + ub = std::max(ub, r_bounds_float[j][i].u); + } + if (results[j]) { + FloatBounds orig_r_bounds = compute_float_bounds(env, results[j]->id()); + if (orig_r_bounds.valid) { + lb = std::max(lb, orig_r_bounds.l); + ub = std::min(ub, orig_r_bounds.u); + } + } + BinOp* r_dom = + new BinOp(Location().introduce(), FloatLit::a(lb), BOT_DOTDOT, FloatLit::a(ub)); + r_dom->type(Type::parfloat(1)); + nr->ti()->domain(r_dom); + } + } + + // flatten else branch + EE eelse = flat_exp(env, cmix, e_else[j], NULL, NULL); + assert(eelse.b()); + defined[j].push_back(eelse.b); + allDefined = allDefined && (eelse.b() == constants().lit_true); + branches[j].push_back(eelse.r); + if (eelse.r()->type().isvar()) { + allBranchesPar[j] = false; + } + } + } + + // Create ite predicate calls + GCLock lock; + ArrayLit* al_cond = new ArrayLit(Location().introduce(), conditions); + al_cond->type(Type::varbool(1)); + for (unsigned int j = 0; j < results.size(); j++) { + ArrayLit* al_branches = new ArrayLit(Location().introduce(), branches[j]); + Type branches_t = results[j]->type(); + branches_t.dim(1); + branches_t.ti(allBranchesPar[j] ? Type::TI_PAR : Type::TI_VAR); + al_branches->type(branches_t); + Call* ite_pred = new Call(ite->loc().introduce(), ASTString("if_then_else"), + {al_cond, al_branches, results[j]->id()}); + ite_pred->decl(env.model->matchFn(env, ite_pred, false)); + ite_pred->type(Type::varbool()); + (void)flat_exp(env, Ctx(), ite_pred, constants().var_true, constants().var_true); + } + EE ret; + if (noOtherBranches) { + ret.r = constants().var_true->id(); + } else { + ret.r = results.back()->id(); + } + if (allDefined) { + bind(env, ctx, b, constants().lit_true); + ret.b = constants().lit_true; + } else { + // Otherwise, constraint linking conditions, b and the definedness variables + if (b == NULL) { + CallStackItem _csi(env, new StringLit(Location().introduce(), "b")); + b = newVarDecl(env, Ctx(), new TypeInst(Location().introduce(), Type::varbool()), NULL, NULL, + NULL); + } + ret.b = b->id(); + + std::vector defined_conjunctions(ite->size() + 1); + for (unsigned int i = 0; i < ite->size(); i++) { + std::vector def_i; + for (unsigned int j = 0; j < defined.size(); j++) { + if (defined[j][i]() != constants().lit_true) { + def_i.push_back(defined[j][i]()); + } + } + if (def_i.size() == 0) { + defined_conjunctions[i] = constants().lit_true; + } else if (def_i.size() == 1) { + defined_conjunctions[i] = def_i[0]; + } else { + ArrayLit* al = new ArrayLit(Location().introduce(), def_i); + al->type(Type::varbool(1)); + Call* forall = new Call(Location().introduce(), constants().ids.forall, {al}); + forall->decl(env.model->matchFn(env, forall, false)); + forall->type(forall->decl()->rtype(env, {al}, false)); + defined_conjunctions[i] = forall; + } + } + ArrayLit* al_defined = new ArrayLit(Location().introduce(), defined_conjunctions); + al_defined->type(Type::varbool(1)); + Call* ite_defined_pred = new Call(ite->loc().introduce(), ASTString("if_then_else_partiality"), + {al_cond, al_defined, b->id()}); + ite_defined_pred->decl(env.model->matchFn(env, ite_defined_pred, false)); + ite_defined_pred->type(Type::varbool()); + (void)flat_exp(env, Ctx(), ite_defined_pred, constants().var_true, constants().var_true); + } + + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_let.cpp b/lib/flatten/flatten_let.cpp new file mode 100644 index 0000000..10347ea --- /dev/null +++ b/lib/flatten/flatten_let.cpp @@ -0,0 +1,130 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_let(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + Let* let = e->cast(); + GC::mark(); + std::vector cs; + std::vector flatmap; + let->pushbindings(); + for (unsigned int i = 0; i < let->let().size(); i++) { + Expression* le = let->let()[i]; + if (VarDecl* vd = le->dyn_cast()) { + Expression* let_e = NULL; + if (vd->e()) { + Ctx nctx = ctx; + nctx.neg = false; + if (vd->e()->type().bt() == Type::BT_BOOL) nctx.b = C_MIX; + + EE ee = flat_exp(env, nctx, vd->e(), NULL, NULL); + let_e = ee.r(); + cs.push_back(ee); + if (vd->ti()->domain() != NULL) { + GCLock lock; + std::vector domargs(2); + domargs[0] = ee.r(); + if (vd->ti()->type().isfloat()) { + FloatSetVal* fsv = eval_floatset(env, vd->ti()->domain()); + if (fsv->size() == 1) { + domargs[1] = FloatLit::a(fsv->min()); + domargs.push_back(FloatLit::a(fsv->max())); + } else { + domargs[1] = vd->ti()->domain(); + } + } else { + domargs[1] = vd->ti()->domain(); + } + Call* c = new Call(vd->ti()->loc().introduce(), "var_dom", domargs); + c->type(Type::varbool()); + c->decl(env.model->matchFn(env, c, false)); + if (c->decl() == NULL) throw InternalError("no matching declaration found for var_dom"); + VarDecl* b_b = (nctx.b == C_ROOT && b == constants().var_true) ? b : NULL; + VarDecl* r_r = (nctx.b == C_ROOT && b == constants().var_true) ? b : NULL; + ee = flat_exp(env, nctx, c, r_r, b_b); + cs.push_back(ee); + ee.b = ee.r; + cs.push_back(ee); + } + if (vd->type().dim() > 0) { + checkIndexSets(env, vd, let_e); + } + } else { + if ((ctx.b == C_NEG || ctx.b == C_MIX) && + !vd->ann().contains(constants().ann.promise_total)) { + CallStackItem csi_vd(env, vd); + throw FlatteningError(env, vd->loc(), "free variable in non-positive context"); + } + CallStackItem csi_vd(env, vd); + GCLock lock; + TypeInst* ti = eval_typeinst(env, vd); + VarDecl* nvd = newVarDecl(env, ctx, ti, NULL, vd, NULL); + let_e = nvd->id(); + } + vd->e(let_e); + flatmap.push_back(vd->flat()); + if (Id* id = Expression::dyn_cast(let_e)) { + vd->flat(id->decl()); + } else { + vd->flat(vd); + } + } else { + if (ctx.b == C_ROOT || le->ann().contains(constants().ann.promise_total)) { + (void)flat_exp(env, Ctx(), le, constants().var_true, constants().var_true); + } else { + EE ee = flat_exp(env, ctx, le, NULL, constants().var_true); + ee.b = ee.r; + cs.push_back(ee); + } + } + } + if (r == constants().var_true && ctx.b == C_ROOT && !ctx.neg) { + ret.b = bind(env, Ctx(), b, constants().lit_true); + (void)flat_exp(env, ctx, let->in(), r, b); + ret.r = conj(env, r, Ctx(), cs); + } else { + Ctx nctx = ctx; + nctx.neg = false; + VarDecl* bb = b; + for (EE& ee : cs) { + if (ee.b() != constants().lit_true) { + bb = NULL; + break; + } + } + EE ee = flat_exp(env, nctx, let->in(), NULL, bb); + if (let->type().isbool() && !let->type().isopt()) { + ee.b = ee.r; + cs.push_back(ee); + ret.r = conj(env, r, ctx, cs); + ret.b = bind(env, Ctx(), b, constants().lit_true); + } else { + cs.push_back(ee); + ret.r = bind(env, Ctx(), r, ee.r()); + ret.b = conj(env, b, Ctx(), cs); + } + } + let->popbindings(); + // Restore previous mapping + for (unsigned int i = 0; i < let->let().size(); i++) { + if (VarDecl* vd = let->let()[i]->dyn_cast()) { + vd->flat(Expression::cast(flatmap.back()())); + flatmap.pop_back(); + } + } + return ret; +} +} // namespace MiniZinc diff --git a/lib/flatten/flatten_par.cpp b/lib/flatten/flatten_par.cpp new file mode 100644 index 0000000..d4a04fc --- /dev/null +++ b/lib/flatten/flatten_par.cpp @@ -0,0 +1,95 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_par(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + EE ret; + if (e->type().cv()) { + KeepAlive ka = flat_cv_exp(env, ctx, e); + ret.r = bind(env, ctx, r, ka()); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } + if (e->type().dim() > 0) { + EnvI::CSEMap::iterator it; + Id* id = e->dyn_cast(); + if (id && (id->decl()->flat() == NULL || id->decl()->toplevel())) { + VarDecl* vd = id->decl()->flat(); + if (vd == NULL) { + vd = flat_exp(env, Ctx(), id->decl(), NULL, constants().var_true).r()->cast()->decl(); + id->decl()->flat(vd); + ArrayLit* al = follow_id(vd->id())->cast(); + if (al->size() == 0) { + if (r == NULL) + ret.r = al; + else + ret.r = bind(env, ctx, r, al); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } + } + ret.r = bind(env, ctx, r, e->cast()->decl()->flat()->id()); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } else if ((it = env.cse_map_find(e)) != env.cse_map_end()) { + ret.r = bind(env, ctx, r, it->second.r()->cast()->id()); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } else { + GCLock lock; + ArrayLit* al = follow_id(eval_par(env, e))->cast(); + if (al->size() == 0 || (r && r->e() == NULL)) { + if (r == NULL) + ret.r = al; + else + ret.r = bind(env, ctx, r, al); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } + if ((it = env.cse_map_find(al)) != env.cse_map_end()) { + ret.r = bind(env, ctx, r, it->second.r()->cast()->id()); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } + std::vector ranges(al->dims()); + for (unsigned int i = 0; i < ranges.size(); i++) { + ranges[i] = + new TypeInst(e->loc(), Type(), + new SetLit(Location().introduce(), IntSetVal::a(al->min(i), al->max(i)))); + } + ASTExprVec ranges_v(ranges); + assert(!al->type().isbot()); + TypeInst* ti = new TypeInst(e->loc(), al->type(), ranges_v, NULL); + VarDecl* vd = newVarDecl(env, ctx, ti, NULL, NULL, al); + EE ee(vd, NULL); + env.cse_map_insert(al, ee); + env.cse_map_insert(vd->e(), ee); + + ret.r = bind(env, ctx, r, vd->id()); + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; + } + } + GCLock lock; + try { + ret.r = bind(env, ctx, r, eval_par(env, e)); + ret.b = bind(env, Ctx(), b, constants().lit_true); + } catch (ResultUndefinedError&) { + ret.r = createDummyValue(env, e->type()); + ret.b = bind(env, Ctx(), b, constants().lit_false); + } + return ret; +} + +} // namespace MiniZinc diff --git a/lib/flatten/flatten_setlit.cpp b/lib/flatten/flatten_setlit.cpp new file mode 100644 index 0000000..7e46b18 --- /dev/null +++ b/lib/flatten/flatten_setlit.cpp @@ -0,0 +1,56 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_setlit(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + SetLit* sl = e->cast(); + assert(sl->isv() == NULL && sl->fsv() == NULL); + std::vector elems_ee(sl->v().size()); + for (unsigned int i = sl->v().size(); i--;) + elems_ee[i] = flat_exp(env, ctx, sl->v()[i], NULL, NULL); + std::vector elems(elems_ee.size()); + bool allPar = true; + for (unsigned int i = static_cast(elems.size()); i--;) { + elems[i] = elems_ee[i].r(); + allPar = allPar && elems[i]->type().ispar(); + } + + ret.b = conj(env, b, Ctx(), elems_ee); + if (allPar) { + GCLock lock; + Expression* ee = eval_par(env, e); + ret.r = bind(env, Ctx(), r, ee); + } else { + GCLock lock; + ArrayLit* al = new ArrayLit(sl->loc(), elems); + al->type(Type::varint(1)); + std::vector args(1); + args[0] = al; + Call* cc = new Call(sl->loc().introduce(), "array2set", args); + cc->type(Type::varsetint()); + FunctionI* fi = env.model->matchFn(env, cc->id(), args, false); + if (fi == NULL) { + throw FlatteningError(env, cc->loc(), "cannot find matching declaration"); + } + assert(fi); + assert(env.isSubtype(fi->rtype(env, args, false), cc->type(), false)); + cc->decl(fi); + EE ee = flat_exp(env, Ctx(), cc, NULL, constants().var_true); + ret.r = bind(env, Ctx(), r, ee.r()); + } + return ret; +} +} // namespace MiniZinc diff --git a/lib/flatten/flatten_unop.cpp b/lib/flatten/flatten_unop.cpp new file mode 100644 index 0000000..c220bf3 --- /dev/null +++ b/lib/flatten/flatten_unop.cpp @@ -0,0 +1,54 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_unop(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + UnOp* uo = e->cast(); + switch (uo->op()) { + case UOT_NOT: { + Ctx nctx = ctx; + nctx.b = -nctx.b; + nctx.neg = !nctx.neg; + ret = flat_exp(env, nctx, uo->e(), r, b); + } break; + case UOT_PLUS: + ret = flat_exp(env, ctx, uo->e(), r, b); + break; + case UOT_MINUS: { + GC::lock(); + if (UnOp* uo_inner = uo->e()->dyn_cast()) { + if (uo_inner->op() == UOT_MINUS) { + ret = flat_exp(env, ctx, uo_inner->e(), r, b); + break; + } + } + Expression* zero; + if (uo->e()->type().bt() == Type::BT_INT) + zero = IntLit::a(0); + else + zero = FloatLit::a(0.0); + BinOp* bo = new BinOp(Location().introduce(), zero, BOT_MINUS, uo->e()); + bo->type(uo->type()); + KeepAlive ka(bo); + GC::unlock(); + ret = flat_exp(env, ctx, ka(), r, b); + } break; + default: + break; + } + return ret; +} +} // namespace MiniZinc diff --git a/lib/flatten/flatten_vardecl.cpp b/lib/flatten/flatten_vardecl.cpp new file mode 100644 index 0000000..978c6f6 --- /dev/null +++ b/lib/flatten/flatten_vardecl.cpp @@ -0,0 +1,58 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +EE flatten_vardecl(EnvI& env, Ctx ctx, Expression* e, VarDecl* r, VarDecl* b) { + CallStackItem _csi(env, e); + EE ret; + GCLock lock; + if (ctx.b != C_ROOT) throw FlatteningError(env, e->loc(), "not in root context"); + VarDecl* v = e->cast(); + VarDecl* it = v->flat(); + if (it == NULL) { + TypeInst* ti = eval_typeinst(env, v); + bool reuseVarId = + v->type().isann() || (v->toplevel() && v->id()->idn() == -1 && + v->id()->v().c_str()[0] != '\'' && v->id()->v().c_str()[0] != '_'); + VarDecl* vd = newVarDecl(env, ctx, ti, reuseVarId ? v->id() : NULL, v, NULL); + v->flat(vd); + Ctx nctx; + if (v->e() && v->e()->type().bt() == Type::BT_BOOL) nctx.b = C_MIX; + if (v->e()) { + (void)flat_exp(env, nctx, v->e(), vd, constants().var_true); + if (v->e()->type().dim() > 0) { + Expression* ee = follow_id_to_decl(vd->e()); + if (ee->isa()) ee = ee->cast()->e(); + assert(ee && ee->isa()); + ArrayLit* al = ee->cast(); + if (vd->ti()->domain()) { + for (unsigned int i = 0; i < al->size(); i++) { + if (Id* ali_id = (*al)[i]->dyn_cast()) { + if (ali_id->decl()->ti()->domain() == NULL) { + ali_id->decl()->ti()->domain(vd->ti()->domain()); + } + } + } + } + } + } + + ret.r = bind(env, Ctx(), r, vd->id()); + } else { + ret.r = bind(env, Ctx(), r, it); + } + ret.b = bind(env, Ctx(), b, constants().lit_true); + return ret; +} +} // namespace MiniZinc diff --git a/lib/flattener.cpp b/lib/flattener.cpp new file mode 100644 index 0000000..54f0c4f --- /dev/null +++ b/lib/flattener.cpp @@ -0,0 +1,836 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was ! distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A basic mzn2fzn wrapper, can be used as a plugin + */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include + +#include + +#ifdef HAS_GECODE +#include +#endif + +using namespace std; +using namespace MiniZinc; + +void Flattener::printVersion(ostream& os) { + os << "MiniZinc to FlatZinc converter, version " << MZN_VERSION_MAJOR << "." << MZN_VERSION_MINOR + << "." << MZN_VERSION_PATCH; + if (!std::string(MZN_BUILD_REF).empty()) { + os << ", build " << MZN_BUILD_REF; + } + os << std::endl; + os << "Copyright (C) 2014-" << string(__DATE__).substr(7, 4) + << " Monash University, NICTA, Data61" << std::endl; +} + +void Flattener::printHelp(ostream& os) { + os << std::endl + << "Flattener input options:" << std::endl + << " --ignore-stdlib\n Ignore the standard libraries stdlib.mzn and builtins.mzn" + << std::endl + << " --instance-check-only\n Check the model instance (including data) for errors, but do " + "not\n convert to FlatZinc." + << std::endl + << " -e, --model-check-only\n Check the model (without requiring data) for errors, but do " + "not\n convert to FlatZinc." + << std::endl + << " --model-interface-only\n Only extract parameters and output variables." << std::endl + << " --model-types-only\n Only output variable (enum) type information." << std::endl + << " --no-optimize\n Do not optimize the FlatZinc" << std::endl + << " --no-chain-compression\n Do not simplify chains of implication constraints." + << std::endl + << " -d , --data \n File named contains data used by the model." + << std::endl + << " -D , --cmdline-data \n Include the given data assignment in the model." + << std::endl + << " --stdlib-dir \n Path to MiniZinc standard library directory" << std::endl + << " -G , --globals-dir , --mzn-globals-dir \n Search for included globals " + "in /." + << std::endl + << " -, --input-from-stdin\n Read problem from standard input" << std::endl + << " -I , --search-dir \n Additionally search for included files in ." + << std::endl + << " -D \"fMIPdomains=true\"\n Switch on MIPDomain Unification" << std::endl + << " --MIPDMaxIntvEE \n MIPD: max integer domain subinterval length to enforce " + "equality encoding, default " + << opt_MIPDmaxIntvEE << std::endl + << " --MIPDMaxDensEE \n MIPD: max domain cardinality to N subintervals ratio\n to " + "enforce equality encoding, default " + << opt_MIPDmaxDensEE << ", either condition triggers" << std::endl + << " --only-range-domains\n When no MIPdomains: all domains contiguous, holes replaced by " + "inequalities" + << std::endl + << " --allow-multiple-assignments\n Allow multiple assignments to the same variable (e.g. " + "in dzn)" + << std::endl + << " --no-half-reifications\n Only use fully reified constraints, even when a half " + "reified constraint is defined." + << std::endl + << " --compile-solution-checker .mzc.mzn\n Compile solution checker model" + << std::endl + << std::endl + << "Flattener two-pass options:" << std::endl + << " --two-pass\n Flatten twice to make better flattening decisions for the target" + << std::endl +#ifdef HAS_GECODE + << " --use-gecode\n Perform root-node-propagation with Gecode (adds --two-pass)" + << std::endl + << " --shave\n Probe bounds of all variables at the root node (adds --use-gecode)" + << std::endl + << " --sac\n Probe values of all variables at the root node (adds --use-gecode)" + << std::endl + << " --pre-passes \n Number of times to apply shave/sac pass (0 = fixed-point, 1 = " + "default)" + << std::endl +#endif + << " -O\n Two-pass optimisation levels:" << std::endl + << " -O0: Disable optimize (--no-optimize) -O1: Single pass (default)" << std::endl + << " -O2: Same as: --two-pass" +#ifdef HAS_GECODE + << " -O3: Same as: --use-gecode" << std::endl + << " -O4: Same as: --shave -O5: Same as: --sac" << std::endl +#else + << "\n -O3,4,5: Disabled [Requires MiniZinc with built-in Gecode support]" << std::endl +#endif + << " -g\n Debug mode: Forces -O0 and records all domain changes as constraints instead of " + "applying them" + << std::endl + << std::endl; + os << "Flattener output options:" << std::endl + << " --no-output-ozn, -O-\n Do not output ozn file" << std::endl + << " --output-base \n Base name for output files" << std::endl + << (fOutputByDefault + ? " -o , --fzn , --output-to-file , --output-fzn-to-file \n" + : " --fzn , --output-fzn-to-file \n") + << " Filename for generated FlatZinc output" << std::endl + << " -O, --ozn, --output-ozn-to-file \n Filename for model output specification " + "(-O- for none)" + << std::endl + << " --keep-paths\n Don't remove path annotations from FlatZinc" << std::endl + << " --output-paths\n Output a symbol table (.paths file)" << std::endl + << " --output-paths-to-file \n Output a symbol table (.paths file) to " + << std::endl + << " --output-to-stdout, --output-fzn-to-stdout\n Print generated FlatZinc to standard " + "output" + << std::endl + << " --output-ozn-to-stdout\n Print model output specification to standard output" + << std::endl + << " --output-paths-to-stdout\n Output symbol table to standard output" << std::endl + << " --output-mode \n Create output according to output item (default), or " + "output compatible\n with dzn or json format" + << std::endl + << " --output-objective\n Print value of objective function in dzn or json output" + << std::endl + << " --output-output-item\n Print the output item as a string in the dzn or json output" + << std::endl + << " -Werror\n Turn warnings into errors" << std::endl; +} + +bool Flattener::processOption(int& i, std::vector& argv) { + CLOParser cop(i, argv); + string buffer; + + if (cop.getOption("-I --search-dir", &buffer)) { + includePaths.push_back(buffer + string("/")); + } else if (cop.getOption("--ignore-stdlib")) { + flag_ignoreStdlib = true; + } else if (cop.getOption("--no-typecheck")) { + flag_typecheck = false; + } else if (cop.getOption("--instance-check-only")) { + flag_instance_check_only = true; + } else if (cop.getOption("-e --model-check-only")) { + flag_model_check_only = true; + } else if (cop.getOption("--model-interface-only")) { + flag_model_interface_only = true; + } else if (cop.getOption("--model-types-only")) { + flag_model_types_only = true; + } else if (cop.getOption("-v --verbose")) { + flag_verbose = true; + } else if (string(argv[i]) == string("--newfzn")) { + flag_newfzn = true; + } else if (cop.getOption("--no-optimize --no-optimise")) { + flag_optimize = false; + } else if (cop.getOption("--no-chain-compression")) { + flag_chain_compression = false; + } else if (cop.getOption("--no-output-ozn -O-")) { + flag_no_output_ozn = true; + } else if (cop.getOption("--output-base", &flag_output_base)) { + } else if (cop.getOption(fOutputByDefault ? "-o --fzn --output-to-file --output-fzn-to-file" + : "--fzn --output-fzn-to-file", + &flag_output_fzn)) { + } else if (cop.getOption("--output-paths-to-file", &flag_output_paths)) { + fopts.collect_mzn_paths = true; + } else if (cop.getOption("--output-paths")) { + fopts.collect_mzn_paths = true; + } else if (cop.getOption("--output-to-stdout --output-fzn-to-stdout")) { + flag_output_fzn_stdout = true; + } else if (cop.getOption("--output-ozn-to-stdout")) { + flag_output_ozn_stdout = true; + } else if (cop.getOption("--output-paths-to-stdout")) { + fopts.collect_mzn_paths = true; + flag_output_paths_stdout = true; + } else if (cop.getOption("--output-mode", &buffer)) { + if (buffer == "dzn") { + flag_output_mode = FlatteningOptions::OUTPUT_DZN; + } else if (buffer == "json") { + flag_output_mode = FlatteningOptions::OUTPUT_JSON; + } else if (buffer == "item") { + flag_output_mode = FlatteningOptions::OUTPUT_ITEM; + } else { + return false; + } + } else if (cop.getOption("--output-objective")) { + flag_output_objective = true; + } else if (cop.getOption("--output-output-item")) { + flag_output_output_item = true; + } else if (cop.getOption("- --input-from-stdin")) { + flag_stdinInput = true; + } else if (cop.getOption("-d --data", &buffer)) { + if (buffer.length() <= 4 || buffer.substr(buffer.length() - 4, string::npos) != ".dzn") + return false; + datafiles.push_back(buffer); + } else if (cop.getOption("--stdlib-dir", &std_lib_dir)) { + } else if (cop.getOption("-G --globals-dir --mzn-globals-dir", &globals_dir)) { + } else if (cop.getOption("-D --cmdline-data", &buffer)) { + datafiles.push_back("cmd:/" + buffer); + } else if (cop.getOption("--allow-unbounded-vars")) { + flag_allow_unbounded_vars = true; + } else if (cop.getOption("--only-range-domains")) { + flag_only_range_domains = true; + } else if (cop.getOption("--no-MIPdomains")) { // internal + flag_noMIPdomains = true; + } else if (cop.getOption("--MIPDMaxIntvEE", &opt_MIPDmaxIntvEE)) { + } else if (cop.getOption("--MIPDMaxDensEE", &opt_MIPDmaxDensEE)) { + } else if (cop.getOption("-Werror")) { + flag_werror = true; + } else if (string(argv[i]) == "--use-gecode") { +#ifdef HAS_GECODE + flag_two_pass = true; + flag_gecode = true; +#else + log << "warning: Gecode not available. Ignoring '--use-gecode'\n"; +#endif + } else if (string(argv[i]) == "--sac") { +#ifdef HAS_GECODE + flag_two_pass = true; + flag_gecode = true; + flag_sac = true; +#else + log << "warning: Gecode not available. Ignoring '--sac'\n"; +#endif + + } else if (string(argv[i]) == "--shave") { +#ifdef HAS_GECODE + flag_two_pass = true; + flag_gecode = true; + flag_shave = true; +#else + log << "warning: Gecode not available. Ignoring '--shave'\n"; +#endif + } else if (string(argv[i]) == "--two-pass") { + flag_two_pass = true; + } else if (string(argv[i]) == "--npass") { + i++; + if (i == argv.size()) return false; + log << "warning: --npass option is deprecated --two-pass\n"; + int passes = atoi(argv[i].c_str()); + if (passes == 1) + flag_two_pass = false; + else if (passes == 2) + flag_two_pass = true; + } else if (string(argv[i]) == "--pre-passes") { + i++; + if (i == argv.size()) return false; + int passes = atoi(argv[i].c_str()); + if (passes >= 0) { + flag_pre_passes = static_cast(passes); + } + } else if (string(argv[i]) == "-O0") { + flag_optimize = false; + } else if (string(argv[i]) == "-O1") { + // Default settings + } else if (string(argv[i]) == "-O2") { + flag_two_pass = true; +#ifdef HAS_GECODE + } else if (string(argv[i]) == "-O3") { + flag_two_pass = true; + flag_gecode = true; + } else if (string(argv[i]) == "-O4") { + flag_two_pass = true; + flag_gecode = true; + flag_shave = true; + } else if (string(argv[i]) == "-O5") { + flag_two_pass = true; + flag_gecode = true; + flag_sac = true; +#else + } else if (string(argv[i]) == "-O3" || string(argv[i]) == "-O4" || string(argv[i]) == "-O5") { + log << "% Warning: This compiler does not have Gecode builtin, cannot process -O3,-O4,-O5.\n"; + return false; +#endif + // ozn options must be after the -O optimisation options + } else if (cop.getOption("-O --ozn --output-ozn-to-file", &flag_output_ozn)) { + } else if (string(argv[i]) == "-g") { + flag_optimize = false; + flag_two_pass = false; + flag_gecode = false; + flag_shave = false; + flag_sac = false; + fopts.record_domain_changes = true; + } else if (string(argv[i]) == "--keep-paths") { + flag_keep_mzn_paths = true; + fopts.collect_mzn_paths = true; + } else if (string(argv[i]) == "--only-toplevel-presolve") { + fopts.only_toplevel_paths = true; + } else if (cop.getOption("--allow-multiple-assignments")) { + flag_allow_multi_assign = true; + } else if (cop.getOption("--no-half-reifications")) { + fopts.enable_imp = false; + } else if (string(argv[i]) == "--input-is-flatzinc") { + is_flatzinc = true; + } else if (cop.getOption("--compile-solution-checker", &buffer)) { + if (buffer.length() >= 8 && buffer.substr(buffer.length() - 8, string::npos) == ".mzc.mzn") { + flag_compile_solution_check_model = true; + flag_model_check_only = true; + filenames.push_back(buffer); + } else { + log << "Error: solution checker model must have extension .mzc.mzn" << std::endl; + return false; + } + } else { + std::string input_file(argv[i]); + if (input_file.length() <= 4) { + return false; + } + size_t last_dot = input_file.find_last_of('.'); + if (last_dot == string::npos) { + return false; + } + std::string extension = input_file.substr(last_dot, string::npos); + if (extension == ".mzc" || + (input_file.length() >= 8 && + input_file.substr(input_file.length() - 8, string::npos) == ".mzc.mzn")) { + flag_solution_check_model = input_file; + } else if (extension == ".mzn" || extension == ".fzn") { + if (extension == ".fzn") { + is_flatzinc = true; + if (fOutputByDefault) // mzn2fzn mode + return false; + } + filenames.push_back(input_file); + } else if (extension == ".dzn" || extension == ".json") { + datafiles.push_back(input_file); + } else { + if (fOutputByDefault) + log << "Error: cannot handle file extension " << extension << "." << std::endl; + return false; + } + } + return true; +} + +Flattener::Flattener(std::ostream& os_, std::ostream& log_, const std::string& stdlibDir) + : os(os_), log(log_), std_lib_dir(stdlibDir) {} + +Flattener::~Flattener() { + if (pEnv.get()) { // ??? TODO + if (is_flatzinc) { + pEnv->swap(); + } + } +} + +Env* Flattener::multiPassFlatten(const vector >& passes) { + Env& e = *getEnv(); + + Env* pre_env = &e; + size_t npasses = passes.size(); + pre_env->envi().final_pass_no = static_cast(npasses); + Timer starttime; + bool verbose = false; + for (unsigned int i = 0; i < passes.size(); i++) { + pre_env->envi().current_pass_no = i; + if (verbose) log << "Start pass " << i << ":\n"; + + Env* out_env = passes[i]->run(pre_env, log); + if (out_env == nullptr) return nullptr; + if (pre_env != &e && pre_env != out_env) { + delete pre_env; + } + pre_env = out_env; + + if (verbose) log << "Finish pass " << i << ": " << starttime.stoptime() << "\n"; + } + + return pre_env; +} + +class FlattenTimeout { +public: + FlattenTimeout(unsigned long long int t) { GC::setTimeout(t); } + ~FlattenTimeout(void) { GC::setTimeout(0); } +}; + +void Flattener::flatten(const std::string& modelString, const std::string& modelName) { + FlattenTimeout flatten_timeout(fopts.timeout); + Timer flatten_time; + starttime.reset(); + + if (flag_verbose) printVersion(log); + + if (filenames.empty() && !flag_solution_check_model.empty()) { + // Compile solution check model as if it were a normal model + filenames.push_back(flag_solution_check_model); + flag_solution_check_model = ""; + } + + if (filenames.empty() && !flag_stdinInput && modelString.empty()) { + throw Error("Error: no model file given."); + } + + if (std_lib_dir == "") { + throw Error( + "Error: unknown minizinc standard library directory.\n" + "Specify --stdlib-dir on the command line or set the\n" + "MZN_STDLIB_DIR environment variable."); + } + + if (globals_dir != "") { + includePaths.insert(includePaths.begin(), std_lib_dir + "/" + globals_dir + "/"); + } + includePaths.push_back(std_lib_dir + "/std/"); + + for (unsigned int i = 0; i < includePaths.size(); i++) { + if (!FileUtils::directory_exists(includePaths[i])) { + throw Error("Cannot access include directory " + includePaths[i]); + } + } + + if (flag_output_base == "") { + if (filenames.empty()) { + flag_output_base = "mznout"; + } else { + flag_output_base = filenames[0].substr(0, filenames[0].length() - 4); + } + } + + if (filenames.end() != find(filenames.begin(), filenames.end(), flag_output_fzn) || + datafiles.end() != find(datafiles.begin(), datafiles.end(), flag_output_fzn)) { + log << " WARNING: fzn filename '" << flag_output_fzn << "' matches an input file, ignoring." + << endl; + flag_output_fzn = ""; + } + if (filenames.end() != find(filenames.begin(), filenames.end(), flag_output_ozn) || + datafiles.end() != find(datafiles.begin(), datafiles.end(), flag_output_ozn)) { + log << " WARNING: ozn filename '" << flag_output_ozn << "' matches an input file, ignoring." + << endl; + flag_output_ozn = ""; + } + + if (fOutputByDefault) { + if (flag_output_fzn == "") { + flag_output_fzn = flag_output_base + ".fzn"; + } + if (flag_output_paths == "" && fopts.collect_mzn_paths) { + flag_output_paths = flag_output_base + ".paths"; + } + if (flag_output_ozn == "" && !flag_no_output_ozn) { + flag_output_ozn = flag_output_base + ".ozn"; + } + } + + { + std::stringstream errstream; + + Model* m; + pEnv.reset(new Env(NULL, os, log)); + Env* env = getEnv(); + + if (!flag_compile_solution_check_model && !flag_solution_check_model.empty()) { + // Extract variables to check from solution check model + if (flag_verbose) + log << "Parsing solution checker model " << flag_solution_check_model << " ..." << endl; + bool isCompressedChecker = + flag_solution_check_model.size() >= 4 && + flag_solution_check_model.substr(flag_solution_check_model.size() - 4) == ".mzc"; + std::vector smm_model({flag_solution_check_model}); + Model* smm = parse(*env, smm_model, datafiles, "", "", includePaths, flag_ignoreStdlib, false, + flag_verbose, errstream); + if (flag_verbose) log << " done parsing (" << starttime.stoptime() << ")" << std::endl; + if (smm) { + log << errstream.str(); + errstream.str(""); + std::ostringstream smm_oss; + Printer p(smm_oss, 0, false); + p.print(smm); + Env smm_env(smm); + GCLock lock; + vector typeErrors; + try { + MiniZinc::typecheck(smm_env, smm, typeErrors, true, false, true); + if (typeErrors.size() > 0) { + if (!isCompressedChecker) { + for (unsigned int i = 0; i < typeErrors.size(); i++) { + if (flag_verbose) log << std::endl; + log << typeErrors[i].loc() << ":" << std::endl; + log << typeErrors[i].what() << ": " << typeErrors[i].msg() << std::endl; + } + } + throw Error("multiple type errors"); + } + for (unsigned int i = 0; i < smm->size(); i++) { + if (VarDeclI* vdi = (*smm)[i]->dyn_cast()) { + if (vdi->e()->e() == NULL) env->envi().checkVars.push_back(vdi->e()); + } + } + smm->compact(); + std::string smm_compressed = + FileUtils::encodeBase64(FileUtils::deflateString(smm_oss.str())); + TypeInst* ti = new TypeInst(Location().introduce(), Type::parstring(), NULL); + VarDecl* checkString = + new VarDecl(Location().introduce(), ti, ASTString("_mzn_solution_checker"), + new StringLit(Location().introduce(), smm_compressed)); + VarDeclI* checkStringI = new VarDeclI(Location().introduce(), checkString); + env->output()->addItem(checkStringI); + } catch (TypeError& e) { + if (isCompressedChecker) { + log << "Warning: type error in solution checker model\n"; + } else { + throw; + } + } + } else { + if (isCompressedChecker) { + log << "Warning: syntax error in solution checker model\n"; + } else { + log << errstream.str(); + throw Error("parse error"); + } + } + } + + if (flag_compile_solution_check_model) { + if (!modelString.empty()) { + throw Error("Cannot compile solution checker model with additional model inputs."); + } + if (flag_stdinInput) { + throw Error( + "Cannot compile solution checker model with additional model from standard input."); + } + if (filenames.size() != 1) { + throw Error("Cannot compile solution checker model with more than one model given."); + } + } + + if (!flag_solution_check_model.empty() && filenames.size() == 0) { + throw Error("Cannot run solution checker without model."); + } + + std::string modelText = modelString; + if (flag_stdinInput) { + std::string input = + std::string(istreambuf_iterator(std::cin), istreambuf_iterator()); + modelText += input; + } + + if (flag_verbose) { + log << "Parsing file(s) "; + for (int i = 0; i < filenames.size(); ++i) + log << (i == 0 ? "" : ", '") << filenames[i] << '\''; + for (const auto& sFln : datafiles) log << ", '" << sFln << '\''; + log << " ..." << std::endl; + } + errstream.str(""); + m = parse(*env, filenames, datafiles, modelText, modelName.empty() ? "stdin" : modelName, + includePaths, flag_ignoreStdlib, false, flag_verbose, errstream); + if (globals_dir != "") { + includePaths.erase(includePaths.begin()); + } + if (m == NULL) throw Error(errstream.str()); + log << errstream.str(); + env->model(m); + if (flag_typecheck) { + if (flag_verbose) log << " done parsing (" << starttime.stoptime() << ")" << std::endl; + + if (flag_instance_check_only || flag_model_check_only || flag_model_interface_only || + flag_model_types_only) { + std::ostringstream compiledSolutionCheckModel; + if (flag_compile_solution_check_model) { + Printer p(compiledSolutionCheckModel, 0); + p.print(m); + } + GCLock lock; + vector typeErrors; + MiniZinc::typecheck( + *env, m, typeErrors, + flag_model_types_only || flag_model_interface_only || flag_model_check_only, + flag_allow_multi_assign); + if (typeErrors.size() > 0) { + for (unsigned int i = 0; i < typeErrors.size(); i++) { + if (flag_verbose) log << std::endl; + log << typeErrors[i].loc() << ":" << std::endl; + log << typeErrors[i].what() << ": " << typeErrors[i].msg() << std::endl; + } + throw Error("multiple type errors"); + } + if (flag_model_interface_only) { + MiniZinc::output_model_interface(*env, m, os); + } + if (flag_model_types_only) { + MiniZinc::output_model_variable_types(*env, m, os); + } + if (flag_compile_solution_check_model) { + std::string mzc(FileUtils::deflateString(compiledSolutionCheckModel.str())); + mzc = FileUtils::encodeBase64(mzc); + std::string mzc_filename = filenames[0].substr(0, filenames[0].size() - 4); + if (flag_verbose) log << "Write solution checker to " << mzc_filename << "\n"; + std::ofstream mzc_f(mzc_filename); + mzc_f << mzc; + mzc_f.close(); + } + status = SolverInstance::NONE; + } else { + if (is_flatzinc) { + GCLock lock; + vector typeErrors; + MiniZinc::typecheck(*env, m, typeErrors, + flag_model_check_only || flag_model_interface_only, + flag_allow_multi_assign, true); + if (typeErrors.size() > 0) { + for (unsigned int i = 0; i < typeErrors.size(); i++) { + if (flag_verbose) log << std::endl; + log << typeErrors[i].loc() << ":" << std::endl; + log << typeErrors[i].what() << ": " << typeErrors[i].msg() << std::endl; + } + throw Error("multiple type errors"); + } + MiniZinc::registerBuiltins(*env); + env->swap(); + populateOutput(*env); + } else { + if (flag_verbose) log << "Flattening ..."; + + fopts.onlyRangeDomains = flag_only_range_domains; + fopts.verbose = flag_verbose; + fopts.outputMode = flag_output_mode; + fopts.outputObjective = flag_output_objective; + fopts.outputOutputItem = flag_output_output_item; +#ifdef HAS_GECODE + GecodeOptions gopts; + gopts.only_range_domains = flag_only_range_domains; + gopts.sac = flag_sac; + gopts.allow_unbounded_vars = flag_allow_unbounded_vars; + gopts.shave = flag_shave; + gopts.printStatistics = flag_statistics; + gopts.pre_passes = flag_pre_passes; +#endif + FlatteningOptions pass_opts = fopts; + CompilePassFlags cfs; + cfs.noMIPdomains = flag_noMIPdomains; + cfs.verbose = flag_verbose; + cfs.statistics = flag_statistics; + cfs.optimize = flag_optimize; + cfs.chain_compression = flag_chain_compression; + cfs.newfzn = flag_newfzn; + cfs.werror = flag_werror; + cfs.model_check_only = flag_model_check_only; + cfs.model_interface_only = flag_model_interface_only; + cfs.allow_multi_assign = flag_allow_multi_assign; + + std::vector > managed_passes; + + if (flag_two_pass) { + std::string library = std_lib_dir + (flag_gecode ? "/gecode_presolver/" : "/std/"); + bool differentLibrary = (library != std_lib_dir + "/" + globals_dir + "/"); + managed_passes.emplace_back(new CompilePass(env, pass_opts, cfs, library, includePaths, + true, differentLibrary)); +#ifdef HAS_GECODE + if (flag_gecode) managed_passes.emplace_back(new GecodePass(&gopts)); +#endif + } + managed_passes.emplace_back(new CompilePass(env, fopts, cfs, + std_lib_dir + "/" + globals_dir + "/", + includePaths, flag_two_pass, false)); + + Env* out_env = multiPassFlatten(managed_passes); + if (out_env == nullptr) exit(EXIT_FAILURE); + + if (out_env != env) { + pEnv.reset(out_env); + } + env = out_env; + if (flag_verbose) + log << " done (" << starttime.stoptime() << ")," + << " max stack depth " << env->maxCallStack() << std::endl; + } + + if (flag_statistics) { + FlatModelStatistics stats = statistics(env->flat()); + os << "% Generated FlatZinc statistics:\n"; + + os << "%%%mzn-stat: paths=" << env->envi().getPathMap().size() << endl; + + if (stats.n_bool_vars) { + os << "%%%mzn-stat: flatBoolVars=" << stats.n_bool_vars << endl; + } + if (stats.n_int_vars) { + os << "%%%mzn-stat: flatIntVars=" << stats.n_int_vars << endl; + } + if (stats.n_float_vars) { + os << "%%%mzn-stat: flatFloatVars=" << stats.n_float_vars << endl; + } + if (stats.n_set_vars) { + os << "%%%mzn-stat: flatSetVars=" << stats.n_set_vars << endl; + } + + if (stats.n_bool_ct) { + os << "%%%mzn-stat: flatBoolConstraints=" << stats.n_bool_ct << endl; + } + if (stats.n_int_ct) { + os << "%%%mzn-stat: flatIntConstraints=" << stats.n_int_ct << endl; + } + if (stats.n_float_ct) { + os << "%%%mzn-stat: flatFloatConstraints=" << stats.n_float_ct << endl; + } + if (stats.n_set_ct) { + os << "%%%mzn-stat: flatSetConstraints=" << stats.n_set_ct << endl; + } + + if (stats.n_reif_ct) { + os << "%%%mzn-stat: evaluatedReifiedConstraints=" << stats.n_reif_ct << endl; + } + if (stats.n_imp_ct) { + os << "%%%mzn-stat: evaluatedHalfReifiedConstraints=" << stats.n_imp_ct << endl; + } + + if (stats.n_imp_del) { + os << "%%%mzn-stat: eliminatedImplications=" << stats.n_imp_del << endl; + } + if (stats.n_lin_del) { + os << "%%%mzn-stat: eliminatedLinearConstraints=" << stats.n_lin_del << endl; + } + + /// Objective / SAT. These messages are used by mzn-test.py. + SolveI* solveItem = env->flat()->solveItem(); + if (solveItem->st() != SolveI::SolveType::ST_SAT) { + if (solveItem->st() == SolveI::SolveType::ST_MAX) { + os << "%%%mzn-stat: method=\"maximize\"" << endl; + } else { + os << "%%%mzn-stat: method=\"minimize\"" << endl; + } + } else { + os << "%%%mzn-stat: method=\"satisfy\"" << endl; + } + + os << "%%%mzn-stat: flatTime=" << flatten_time.s() << endl; + os << "%%%mzn-stat-end" << endl << endl; + } + + if (flag_output_paths_stdout) { + if (flag_verbose) log << "Printing Paths to stdout ..." << std::endl; + PathFilePrinter pfp(os, env->envi()); + pfp.print(env->flat()); + if (flag_verbose) log << " done (" << starttime.stoptime() << ")" << std::endl; + } else if (flag_output_paths != "") { + if (flag_verbose) + log << "Printing Paths to '" << flag_output_paths << "' ..." << std::flush; + std::ofstream ofs; + ofs.open(flag_output_paths.c_str(), ios::out); + checkIOStatus(ofs.good(), " I/O error: cannot open fzn output file. "); + PathFilePrinter pfp(ofs, env->envi()); + pfp.print(env->flat()); + checkIOStatus(ofs.good(), " I/O error: cannot write fzn output file. "); + ofs.close(); + if (flag_verbose) log << " done (" << starttime.stoptime() << ")" << std::endl; + } + + if ((fopts.collect_mzn_paths || flag_two_pass) && !flag_keep_mzn_paths) { + class RemovePathAnnotations : public ItemVisitor { + public: + void removePath(Annotation& a) const { a.removeCall(constants().ann.mzn_path); } + void vVarDeclI(VarDeclI* vdi) const { removePath(vdi->e()->ann()); } + void vConstraintI(ConstraintI* ci) const { removePath(ci->e()->ann()); } + void vSolveI(SolveI* si) const { + removePath(si->ann()); + if (Expression* e = si->e()) removePath(e->ann()); + } + } removePaths; + iterItems(removePaths, env->flat()); + } + + if (flag_output_fzn_stdout) { + if (flag_verbose) log << "Printing FlatZinc to stdout ..." << std::endl; + Printer p(os, 0); + p.print(env->flat()); + if (flag_verbose) log << " done (" << starttime.stoptime() << ")" << std::endl; + } else if (flag_output_fzn != "") { + if (flag_verbose) + log << "Printing FlatZinc to '" << flag_output_fzn << "' ..." << std::flush; + std::ofstream ofs; + ofs.open(flag_output_fzn.c_str(), ios::out); + checkIOStatus(ofs.good(), " I/O error: cannot open fzn output file. "); + Printer p(ofs, 0); + p.print(env->flat()); + checkIOStatus(ofs.good(), " I/O error: cannot write fzn output file. "); + ofs.close(); + if (flag_verbose) log << " done (" << starttime.stoptime() << ")" << std::endl; + } + if (!flag_no_output_ozn) { + if (flag_output_ozn_stdout) { + if (flag_verbose) log << "Printing .ozn to stdout ..." << std::endl; + Printer p(os, 0); + p.print(env->output()); + if (flag_verbose) log << " done (" << starttime.stoptime() << ")" << std::endl; + } else if (flag_output_ozn != "") { + if (flag_verbose) + log << "Printing .ozn to '" << flag_output_ozn << "' ..." << std::flush; + std::ofstream ofs; + ofs.open(flag_output_ozn.c_str(), std::ios::out); + checkIOStatus(ofs.good(), " I/O error: cannot open ozn output file. "); + Printer p(ofs, 0); + p.print(env->output()); + checkIOStatus(ofs.good(), " I/O error: cannot write ozn output file. "); + ofs.close(); + if (flag_verbose) log << " done (" << starttime.stoptime() << ")" << std::endl; + } + } + } + } else { // !flag_typecheck + Printer p(os); + p.print(m); + } + } + + if (getEnv()->envi().failed()) { + status = SolverInstance::UNSAT; + } + + if (flag_verbose) { + size_t mem = GC::maxMem(); + if (mem < 1024) + log << "Maximum memory " << mem << " bytes"; + else if (mem < 1024 * 1024) + log << "Maximum memory " << mem / 1024 << " Kbytes"; + else + log << "Maximum memory " << mem / (1024 * 1024) << " Mbytes"; + log << "." << std::endl; + } +} + +void Flattener::printStatistics(ostream&) {} diff --git a/lib/gc.cpp b/lib/gc.cpp new file mode 100644 index 0000000..1e1cd58 --- /dev/null +++ b/lib/gc.cpp @@ -0,0 +1,809 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//#define MINIZINC_GC_STATS + +#if defined(MINIZINC_GC_STATS) +#include +#endif + +namespace MiniZinc { + +GC*& GC::gc(void) { +#if defined(HAS_DECLSPEC_THREAD) + __declspec(thread) static GC* gc = NULL; +#elif defined(HAS_ATTR_THREAD) + static __thread GC* gc = NULL; +#else +#error Need thread-local storage +#endif + return gc; +} + +bool GC::locked(void) { + assert(gc()); + return gc()->_lock_count > 0; +} + +GCLock::GCLock(void) { GC::lock(); } +GCLock::~GCLock(void) { GC::unlock(); } + +class FreeListNode : public ASTNode { +public: + FreeListNode* next; + size_t size; + FreeListNode(size_t s, FreeListNode* n) : ASTNode(ASTNode::NID_FL), next(n), size(s) { + _gc_mark = 1; + } + FreeListNode(size_t s) : ASTNode(ASTNode::NID_FL), next(NULL), size(s) {} +}; + +class HeapPage { +public: + HeapPage* next; + size_t size; + size_t used; + char data[1]; + HeapPage(HeapPage* n, size_t s) : next(n), size(s), used(0) {} +}; + +/// Memory managed by the garbage collector +class GC::Heap { + friend class GC; +#if defined(MINIZINC_GC_STATS) + static const char* _nodeid[Item::II_END + 1]; + struct GCStat { + int first; + int second; + int keepalive; + int inmodel; + size_t total; + GCStat(void) : first(0), second(0), keepalive(0), inmodel(0), total(0) {} + }; + std::map gc_stats; +#endif +protected: + static const size_t _min_gc_threshold; + + HeapPage* _page; + Model* _rootset; + KeepAlive* _roots; + WeakRef* _weakRefs; + ASTNodeWeakMap* _nodeWeakMaps; + static const int _max_fl = 5; + FreeListNode* _fl[_max_fl + 1]; + static const size_t _fl_size[_max_fl + 1]; + int _fl_slot(size_t _size) { + size_t size = _size; + assert(size <= _fl_size[_max_fl]); + assert(size >= _fl_size[0]); + size -= sizeof(Item); + assert(size % sizeof(void*) == 0); + size /= sizeof(void*); + assert(size >= 1); + int slot = static_cast(size) - 1; + return slot; + } + + /// Total amount of memory allocated + size_t _alloced_mem; + /// Total amount of memory currently free + size_t _free_mem; + /// Memory threshold for next garbage collection + size_t _gc_threshold; + /// High water mark of all allocated memory + size_t _max_alloced_mem; + + /// A trail item + struct TItem { + Expression** l; + Expression* v; + bool mark; + TItem(Expression** l0, Expression* v0) : l(l0), v(v0), mark(false) {} + }; + /// Trail + std::vector trail; + + Heap(void) + : _page(NULL), + _rootset(NULL), + _roots(NULL), + _weakRefs(NULL), + _nodeWeakMaps(NULL), + _alloced_mem(0), + _free_mem(0), + _gc_threshold(_min_gc_threshold), + _max_alloced_mem(0) { + for (int i = _max_fl + 1; i--;) _fl[i] = NULL; + } + + /// Default size of pages to allocate + static const size_t pageSize = 1 << 20; + + HeapPage* allocPage(size_t s, bool exact = false) { + if (!exact) s = std::max(s, pageSize); + HeapPage* newPage = static_cast(::malloc(sizeof(HeapPage) + s - 1)); + if (newPage == NULL) { + throw InternalError("out of memory"); + } +#ifndef NDEBUG + memset(newPage, 255, sizeof(HeapPage) + s - 1); +#endif + _alloced_mem += s; + _max_alloced_mem = std::max(_max_alloced_mem, _alloced_mem); + _free_mem += s; + if (exact && _page) { + new (newPage) HeapPage(_page->next, s); + _page->next = newPage; + } else { + if (_page) { + size_t ns = _page->size - _page->used; + assert(ns <= _fl_size[_max_fl]); + if (ns >= _fl_size[0]) { + // Remainder of page can be added to free lists + FreeListNode* fln = reinterpret_cast(_page->data + _page->used); + _page->used += ns; + new (fln) FreeListNode(ns, _fl[_fl_slot(ns)]); + _fl[_fl_slot(ns)] = fln; + } else { + // Waste a little memory (less than smallest free list slot) + _free_mem -= ns; + assert(_alloced_mem >= _free_mem); + } + } + new (newPage) HeapPage(_page, s); + _page = newPage; + } + return newPage; + } + + void* alloc(size_t size, bool exact = false) { + assert(size <= 80 || exact); + /// Align to word boundary + size += ((8 - (size & 7)) & 7); + HeapPage* p = _page; + if (exact || _page == NULL || _page->used + size >= _page->size) p = allocPage(size, exact); + char* ret = p->data + p->used; + p->used += size; + _free_mem -= size; + if (p->size - p->used < _fl_size[0]) { + _free_mem -= (p->size - p->used); + _alloced_mem -= (p->size - p->used); + p->size = p->used; + } + assert(_alloced_mem >= _free_mem); + return ret; + } + + /// Allocate one object of type T (no initialisation) + template + T* alloc(void) { + return static_cast(alloc(sizeof(T))); + } + + /// Allocate \a n objects of type T (no initialisation) + template + T* alloc(int n) { + return static_cast(alloc(n * sizeof(T))); + } + + void* fl(size_t size) { + int slot = _fl_slot(size); + assert(slot <= _max_fl); + if (_fl[slot]) { + FreeListNode* p = _fl[slot]; + _fl[slot] = p->next; + _free_mem -= size; + return p; + } + return alloc(size); + } + + void trigger(void) { + DTRACE0(GC_START); +#ifdef MINIZINC_GC_STATS + std::cerr << "GC\n\talloced " << (_alloced_mem / 1024) << "\n\tfree " << (_free_mem / 1024) + << "\n\tdiff " << ((_alloced_mem - _free_mem) / 1024) << "\n\tthreshold " + << (_gc_threshold / 1024) << "\n"; +#endif + size_t old_free = _free_mem; + mark(); + sweep(); + // GC strategy: + // increase threshold if either + // a) we haven't been able to put much on the free list (comapred to before GC), or + // b) the free list memory (after GC) is less than 50% of the allocated memory + // otherwise (i.e., we have been able to increase the free list, and it is now more + // than 50% of overall allocated memory), keep threshold at allocated memory + if ((old_free != 0 && static_cast(old_free) / static_cast(_free_mem) > 0.9) || + static_cast(_free_mem) / _alloced_mem < 0.5) { + _gc_threshold = std::max(_min_gc_threshold, static_cast(_alloced_mem * 1.5)); + } else { + _gc_threshold = std::max(_min_gc_threshold, _alloced_mem); + } +#ifdef MINIZINC_GC_STATS + std::cerr << "done\n\talloced " << (_alloced_mem / 1024) << "\n\tfree " << (_free_mem / 1024) + << "\n\tdiff " << ((_alloced_mem - _free_mem) / 1024) << "\n\tthreshold " + << (_gc_threshold / 1024) << "\n"; +#endif + DTRACE0(GC_END); + } + void rungc(void) { + if (_alloced_mem > _gc_threshold) { + trigger(); + } + } + void mark(void); + void sweep(void); + + static size_t nodesize(ASTNode* n) { + static const size_t _nodesize[Item::II_END + 1] = { + 0, // NID_FL + 0, // NID_CHUNK + 0, // NID_VEC + sizeof(IntLit), // E_INTLIT + sizeof(FloatLit), // E_FLOATLIT + sizeof(SetLit), // E_SETLIT + sizeof(BoolLit), // E_BOOLLIT + sizeof(StringLit), // E_STRINGLIT + sizeof(Id), // E_ID + sizeof(AnonVar), // E_ANON + sizeof(ArrayLit), // E_ARRAYLIT + sizeof(ArrayAccess), // E_ARRAYACCESS + sizeof(Comprehension), // E_COMP + sizeof(ITE), // E_ITE + sizeof(BinOp), // E_BINOP + sizeof(UnOp), // E_UNOP + sizeof(Call), // E_CALL + sizeof(VarDecl), // E_VARDECL + sizeof(Let), // E_LET + sizeof(TypeInst), // E_TI + sizeof(TIId), // E_TIID + sizeof(IncludeI), // II_INC + sizeof(VarDeclI), // II_VD + sizeof(AssignI), // II_ASN + sizeof(ConstraintI), // II_CON + sizeof(SolveI), // II_SOL + sizeof(OutputI), // II_OUT + sizeof(FunctionI) // II_FUN + }; + size_t ns; + switch (n->_id) { + case ASTNode::NID_FL: + ns = static_cast(n)->size; + break; + case ASTNode::NID_CHUNK: + ns = static_cast(n)->memsize(); + break; + case ASTNode::NID_VEC: + ns = static_cast(n)->memsize(); + break; + default: + assert(n->_id <= Item::II_END); + ns = _nodesize[n->_id]; + break; + } + ns += ((8 - (ns & 7)) & 7); + return ns; + } +}; + +const size_t GC::Heap::_min_gc_threshold = 10ll * 1024ll; + +#ifdef MINIZINC_GC_STATS +const char* GC::Heap::_nodeid[] = { + "FreeList ", // NID_FL + "Chunk ", // NID_CHUNK + "Vec ", // NID_VEC + "IntLit ", // E_INTLIT + "FloatLit ", // E_FLOATLIT + "SetLit ", // E_SETLIT + "BoolLit ", // E_BOOLLIT + "StringLit ", // E_STRINGLIT + "Id ", // E_ID + "AnonVar ", // E_ANON + "ArrayLit ", // E_ARRAYLIT + "ArrayAccess ", // E_ARRAYACCESS + "Comprehension ", // E_COMP + "ITE ", // E_ITE + "BinOp ", // E_BINOP + "UnOp ", // E_UNOP + "Call ", // E_CALL + "VarDecl ", // E_VARDECL + "Let ", // E_LET + "TypeInst ", // E_TI + "TIId ", // E_TIID + "IncludeI ", // II_INC + "VarDeclI ", // II_VD + "AssignI ", // II_ASN + "ConstraintI ", // II_CON + "SolveI ", // II_SOL + "OutputI ", // II_OUT + "FunctionI " // II_FUN +}; +#endif + +void GC::setTimeout(unsigned long long int t) { + if (gc() == NULL) { + gc() = new GC(); + } + gc()->_timeout = t; + gc()->_timeout_timer.reset(); +} + +void GC::lock(void) { + if (gc() == NULL) { + gc() = new GC(); + } + // If a timeout has been specified, first check counter + // before checking timer (counter is much cheaper, introduces + // less overhead) + if (gc()->_timeout > 0 && gc()->_timeout_counter++ > 500) { + gc()->_timeout_counter = 0; + if (gc()->_timeout_timer.ms() > gc()->_timeout) { + gc()->_timeout = 0; + gc()->_timeout_counter = 0; + throw Timeout(); + } + } + if (gc()->_lock_count == 0) { + gc()->_heap->rungc(); + } + gc()->_lock_count++; +} +void GC::unlock(void) { + assert(locked()); + gc()->_lock_count--; +} + +void GC::trigger(void) { + if (!locked()) gc()->_heap->trigger(); +} + +const size_t GC::Heap::pageSize; + +const size_t GC::Heap::_fl_size[GC::Heap::_max_fl + 1] = { + sizeof(Item) + 1 * sizeof(void*), sizeof(Item) + 2 * sizeof(void*), + sizeof(Item) + 3 * sizeof(void*), sizeof(Item) + 4 * sizeof(void*), + sizeof(Item) + 5 * sizeof(void*), sizeof(Item) + 6 * sizeof(void*), +}; + +GC::GC(void) : _heap(new Heap()), _lock_count(0), _timeout(0), _timeout_counter(0) {} + +void GC::add(Model* m) { + GC* gc = GC::gc(); + if (gc->_heap->_rootset) { + m->_roots_next = gc->_heap->_rootset; + m->_roots_prev = m->_roots_next->_roots_prev; + m->_roots_prev->_roots_next = m; + m->_roots_next->_roots_prev = m; + } else { + gc->_heap->_rootset = m->_roots_next = m->_roots_prev = m; + } +} + +void GC::remove(Model* m) { + GC* gc = GC::gc(); + if (m->_roots_next == m) { + gc->_heap->_rootset = NULL; + } else { + m->_roots_next->_roots_prev = m->_roots_prev; + m->_roots_prev->_roots_next = m->_roots_next; + if (m == gc->_heap->_rootset) gc->_heap->_rootset = m->_roots_prev; + } +} + +void* GC::alloc(size_t size) { + assert(locked()); + void* ret; + if (size < _heap->_fl_size[0] || size > _heap->_fl_size[_heap->_max_fl]) { + ret = _heap->alloc(size, true); + } else { + ret = _heap->fl(size); + } + new (ret) FreeListNode(size); + return ret; +} + +void GC::Heap::mark(void) { +#if defined(MINIZINC_GC_STATS) + std::cerr << "================= mark =================: "; + gc_stats.clear(); +#endif + + for (KeepAlive* e = _roots; e != NULL; e = e->next()) { + if ((*e)() && (*e)()->_gc_mark == 0) { + Expression::mark((*e)()); +#if defined(MINIZINC_GC_STATS) + gc_stats[(*e)()->_id].keepalive++; +#endif + } + } +#if defined(MINIZINC_GC_STATS) + std::cerr << "+"; +#endif + + Model* m = _rootset; + if (m == NULL) return; + do { + m->_filepath.mark(); + m->_filename.mark(); + for (unsigned int j = 0; j < m->_items.size(); j++) { + Item* i = m->_items[j]; + if (i->_gc_mark == 0) { + i->_gc_mark = 1; + i->loc().mark(); + switch (i->iid()) { + case Item::II_INC: + i->cast()->f().mark(); + break; + case Item::II_VD: + Expression::mark(i->cast()->e()); +#if defined(MINIZINC_GC_STATS) + gc_stats[i->cast()->e()->Expression::eid()].inmodel++; +#endif + break; + case Item::II_ASN: + i->cast()->id().mark(); + Expression::mark(i->cast()->e()); + Expression::mark(i->cast()->decl()); + break; + case Item::II_CON: + Expression::mark(i->cast()->e()); +#if defined(MINIZINC_GC_STATS) + gc_stats[i->cast()->e()->Expression::eid()].inmodel++; +#endif + break; + case Item::II_SOL: { + SolveI* si = i->cast(); + for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) { + Expression::mark(*it); + } + } + Expression::mark(i->cast()->e()); + break; + case Item::II_OUT: + Expression::mark(i->cast()->e()); + break; + case Item::II_FUN: { + FunctionI* fi = i->cast(); + fi->id().mark(); + Expression::mark(fi->ti()); + for (ExpressionSetIter it = fi->ann().begin(); it != fi->ann().end(); ++it) { + Expression::mark(*it); + } + Expression::mark(fi->e()); + fi->params().mark(); + for (unsigned int k = 0; k < fi->params().size(); k++) { + Expression::mark(fi->params()[k]); + } + } break; + } + } + } + m = m->_roots_next; + } while (m != _rootset); + + for (unsigned int i = static_cast(trail.size()); i--;) { + Expression::mark(trail[i].v); + } + + bool fixPrev = false; + WeakRef* prevWr = NULL; + for (WeakRef* wr = _weakRefs; wr != NULL; wr = wr->next()) { + if (fixPrev) { + fixPrev = false; + removeWeakRef(prevWr); + prevWr->_n = NULL; + prevWr->_p = NULL; + } + if ((*wr)() && (*wr)()->_gc_mark == 0) { + wr->_e = NULL; + wr->_valid = false; + fixPrev = true; + prevWr = wr; + } + } + if (fixPrev) { + removeWeakRef(prevWr); + prevWr->_n = NULL; + prevWr->_p = NULL; + } + + for (ASTNodeWeakMap* wr = _nodeWeakMaps; wr != NULL; wr = wr->next()) { + std::vector toRemove; + for (auto n : wr->_m) { + if (n.first->_gc_mark == 0 || n.second->_gc_mark == 0) toRemove.push_back(n.first); + } + for (auto n : toRemove) { + wr->_m.erase(n); + } + } + +#if defined(MINIZINC_GC_STATS) + std::cerr << "+"; + std::cerr << "\n"; +#endif +} + +void GC::Heap::sweep(void) { +#if defined(MINIZINC_GC_STATS) + std::cerr << "=============== GC sweep =============\n"; +#endif + HeapPage* p = _page; + HeapPage* prev = NULL; + while (p) { + size_t off = 0; + bool wholepage = true; + struct NodeInfo { + ASTNode* n; + size_t ns; + NodeInfo(ASTNode* n0, size_t ns0) : n(n0), ns(ns0) {} + }; + std::vector freeNodes; + freeNodes.reserve(100); + while (off < p->used) { + ASTNode* n = reinterpret_cast(p->data + off); + size_t ns = nodesize(n); + assert(ns != 0); +#if defined(MINIZINC_GC_STATS) + GCStat& stats = gc_stats[n->_id]; + stats.first++; + stats.total += ns; +#endif + if (n->_gc_mark == 0) { + switch (n->_id) { + case Item::II_FUN: + static_cast(n)->ann().~Annotation(); + break; + case Item::II_SOL: + static_cast(n)->ann().~Annotation(); + break; + case Expression::E_VARDECL: + // Reset WeakRef inside VarDecl + static_cast(n)->flat(NULL); + // fall through + default: + if (n->_id >= ASTNode::NID_END + 1 && n->_id <= Expression::EID_END) { + static_cast(n)->ann().~Annotation(); + } + } + if (ns >= _fl_size[0] && ns <= _fl_size[_max_fl]) { + freeNodes.push_back(NodeInfo(n, ns)); + } else { + assert(off == 0); + assert(p->used == p->size); + } + } else { +#if defined(MINIZINC_GC_STATS) + stats.second++; +#endif + wholepage = false; + if (n->_id != ASTNode::NID_FL) n->_gc_mark = 0; + } + off += ns; + } + if (wholepage) { +#ifndef NDEBUG + memset(p->data, 42, p->size); +#endif + if (prev) { + prev->next = p->next; + } else { + assert(p == _page); + _page = p->next; + } + HeapPage* pf = p; + p = p->next; + _alloced_mem -= pf->size; + if (pf->size - pf->used >= _fl_size[0]) _free_mem -= (pf->size - pf->used); + assert(_alloced_mem >= _free_mem); + ::free(pf); + } else { + for (auto ni : freeNodes) { + FreeListNode* fln = static_cast(ni.n); + new (fln) FreeListNode(ni.ns, _fl[_fl_slot(ni.ns)]); + _fl[_fl_slot(ni.ns)] = fln; + _free_mem += ni.ns; +#if defined(MINIZINC_GC_STATS) + gc_stats[fln->_id].second++; +#endif + } + assert(_alloced_mem >= _free_mem); + prev = p; + p = p->next; + } + } +#if defined(MINIZINC_GC_STATS) + for (auto stat : gc_stats) { + std::cerr << _nodeid[stat.first] << ":\t" << stat.second.first << " / " << stat.second.second + << " / " << stat.second.keepalive << " / " << stat.second.inmodel << " / "; + if (stat.second.total > 1024) { + if (stat.second.total > 1024 * 1024) { + std::cerr << (stat.second.total / 1024 / 1024) << "M"; + } else { + std::cerr << (stat.second.total / 1024) << "K"; + } + } else { + std::cerr << (stat.second.total); + } + std::cerr << std::endl; + } +#endif +} + +ASTVec::ASTVec(size_t size) : ASTNode(NID_VEC), _size(size) {} +void* ASTVec::alloc(size_t size) { + size_t s = sizeof(ASTVec) + (size <= 2 ? 0 : size - 2) * sizeof(void*); + s += ((8 - (s & 7)) & 7); + return GC::gc()->alloc(s); +} + +ASTChunk::ASTChunk(size_t size) : ASTNode(NID_CHUNK), _size(size) {} +void* ASTChunk::alloc(size_t size) { + size_t s = sizeof(ASTChunk) + (size <= 4 ? 0 : size - 4) * sizeof(char); + s += ((8 - (s & 7)) & 7); + return GC::gc()->alloc(s); +} + +void GC::mark(void) { + GC* gc = GC::gc(); + if (!gc->_heap->trail.empty()) gc->_heap->trail.back().mark = true; +} +void GC::trail(Expression** l, Expression* v) { + GC* gc = GC::gc(); + gc->_heap->trail.push_back(GC::Heap::TItem(l, v)); +} +void GC::untrail(void) { + GC* gc = GC::gc(); + while (!gc->_heap->trail.empty() && !gc->_heap->trail.back().mark) { + *gc->_heap->trail.back().l = gc->_heap->trail.back().v; + gc->_heap->trail.pop_back(); + } + if (!gc->_heap->trail.empty()) gc->_heap->trail.back().mark = false; +} +size_t GC::maxMem(void) { + GC* gc = GC::gc(); + return gc->_heap->_max_alloced_mem; +} + +void* ASTNode::operator new(size_t size) { return GC::gc()->alloc(size); } + +void GC::addKeepAlive(KeepAlive* e) { + assert(e->_p == NULL); + assert(e->_n == NULL); + e->_n = GC::gc()->_heap->_roots; + if (GC::gc()->_heap->_roots) GC::gc()->_heap->_roots->_p = e; + GC::gc()->_heap->_roots = e; +} +void GC::removeKeepAlive(KeepAlive* e) { + if (e->_p) { + e->_p->_n = e->_n; + } else { + assert(GC::gc()->_heap->_roots == e); + GC::gc()->_heap->_roots = e->_n; + } + if (e->_n) { + e->_n->_p = e->_p; + } +} + +KeepAlive::KeepAlive(Expression* e) : _e(e), _p(NULL), _n(NULL) { + if (_e && !_e->isUnboxedVal()) GC::gc()->addKeepAlive(this); +} +KeepAlive::~KeepAlive(void) { + if (_e && !_e->isUnboxedVal()) GC::gc()->removeKeepAlive(this); +} +KeepAlive::KeepAlive(const KeepAlive& e) : _e(e._e), _p(NULL), _n(NULL) { + if (_e && !_e->isUnboxedVal()) GC::gc()->addKeepAlive(this); +} +KeepAlive& KeepAlive::operator=(const KeepAlive& e) { + if (_e && !_e->isUnboxedVal()) { + if (e._e == NULL || e._e->isUnboxedVal()) { + GC::gc()->removeKeepAlive(this); + _p = _n = NULL; + } + } else { + if (e._e != NULL && !e._e->isUnboxedVal()) GC::gc()->addKeepAlive(this); + } + _e = e._e; + return *this; +} + +void GC::addWeakRef(WeakRef* e) { + assert(e->_p == NULL); + assert(e->_n == NULL); + e->_n = GC::gc()->_heap->_weakRefs; + if (GC::gc()->_heap->_weakRefs) GC::gc()->_heap->_weakRefs->_p = e; + GC::gc()->_heap->_weakRefs = e; +} +void GC::removeWeakRef(MiniZinc::WeakRef* e) { + if (e->_p) { + e->_p->_n = e->_n; + } else { + assert(GC::gc()->_heap->_weakRefs == e); + GC::gc()->_heap->_weakRefs = e->_n; + } + if (e->_n) { + e->_n->_p = e->_p; + } +} +void GC::addNodeWeakMap(ASTNodeWeakMap* m) { + assert(m->_p == NULL); + assert(m->_n == NULL); + m->_n = GC::gc()->_heap->_nodeWeakMaps; + if (GC::gc()->_heap->_nodeWeakMaps) GC::gc()->_heap->_nodeWeakMaps->_p = m; + GC::gc()->_heap->_nodeWeakMaps = m; +} +void GC::removeNodeWeakMap(ASTNodeWeakMap* m) { + if (m->_p) { + m->_p->_n = m->_n; + } else { + assert(GC::gc()->_heap->_nodeWeakMaps == m); + GC::gc()->_heap->_nodeWeakMaps = m->_n; + } + if (m->_n) { + m->_n->_p = m->_p; + } +} + +WeakRef::WeakRef(Expression* e) : _e(e), _p(NULL), _n(NULL), _valid(true) { + if (_e && !_e->isUnboxedVal()) GC::gc()->addWeakRef(this); +} +WeakRef::~WeakRef(void) { + if (_e && !_e->isUnboxedVal()) GC::gc()->removeWeakRef(this); +} +WeakRef::WeakRef(const WeakRef& e) : _e(e()), _p(NULL), _n(NULL), _valid(true) { + if (_e && !_e->isUnboxedVal()) GC::gc()->addWeakRef(this); +} +WeakRef& WeakRef::operator=(const WeakRef& e) { + // Test if this WeakRef is currently active in the GC + bool isActive = (_e && !_e->isUnboxedVal()); + if (isActive) { + // Yes, active WeakRef. + // If after assigning WeakRef should be inactive, remove it. + if (e() == NULL || e()->isUnboxedVal()) { + GC::gc()->removeWeakRef(this); + _n = _p = NULL; + } + } + _e = e(); + _valid = true; + + // If this WeakRef was not active but now should be, add it + if (!isActive && _e != NULL && !_e->isUnboxedVal()) GC::gc()->addWeakRef(this); + return *this; +} + +ASTNodeWeakMap::ASTNodeWeakMap(void) : _p(NULL), _n(NULL) { GC::gc()->addNodeWeakMap(this); } + +ASTNodeWeakMap::~ASTNodeWeakMap(void) { GC::gc()->removeNodeWeakMap(this); } + +void ASTNodeWeakMap::insert(ASTNode* n0, ASTNode* n1) { _m.insert(std::make_pair(n0, n1)); } + +ASTNode* ASTNodeWeakMap::find(ASTNode* n) { + NodeMap::iterator it = _m.find(n); + if (it == _m.end()) return NULL; + return it->second; +} + +} // namespace MiniZinc diff --git a/lib/htmlprinter.cpp b/lib/htmlprinter.cpp new file mode 100644 index 0000000..44b94ef --- /dev/null +++ b/lib/htmlprinter.cpp @@ -0,0 +1,1284 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace MiniZinc { + +namespace HtmlDocOutput { + +// Trim leading space: +// - always trim first line completely +// - second line defines the base indentation +std::string trim(const std::string& s0) { + std::string s = s0; + // remove carriage returns + size_t j = 0; + for (size_t i = 0; i < s.size(); i++) { + if (s[i] != '\r') s[j++] = s[i]; + } + s.resize(j); + size_t first_line_indent = s.find_first_not_of(" \t"); + if (first_line_indent == std::string::npos) return ""; + size_t first_nl = s.find("\n"); + std::ostringstream oss; + if (first_line_indent == first_nl) { + // first line is empty + oss << "\n"; + std::cerr << "--empty first line\n"; + } else { + // strip first line + size_t end_of_first_line = + first_nl == std::string::npos ? std::string::npos : first_nl - first_line_indent + 1; + oss << s.substr(first_line_indent, end_of_first_line); + std::cerr << "--" << first_line_indent << ", " << first_nl << "\n"; + std::cerr << "'" << s.substr(first_line_indent, end_of_first_line) << "'\n"; + } + if (first_nl == std::string::npos) return oss.str(); + size_t unindent = s.find_first_not_of(" \t", first_nl + 1); + if (unindent == std::string::npos) return oss.str(); + size_t pos = s.find("\n", first_nl + 1); + if (unindent == 0 || unindent > pos) { + oss << s.substr(first_nl + 1, std::string::npos); + return oss.str(); + } + size_t lastpos = unindent; + while (pos != std::string::npos) { + oss << s.substr(lastpos, pos - lastpos) << "\n"; + size_t next_indent = s.find_first_not_of(" \t", pos + 1); + if (next_indent == std::string::npos) { + lastpos = next_indent; + } else if (next_indent - (pos + 1) < unindent) { + lastpos = next_indent; + } else { + lastpos = pos + 1 + unindent; + } + pos = (lastpos == std::string::npos ? lastpos : s.find("\n", lastpos)); + } + if (lastpos != std::string::npos) oss << s.substr(lastpos, std::string::npos); + return oss.str(); +} + +class DocItem { +public: + enum DocType { T_PAR = 0, T_VAR = 1, T_FUN = 2 }; + DocItem(const DocType& t0, std::string id0, std::string sig0, std::string doc0) + : t(t0), id(id0), sig(sig0), doc(doc0) {} + DocType t; + std::string id; + std::string sig; + std::string doc; +}; + +typedef std::unordered_map FunMap; + +class Group; + +class GroupMap { +public: + typedef std::vector Map; + Map m; + ~GroupMap(); + Map::iterator find(const std::string& n); +}; + +class Group { +public: + Group(const std::string& name0, const std::string& fullPath0) + : name(name0), fullPath(fullPath0) {} + std::string name; + std::string fullPath; + std::string desc; + std::string htmlName; + GroupMap subgroups; + std::vector items; + + std::string getAnchor(int level, int indivFileLevel) { + if (level < indivFileLevel) { + return fullPath + ".html"; + } else { + return "#" + fullPath; + } + } + + std::string toHTML(int level, int indivFileLevel, Group* parent, int idx, + const std::string& basename, bool generateIndex) { + std::ostringstream oss; + + int realLevel = (level < indivFileLevel) ? 0 : level - indivFileLevel; + oss << "
\n"; + if (parent) { + oss << "
"; + if (idx > 0) { + oss << "⇐ "; + } + oss << "⇧ "; + if (idx < parent->subgroups.m.size() - 1) { + oss << "⇒ "; + } + if (generateIndex) oss << "Index\n"; + if (items.size() > 0) { + oss << "reveal " + "all\n"; + oss << "hide " + "all\n"; + } + oss << "
"; + } + if (!htmlName.empty()) { + oss << "\n"; + oss << "
\n" << desc << "
\n"; + } + + if (subgroups.m.size() != 0) { + oss << "

Sections:

\n"; + oss << "
    \n"; + for (GroupMap::Map::iterator it = subgroups.m.begin(); it != subgroups.m.end(); ++it) { + oss << "
  • " << (*it)->htmlName + << "\n"; + + if ((*it)->htmlName.empty()) { + std::cerr << "Warning: undocumented group " << (*it)->fullPath << "\n"; + } + } + oss << "
\n"; + if (parent == NULL && generateIndex) { + oss << "

Index

\n"; + } + if (items.size() > 0) oss << "

Declarations in this section:

\n"; + } + + struct SortById { + bool operator()(const DocItem& i0, const DocItem& i1) { + return i0.t < i1.t || (i0.t == i1.t && i0.id < i1.id); + } + } _cmp; + std::stable_sort(items.begin(), items.end(), _cmp); + + int cur_t = -1; + const char* dt[] = {"par", "var", "fun"}; + const char* dt_desc[] = {"Parameters", "Variables", "Functions and Predicates"}; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { + if (it->t != cur_t) { + if (cur_t != -1) oss << "
\n"; + cur_t = it->t; + oss << "
\n"; + oss << "
" << dt_desc[cur_t] << "
\n"; + } + oss << it->doc; + } + if (cur_t != -1) oss << "
\n"; + + if (level >= indivFileLevel) { + for (unsigned int i = 0; i < subgroups.m.size(); i++) { + oss << subgroups.m[i]->toHTML(level + 1, indivFileLevel, this, i, basename, generateIndex); + } + } + + oss << ""; + return oss.str(); + } + + static std::string rstHeading(std::string s, int level) { + std::vector levelChar({'#', '=', '-', '^', '+', '"'}); + std::ostringstream oss; + oss << s << "\n"; + for (int i = 0; i < s.size(); i++) oss << levelChar[level]; + oss << "\n\n"; + return oss.str(); + } + + std::string toRST(int level) { + std::ostringstream oss; + if (!htmlName.empty()) { + if (level == 0) { + oss << ".. _ch-lib-" << name << ":\n\n"; + } + oss << rstHeading(htmlName, level); + oss << HtmlDocOutput::trim(desc) << "\n\n"; + } + for (unsigned int i = 0; i < subgroups.m.size(); i++) { + oss << subgroups.m[i]->toRST(level + 1); + } + if (items.size() > 0) { + if (subgroups.m.size() != 0) { + oss << rstHeading("Other declarations", level + 1); + } + struct SortById { + bool operator()(const DocItem& i0, const DocItem& i1) { + return i0.t < i1.t || (i0.t == i1.t && i0.id < i1.id); + } + } _cmp; + std::stable_sort(items.begin(), items.end(), _cmp); + + int cur_t = -1; + int nHeadings = 0; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { + if (it->t != cur_t) { + cur_t = it->t; + nHeadings++; + } + } + cur_t = -1; + const char* dt_desc[] = {"Parameters", "Variables", "Functions and Predicates"}; + for (std::vector::const_iterator it = items.begin(); it != items.end(); ++it) { + if (it->t != cur_t) { + cur_t = it->t; + if (nHeadings > 1) + oss << rstHeading(dt_desc[cur_t], subgroups.m.size() == 0 ? level + 1 : level + 2); + } + oss << it->doc; + } + } + return oss.str(); + } +}; + +GroupMap::~GroupMap() { + for (Map::iterator it = m.begin(); it != m.end(); ++it) { + delete *it; + } +} +GroupMap::Map::iterator GroupMap::find(const std::string& n) { + for (Map::iterator it = m.begin(); it != m.end(); ++it) + if ((*it)->name == n) return it; + return m.end(); +} + +void addToGroup(Group& gm, const std::string& group, DocItem& di) { + std::vector subgroups; + size_t lastpos = 0; + size_t pos = group.find("."); + while (pos != std::string::npos) { + subgroups.push_back(group.substr(lastpos, pos - lastpos)); + lastpos = pos + 1; + pos = group.find(".", lastpos); + } + subgroups.push_back(group.substr(lastpos, std::string::npos)); + + GroupMap* cgm = &gm.subgroups; + std::string gpath(gm.fullPath); + for (unsigned int i = 0; i < subgroups.size(); i++) { + gpath += "-"; + gpath += subgroups[i]; + if (cgm->find(subgroups[i]) == cgm->m.end()) { + cgm->m.push_back(new Group(subgroups[i], gpath)); + } + Group& g = **cgm->find(subgroups[i]); + if (i == subgroups.size() - 1) { + g.items.push_back(di); + } else { + cgm = &g.subgroups; + } + } +} + +void setGroupDesc(Group& maingroup, const std::string& group, std::string htmlName, std::string s) { + if (group == "MAIN") { + if (!maingroup.htmlName.empty()) { + std::cerr << "Warning: two descriptions for group `" << group << "'\n"; + } + maingroup.htmlName = htmlName; + maingroup.desc = s; + return; + } + + std::vector subgroups; + size_t lastpos = 0; + size_t pos = group.find("."); + while (pos != std::string::npos) { + subgroups.push_back(group.substr(lastpos, pos - lastpos)); + lastpos = pos + 1; + pos = group.find(".", lastpos); + } + subgroups.push_back(group.substr(lastpos, std::string::npos)); + + GroupMap* cgm = &maingroup.subgroups; + std::string gpath(maingroup.fullPath); + for (unsigned int i = 0; i < subgroups.size(); i++) { + gpath += "-"; + gpath += subgroups[i]; + if (cgm->find(subgroups[i]) == cgm->m.end()) { + cgm->m.push_back(new Group(subgroups[i], gpath)); + } + Group& g = **cgm->find(subgroups[i]); + if (i == subgroups.size() - 1) { + if (!g.htmlName.empty()) { + std::cerr << "Warning: two descriptions for group `" << group << "'\n"; + } + g.htmlName = htmlName; + g.desc = s; + } else { + cgm = &g.subgroups; + } + } +} + +std::string extractArgWord(std::string& s, size_t n) { + size_t start = n; + while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; + while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; + size_t end = start + 1; + while (end < s.size() && (isalnum(s[end]) || s[end] == '_' || s[end] == '.')) end++; + std::string ret = s.substr(start, end - start); + s = s.substr(end, std::string::npos); + return ret; +} + +std::string makeHTMLId(const std::string& ident) { + std::ostringstream oss; + oss << "I"; + bool prevWasSym = false; + for (size_t i = 0; i < ident.size(); i++) { + bool isSym = true; + switch (ident[i]) { + case '!': + oss << "-ex"; + break; + case '=': + oss << "-eq"; + break; + case '*': + oss << "-as"; + break; + case '+': + oss << "-pl"; + break; + case '-': + oss << "-mi"; + break; + case '>': + oss << "-gr"; + break; + case '<': + oss << "-lt"; + break; + case '/': + oss << "-dv"; + break; + case '\\': + oss << "-bs"; + break; + case '~': + oss << "-tl"; + break; + case '\'': + oss << "-tk"; + break; + case ' ': + break; + case '\t': + break; + case '\n': + break; + case ':': + oss << "-cl"; + break; + case '[': + oss << "-bo"; + break; + case ']': + oss << "-bc"; + break; + case '$': + oss << "-dd"; + break; + case '(': + oss << "-po"; + break; + case ')': + oss << "-pc"; + break; + case ',': + oss << "-cm"; + break; + default: + oss << (prevWasSym ? "-" : "") << ident[i]; + isSym = false; + break; + } + prevWasSym = isSym; + } + return oss.str(); +} + +} // namespace HtmlDocOutput + +class CollectFunctionsVisitor : public ItemVisitor { +protected: + EnvI& env; + HtmlDocOutput::FunMap& _funmap; + bool _includeStdLib; + +public: + CollectFunctionsVisitor(EnvI& env0, HtmlDocOutput::FunMap& funmap, bool includeStdLib) + : env(env0), _funmap(funmap), _includeStdLib(includeStdLib) {} + bool enterModel(Model* m) { return _includeStdLib || m->filename() != "stdlib.mzn"; } + void vFunctionI(FunctionI* fi) { + if (Call* docstring = + Expression::dyn_cast(getAnnotation(fi->ann(), constants().ann.doc_comment))) { + std::string ds = eval_string(env, docstring->arg(0)); + std::string group("main"); + size_t group_idx = ds.find("@group"); + if (group_idx != std::string::npos) { + group = HtmlDocOutput::extractArgWord(ds, group_idx); + } + _funmap.insert(std::make_pair(fi, group)); + } + } +}; + +class PrintHtmlVisitor : public ItemVisitor { +protected: + EnvI& env; + HtmlDocOutput::Group& _maingroup; + HtmlDocOutput::FunMap& _funmap; + bool _includeStdLib; + + std::vector replaceArgs(std::string& s) { + std::vector replacements; + std::ostringstream oss; + size_t lastpos = 0; + size_t pos = std::min(s.find("\\a"), s.find("\\p")); + size_t mathjax_open = s.find("\\("); + size_t mathjax_close = s.rfind("\\)"); + if (pos == std::string::npos) return replacements; + while (pos != std::string::npos) { + oss << s.substr(lastpos, pos - lastpos); + size_t start = pos; + while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; + while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; + size_t end = start + 1; + while (end < s.size() && (isalnum(s[end]) || s[end] == '_')) end++; + if (s[pos + 1] == 'a') { + replacements.push_back(s.substr(start, end - start)); + if (pos >= mathjax_open && pos <= mathjax_close) { + oss << "{\\bf " << replacements.back() << "}"; + } else { + oss << "" << replacements.back() << ""; + } + } else { + if (pos >= mathjax_open && pos <= mathjax_close) { + oss << "{\\bf " << s.substr(start, end - start) << "}"; + } else { + oss << "" << s.substr(start, end - start) << ""; + } + } + lastpos = end; + pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); + } + oss << s.substr(lastpos, std::string::npos); + s = oss.str(); + return replacements; + } + + std::pair extractArgLine(std::string& s, size_t n) { + size_t start = n; + while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; + while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; + size_t end = start + 1; + while (end < s.size() && s[end] != ':') end++; + std::string arg = s.substr(start, end - start); + size_t doc_start = end + 1; + while (end < s.size() && s[end] != '\n') end++; + std::string ret = s.substr(doc_start, end - doc_start); + replaceArgs(ret); + s = s.substr(0, n) + s.substr(end, std::string::npos); + return make_pair(arg, ret); + } + + std::string addHTML(const std::string& s) { + std::ostringstream oss; + size_t lastpos = 0; + size_t pos = s.find('\n'); + bool inUl = false; + oss << "

\n"; + while (pos != std::string::npos) { + oss << s.substr(lastpos, pos - lastpos); + size_t next = std::min(s.find('\n', pos + 1), s.find('-', pos + 1)); + if (next == std::string::npos) { + lastpos = pos + 1; + break; + } + bool allwhite = true; + for (size_t cur = pos + 1; cur < next; cur++) { + if (s[cur] != ' ' && s[cur] != '\t') { + allwhite = false; + break; + } + } + if (allwhite) { + if (s[next] == '-') { + if (!inUl) { + oss << "

    \n"; + inUl = true; + } + oss << "
  • "; + } else { + if (inUl) { + oss << "
\n"; + inUl = false; + } else { + oss << "

\n"; + } + } + lastpos = next + 1; + pos = s.find('\n', lastpos); + } else { + lastpos = pos + 1; + if (s[pos] == '\n') { + oss << " "; + } + if (s[next] == '-') { + pos = s.find('\n', next + 1); + } else { + pos = next; + } + } + } + oss << s.substr(lastpos, std::string::npos); + if (inUl) oss << "\n"; + oss << "

\n"; + return oss.str(); + } + +public: + PrintHtmlVisitor(EnvI& env0, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, + bool includeStdLib) + : env(env0), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} + bool enterModel(Model* m) { + if (!_includeStdLib && m->filename() == "stdlib.mzn") return false; + const std::string& dc = m->docComment(); + if (!dc.empty()) { + size_t gpos = dc.find("@groupdef"); + while (gpos != std::string::npos) { + size_t start = gpos; + while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') start++; + while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) start++; + size_t end = start + 1; + while (end < dc.size() && (isalnum(dc[end]) || dc[end] == '_' || dc[end] == '.')) end++; + std::string groupName = dc.substr(start, end - start); + size_t doc_start = end + 1; + while (end < dc.size() && dc[end] != '\n') end++; + std::string groupHTMLName = dc.substr(doc_start, end - doc_start); + + size_t next = dc.find("@groupdef", gpos + 1); + HtmlDocOutput::setGroupDesc( + _maingroup, groupName, groupHTMLName, + addHTML(dc.substr(end, next == std::string::npos ? next : next - end))); + gpos = next; + } + } + return true; + } + /// Visit variable declaration + void vVarDeclI(VarDeclI* vdi) { + if (Call* docstring = Expression::dyn_cast( + getAnnotation(vdi->e()->ann(), constants().ann.doc_comment))) { + std::string ds = eval_string(env, docstring->arg(0)); + std::string group("main"); + size_t group_idx = ds.find("@group"); + if (group_idx != std::string::npos) { + group = HtmlDocOutput::extractArgWord(ds, group_idx); + } + + std::ostringstream os; + std::string sig = vdi->e()->type().toString(env) + " " + vdi->e()->id()->str().str(); + os << "
\n"; + os << "
\n"; + if (vdi->e()->ti()->type() == Type::ann()) { + os << "annotation "; + os << "" << *vdi->e()->id() << ""; + } else { + os << *vdi->e()->ti() << ": " << *vdi->e()->id(); + } + os << "
\n"; + os << addHTML(ds); + os << "
"; + GCLock lock; + HtmlDocOutput::DocItem di( + vdi->e()->type().ispar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, + sig, sig, os.str()); + HtmlDocOutput::addToGroup(_maingroup, group, di); + } + } + /// Visit function item + void vFunctionI(FunctionI* fi) { + if (Call* docstring = + Expression::dyn_cast(getAnnotation(fi->ann(), constants().ann.doc_comment))) { + std::string ds = eval_string(env, docstring->arg(0)); + std::string group("main"); + size_t group_idx = ds.find("@group"); + if (group_idx != std::string::npos) { + group = HtmlDocOutput::extractArgWord(ds, group_idx); + } + + size_t param_idx = ds.find("@param"); + std::vector > params; + while (param_idx != std::string::npos) { + params.push_back(extractArgLine(ds, param_idx)); + param_idx = ds.find("@param"); + } + + std::vector args = replaceArgs(ds); + + std::unordered_set allArgs; + for (unsigned int i = 0; i < args.size(); i++) allArgs.insert(args[i]); + for (unsigned int i = 0; i < params.size(); i++) allArgs.insert(params[i].first); + + GCLock lock; + for (unsigned int i = 0; i < fi->params().size(); i++) { + if (allArgs.find(fi->params()[i]->id()->str().str()) == allArgs.end()) { + std::cerr << "Warning: parameter " << *fi->params()[i]->id() + << " not documented for function " << fi->id() << " at location " << fi->loc() + << "\n"; + } + } + + std::string sig; + { + GCLock lock; + FunctionI* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), fi->params()); + std::ostringstream oss_sig; + oss_sig << *fi_c; + sig = oss_sig.str(); + sig.resize(sig.size() - 2); + } + + std::ostringstream os; + os << "
\n"; + os << "
"; + os << "◀"; + + std::ostringstream fs; + if (fi->ti()->type() == Type::ann()) { + fs << "annotation "; + os << "annotation "; + } else if (fi->ti()->type() == Type::parbool()) { + fs << "test "; + os << "test "; + } else if (fi->ti()->type() == Type::varbool()) { + fs << "predicate "; + os << "predicate "; + } else { + fs << "function " << *fi->ti() << ": "; + os << "function " << *fi->ti() + << ": "; + } + fs << fi->id() << "("; + os << "" << fi->id() << "("; + size_t align = fs.str().size(); + for (unsigned int i = 0; i < fi->params().size(); i++) { + fs << *fi->params()[i]->ti() << ": " << *fi->params()[i]->id(); + if (i < fi->params().size() - 1) { + fs << ", "; + } + } + bool splitArgs = (fs.str().size() > 70); + for (unsigned int i = 0; i < fi->params().size(); i++) { + os << "" << *fi->params()[i]->ti() << ": " + << "" << *fi->params()[i]->id() << ""; + if (i < fi->params().size() - 1) { + os << ","; + if (splitArgs) { + os << "\n"; + for (unsigned int j = static_cast(align); j--;) os << " "; + } else { + os << " "; + } + } + } + os << ")"; + + if (fi->e()) { + FunctionI* f_body = fi; + bool alias; + do { + alias = false; + Call* c = Expression::dyn_cast(f_body->e()); + if (c && c->n_args() == f_body->params().size()) { + bool sameParams = true; + for (unsigned int i = 0; i < f_body->params().size(); i++) { + Id* ident = c->arg(i)->dyn_cast(); + if (ident == NULL || ident->decl() != f_body->params()[i] || + ident->str() != c->decl()->params()[i]->id()->str()) { + sameParams = false; + break; + } + } + if (sameParams) { + alias = true; + f_body = c->decl(); + } + } + } while (alias); + if (f_body->e()) { + std::ostringstream body_os; + Printer p(body_os, 70); + p.print(f_body->e()); + + std::string filename = f_body->loc().filename().str(); + size_t lastSlash = filename.find_last_of("/"); + if (lastSlash != std::string::npos) { + filename = filename.substr(lastSlash + 1, std::string::npos); + } + os << " ="; + os << "\n
"; + os << "
"; + os << body_os.str(); + os << "
\n"; + os << "(standard decomposition from " << filename << ":" << f_body->loc().first_line() + << ")"; + os << "
"; + } + } + + os << "
\n
\n"; + + if (fi->id().c_str()[0] == '\'') { + std::string op = fi->id().str(); + op = op.substr(1, op.length() - 2); + const char* space = (op[0] >= 'a' ? " " : ""); + if (fi->params().size() == 2) { + os << "

Usage: " << *fi->params()[0]->id() << space << op + << space << *fi->params()[1]->id() << "

"; + } else if (fi->params().size() == 1) { + os << "

Usage: " << op << space << *fi->params()[0]->id() + << "

"; + } + } + + std::string dshtml = addHTML(ds); + + os << dshtml; + if (params.size() > 0) { + os << "
Parameters
\n"; + os << "
    \n"; + for (unsigned int i = 0; i < params.size(); i++) { + os << "
  • " << params[i].first << ": " << params[i].second + << "
  • \n"; + } + os << "
\n"; + } + os << "
"; + os << "
"; + + HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, fi->id().str(), sig, os.str()); + HtmlDocOutput::addToGroup(_maingroup, group, di); + } + } +}; + +std::vector HtmlPrinter::printHtml(EnvI& env, MiniZinc::Model* m, + const std::string& basename, int splitLevel, + bool includeStdLib, bool generateIndex) { + using namespace HtmlDocOutput; + Group g(basename, basename); + FunMap funMap; + CollectFunctionsVisitor fv(env, funMap, includeStdLib); + iterItems(fv, m); + PrintHtmlVisitor phv(env, g, funMap, includeStdLib); + iterItems(phv, m); + + std::vector ret; + + struct SI { + Group* g; + Group* p; + int level; + int idx; + SI(Group* g0, Group* p0, int level0, int idx0) : g(g0), p(p0), level(level0), idx(idx0) {} + }; + + struct IndexEntry { + std::string id; + std::string sig; + std::string link; + std::string groupName; + IndexEntry(const std::string& id0, const std::string& sig0, const std::string& link0, + const std::string& groupName0) + : id(id0), sig(sig0), link(link0), groupName(groupName0) { + size_t spacepos = id.find_last_of(' '); + if (spacepos != std::string::npos) { + id = id.substr(spacepos + 1); + } + } + bool operator<(const IndexEntry& e) const { + if (!isalpha(id[0]) && isalpha(e.id[0])) return true; + return id == e.id ? groupName < e.groupName : id < e.id; + } + }; + std::vector index; + + std::vector stack; + stack.push_back(SI(&g, NULL, 0, 0)); + while (!stack.empty()) { + Group& g = *stack.back().g; + int curLevel = stack.back().level; + int curIdx = stack.back().idx; + Group* p = stack.back().p; + stack.pop_back(); + for (auto it : g.items) { + index.push_back(IndexEntry(it.id, it.sig, g.fullPath, g.htmlName)); + } + ret.push_back(HtmlDocument(g.fullPath, g.htmlName, + g.toHTML(curLevel, splitLevel, p, curIdx, basename, generateIndex))); + if (curLevel < splitLevel) { + for (unsigned int i = 0; i < g.subgroups.m.size(); i++) { + stack.push_back(SI(g.subgroups.m[i], &g, curLevel + 1, i)); + } + } + } + + if (generateIndex) { + std::sort(index.begin(), index.end()); + std::ostringstream oss; + index.push_back(IndexEntry("", "", "", "")); + + std::vector idxSections; + + if (index.size() != 0) { + if (isalpha(index[0].id[0])) { + char idxSec_c = (char)toupper(index[0].id[0]); + std::string idxSec(&idxSec_c, 1); + oss << "

" << idxSec << "

\n"; + idxSections.push_back(idxSec); + } else { + oss << "

Symbols

\n"; + idxSections.push_back("Symbols"); + } + } + oss << "
    \n"; + std::string prevId = index.size() == 0 ? "" : index[0].id; + std::vector curEntries; + for (auto ie : index) { + if (ie.id != prevId) { + oss << "
  • "; + assert(curEntries.size() != 0); + IndexEntry& cur = curEntries[0]; + if (curEntries.size() == 1) { + oss << cur.id << " " + << "(" << cur.groupName << ")"; + } else { + oss << cur.id << " ("; + bool first = true; + for (auto i_ie : curEntries) { + if (first) { + first = false; + } else { + oss << ", "; + } + oss << ""; + oss << i_ie.groupName << ""; + } + oss << ")"; + } + oss << "
  • \n"; + curEntries.clear(); + } + if (isalpha(ie.id[0]) && ie.id[0] != prevId[0]) { + char idxSec_c = (char)toupper(ie.id[0]); + std::string idxSec(&idxSec_c, 1); + oss << "
\n

" << idxSec << "

    "; + idxSections.push_back(idxSec); + } + prevId = ie.id; + if (curEntries.size() == 0 || curEntries.back().groupName != ie.groupName) { + curEntries.push_back(ie); + } + } + oss << "
\n"; + + std::ostringstream oss_header; + oss_header << "
\n"; + oss_header << "
"; + oss_header << "⇧ "; + bool first = true; + for (auto is : idxSections) { + if (first) { + first = false; + } else { + oss_header << " | "; + } + oss_header << "" << is << ""; + } + + oss_header << "
"; + + oss_header << "
Index
\n"; + + HtmlDocument idx("doc-index", "Index", oss_header.str() + oss.str()); + ret.push_back(idx); + } + return ret; +} + +class PrintRSTVisitor : public ItemVisitor { +protected: + EnvI& env; + HtmlDocOutput::Group& _maingroup; + HtmlDocOutput::FunMap& _funmap; + bool _includeStdLib; + + std::vector replaceArgsRST(std::string& s) { + std::vector replacements; + std::ostringstream oss; + size_t lastpos = 0; + size_t pos = std::min(s.find("\\a"), s.find("\\p")); + size_t mathjax_open = s.find("\\("); + size_t mathjax_close = s.rfind("\\)"); + if (pos == std::string::npos) return replacements; + while (pos != std::string::npos) { + oss << s.substr(lastpos, pos - lastpos); + size_t start = pos; + while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; + while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; + size_t end = start + 1; + while (end < s.size() && (isalnum(s[end]) || s[end] == '_')) end++; + bool needSpace = pos != 0 && s[pos - 1] != ' ' && s[pos - 1] != '\n'; + if (s[pos + 1] == 'a') { + replacements.push_back(s.substr(start, end - start)); + if (pos >= mathjax_open && pos <= mathjax_close) { + oss << "{\\bf " << replacements.back() << "}"; + } else { + oss << (needSpace ? " " : "") << "``" << replacements.back() << "`` "; + } + } else { + if (pos >= mathjax_open && pos <= mathjax_close) { + oss << "{\\bf " << s.substr(start, end - start) << "}"; + } else { + oss << (needSpace ? " " : "") << "``" << s.substr(start, end - start) << "`` "; + } + } + lastpos = end; + pos = std::min(s.find("\\a", lastpos), s.find("\\p", lastpos)); + } + oss << s.substr(lastpos, std::string::npos); + s = oss.str(); + + std::ostringstream oss2; + pos = std::min(s.find("\\("), s.find("\\)")); + lastpos = 0; + while (pos != std::string::npos) { + if (s[pos + 1] == ')') { + // remove trailing whitespace + std::string t = s.substr(lastpos, pos - lastpos); + size_t t_end = t.find_last_not_of(" "); + if (t_end != std::string::npos) t_end++; + oss2 << t.substr(0, t_end); + } else { + oss2 << s.substr(lastpos, pos - lastpos); + } + lastpos = pos + 2; + if (s[pos + 1] == '(') { + oss2 << ":math:`"; + lastpos = s.find_first_not_of(" ", lastpos); + } else { + oss2 << "`"; + } + pos = std::min(s.find("\\(", lastpos), s.find("\\)", lastpos)); + } + oss2 << s.substr(lastpos, std::string::npos); + s = oss2.str(); + return replacements; + } + + std::pair extractArgLine(std::string& s, size_t n) { + size_t start = n; + while (start < s.size() && s[start] != ' ' && s[start] != '\t') start++; + while (start < s.size() && (s[start] == ' ' || s[start] == '\t')) start++; + size_t end = start + 1; + while (end < s.size() && s[end] != ':') end++; + std::string arg = s.substr(start, end - start); + size_t doc_start = end + 1; + while (end < s.size() && s[end] != '\n') end++; + std::string ret = s.substr(doc_start, end - doc_start); + replaceArgsRST(ret); + s = s.substr(0, n) + s.substr(end, std::string::npos); + return make_pair(arg, ret); + } + +public: + PrintRSTVisitor(EnvI& env0, HtmlDocOutput::Group& mg, HtmlDocOutput::FunMap& fm, + bool includeStdLib) + : env(env0), _maingroup(mg), _funmap(fm), _includeStdLib(includeStdLib) {} + bool enterModel(Model* m) { + if (!_includeStdLib && m->filename() == "stdlib.mzn") return false; + const std::string& dc = m->docComment(); + if (!dc.empty()) { + size_t gpos = dc.find("@groupdef"); + while (gpos != std::string::npos) { + size_t start = gpos; + while (start < dc.size() && dc[start] != ' ' && dc[start] != '\t') start++; + while (start < dc.size() && (dc[start] == ' ' || dc[start] == '\t')) start++; + size_t end = start + 1; + while (end < dc.size() && (isalnum(dc[end]) || dc[end] == '_' || dc[end] == '.')) end++; + std::string groupName = dc.substr(start, end - start); + size_t doc_start = end + 1; + while (end < dc.size() && dc[end] != '\n') end++; + std::string groupHTMLName = dc.substr(doc_start, end - doc_start); + + size_t next = dc.find("@groupdef", gpos + 1); + HtmlDocOutput::setGroupDesc( + _maingroup, groupName, groupHTMLName, + (dc.substr(end, next == std::string::npos ? next : next - end))); + gpos = next; + } + } + return true; + } + /// Visit variable declaration + void vVarDeclI(VarDeclI* vdi) { + if (Call* docstring = Expression::dyn_cast( + getAnnotation(vdi->e()->ann(), constants().ann.doc_comment))) { + std::string ds = eval_string(env, docstring->arg(0)); + std::string group("main"); + size_t group_idx = ds.find("@group"); + if (group_idx != std::string::npos) { + group = HtmlDocOutput::extractArgWord(ds, group_idx); + } + std::ostringstream os; + std::string sig = vdi->e()->type().toString(env) + " " + vdi->e()->id()->str().str(); + + std::string myMainGroup = group.substr(0, group.find_first_of(".")); + auto it = _maingroup.subgroups.find(myMainGroup); + os << ".. index::\n"; + if (it != _maingroup.subgroups.m.end()) { + os << " pair: " << (*it)->htmlName << "; " << *vdi->e()->id() << "\n\n"; + } else { + std::cerr << "did not find " << myMainGroup << "\n"; + os << " single: " << *vdi->e()->id() << "\n\n"; + } + + os << ".. code-block:: minizinc\n\n"; + if (vdi->e()->ti()->type() == Type::ann()) { + os << " annotation " << *vdi->e()->id(); + } else { + os << " " << *vdi->e()->ti() << ": " << *vdi->e()->id(); + } + os << "\n\n"; + os << HtmlDocOutput::trim(ds) << "\n\n"; + GCLock lock; + HtmlDocOutput::DocItem di( + vdi->e()->type().ispar() ? HtmlDocOutput::DocItem::T_PAR : HtmlDocOutput::DocItem::T_VAR, + sig, sig, os.str()); + HtmlDocOutput::addToGroup(_maingroup, group, di); + } + } + /// Visit function item + void vFunctionI(FunctionI* fi) { + if (Call* docstring = + Expression::dyn_cast(getAnnotation(fi->ann(), constants().ann.doc_comment))) { + std::string ds = eval_string(env, docstring->arg(0)); + std::string group("main"); + size_t group_idx = ds.find("@group"); + if (group_idx != std::string::npos) { + group = HtmlDocOutput::extractArgWord(ds, group_idx); + } + + size_t param_idx = ds.find("@param"); + std::vector > params; + while (param_idx != std::string::npos) { + params.push_back(extractArgLine(ds, param_idx)); + param_idx = ds.find("@param"); + } + + std::vector args = replaceArgsRST(ds); + + std::unordered_set allArgs; + for (unsigned int i = 0; i < args.size(); i++) allArgs.insert(args[i]); + for (unsigned int i = 0; i < params.size(); i++) allArgs.insert(params[i].first); + + GCLock lock; + for (unsigned int i = 0; i < fi->params().size(); i++) { + if (allArgs.find(fi->params()[i]->id()->str().str()) == allArgs.end()) { + std::cerr << "Warning: parameter " << *fi->params()[i]->id() + << " not documented for function " << fi->id() << " at location " << fi->loc() + << "\n"; + } + } + + std::string sig; + { + GCLock lock; + FunctionI* fi_c = new FunctionI(Location(), fi->id(), fi->ti(), fi->params()); + std::ostringstream oss_sig; + oss_sig << *fi_c; + sig = oss_sig.str(); + sig.resize(sig.size() - 2); + } + + std::ostringstream os; + std::ostringstream fs; + std::string myMainGroup = group.substr(0, group.find_first_of(".")); + auto it = _maingroup.subgroups.find(myMainGroup); + os << ".. index::\n"; + if (it != _maingroup.subgroups.m.end()) { + os << " pair: " << (*it)->htmlName << "; " << fi->id() << "\n\n"; + } else { + std::cerr << "did not find " << myMainGroup << "\n"; + os << " single: " << fi->id() << "\n\n"; + } + os << ".. code-block:: minizinc\n\n"; + + if (fi->ti()->type() == Type::ann()) { + fs << "annotation "; + } else if (fi->ti()->type() == Type::parbool()) { + fs << "test "; + } else if (fi->ti()->type() == Type::varbool()) { + fs << "predicate "; + } else { + fs << "function " << *fi->ti() << ": "; + } + fs << fi->id() << "("; + os << " " << fs.str(); + size_t align = fs.str().size(); + for (unsigned int i = 0; i < fi->params().size(); i++) { + fs << *fi->params()[i]->ti(); + std::ostringstream fid; + fid << *fi->params()[i]->id(); + if (fid.str().size() != 0) fs << ": " << *fi->params()[i]->id(); + if (i < fi->params().size() - 1) { + fs << ", "; + } + } + bool splitArgs = (fs.str().size() > 70); + for (unsigned int i = 0; i < fi->params().size(); i++) { + os << *fi->params()[i]->ti(); + std::ostringstream fid; + fid << *fi->params()[i]->id(); + if (fid.str().size() != 0) os << ": " << *fi->params()[i]->id(); + if (i < fi->params().size() - 1) { + os << ","; + if (splitArgs) { + os << "\n "; + for (unsigned int j = static_cast(align); j--;) os << " "; + } else { + os << " "; + } + } + } + os << ")"; + + os << "\n\n"; + + if (fi->id().c_str()[0] == '\'') { + std::string op = fi->id().str(); + op = op.substr(1, op.length() - 2); + if (fi->params().size() == 2) { + os << "Usage: ``" << *fi->params()[0]->id() << " " << op << " " << *fi->params()[1]->id() + << "``\n\n"; + } else if (fi->params().size() == 1) { + os << "Usage: ``" << op << " " << *fi->params()[0]->id() << "``\n\n"; + } + } + + os << HtmlDocOutput::trim(ds) << "\n\n"; + + if (fi->e()) { + FunctionI* f_body = fi; + bool alias; + do { + alias = false; + Call* c = Expression::dyn_cast(f_body->e()); + if (c && c->n_args() == f_body->params().size()) { + bool sameParams = true; + for (unsigned int i = 0; i < f_body->params().size(); i++) { + Id* ident = c->arg(i)->dyn_cast(); + if (ident == NULL || ident->decl() != f_body->params()[i] || + ident->str() != c->decl()->params()[i]->id()->str()) { + sameParams = false; + break; + } + } + if (sameParams) { + alias = true; + f_body = c->decl(); + } + } + } while (alias); + if (f_body->e()) { + std::string filename = f_body->loc().filename().str(); + size_t filePos = filename.find("std/"); + if (filePos != std::string::npos) { + filePos += 4; + os << ".. only:: builder_html\n\n"; + os << " `More... loc().first_line() + << "-L" << f_body->loc().last_line() << ">`__\n\n"; + } + } + } + + if (params.size() > 0) { + os << "Parameters:\n\n"; + for (unsigned int i = 0; i < params.size(); i++) { + os << "- ``" << params[i].first << "``: " << params[i].second << "\n"; + } + os << "\n"; + } + os << "\n"; + + HtmlDocOutput::DocItem di(HtmlDocOutput::DocItem::T_FUN, fi->id().str(), sig, os.str()); + HtmlDocOutput::addToGroup(_maingroup, group, di); + } + } +}; + +std::vector RSTPrinter::printRST(EnvI& env, MiniZinc::Model* m, + const std::string& basename, int splitLevel, + bool includeStdLib, bool generateIndex) { + using namespace HtmlDocOutput; + Group g(basename, basename); + FunMap funMap; + CollectFunctionsVisitor fv(env, funMap, includeStdLib); + iterItems(fv, m); + PrintRSTVisitor prv(env, g, funMap, includeStdLib); + iterItems(prv, m); + + std::vector ret; + + std::ostringstream oss; + oss << Group::rstHeading(g.htmlName, 0); + oss << trim(g.desc) << "\n"; + oss << ".. toctree::\n\n"; + for (auto sg : g.subgroups.m) { + oss << " " << sg->fullPath << "\n"; + } + + ret.push_back(HtmlDocument(g.fullPath, g.htmlName, oss.str())); + + for (auto& sg : g.subgroups.m) { + ret.push_back(HtmlDocument(sg->fullPath, sg->htmlName, sg->toRST(0))); + } + return ret; +} + +} // namespace MiniZinc diff --git a/lib/interpreter.cpp b/lib/interpreter.cpp new file mode 100644 index 0000000..dbe9945 --- /dev/null +++ b/lib/interpreter.cpp @@ -0,0 +1,1578 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// #define DBG_CALLTRACE(msg) std::cerr << msg +#define DBG_CALLTRACE(msg) \ + do { \ + } while (0) + +namespace MiniZinc { + +Val AggregationCtx::createVec(Interpreter* interpreter, int timestamp) const { + return Val(Vec::a(interpreter, timestamp, stack)); +} + +AggregationCtx::~AggregationCtx(void) { + /// TODO +} + +const std::string AggregationCtx::symbol_to_string[] = {"AND", "OR", "VEC", "OTHER"}; + +const std::string Interpreter::status_to_string[] = {"Roger", "Aborted", "Inconsistent", "Error"}; + +void Interpreter::pushAgg(const Val& v, int stackOffset) { + assert(stackOffset < 0); + assert(_agg.size() + stackOffset >= 0); + // push value onto surrounding context + _agg[_agg.size() + stackOffset].push(this, v); +} + +void Interpreter::pushConstraint(Constraint* c) { _agg.back().constraints.push_back(c); } + +PropStatus Interpreter::subscribe(Constraint* c) { + // TODO: This disables propagation after trailing, this shouldn't be necessary if we can correctly + // communicate with the solvers + if (trail.is_trailed(_root_var)) { + return PS_OK; + } + if (c->pred() < primitiveMap().size()) { + return primitiveMap()[c->pred()]->subscribe(*this, c); + } + return PS_OK; +} + +void Interpreter::unsubscribe(Constraint* c) { + if (c->pred() < primitiveMap().size()) { + primitiveMap()[c->pred()]->unsubscribe(*this, c); + } +} + +void Interpreter::schedule(Constraint* c, const Variable::SubscriptionEvent& ev) { + if (!c->scheduled()) { + _propQueue.push_back(c); + c->scheduled(true); + } +} + +void Interpreter::deschedule(Constraint* c) { + /// TODO: is this required? seems to be unused + if (c->scheduled()) { + auto it = std::find(_propQueue.begin(), _propQueue.end(), c); + if (it != _propQueue.end()) { + _propQueue.erase(it); + } + } +} + +void Interpreter::propagate(void) { + while (!_propQueue.empty()) { + Constraint* c = _propQueue.front(); + _propQueue.pop_front(); + c->scheduled(false); + auto ps = primitiveMap()[c->pred()]->propagate(*this, c); + switch (ps) { + case PS_OK: + break; + case PS_FAILED: + _status = INCONSISTENT; + return; + case PS_ENTAILED: + // TODO: remove constraint + break; + } + } +} + +void Interpreter::run(void) { + if (_status != ROGER) { + return; + } + for (;;) { + DBG_INTERPRETER(frame().pc << " "); + switch (frame().bs->instr(frame().pc)) { + case BytecodeStream::ADDI: { + if (frame().pc > 130) { + DBG_INTERPRETER("NOOOOO!\n"); + } + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("ADDI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, reg(frame(), r1) + reg(frame(), r2)); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::SUBI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("SUBI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, reg(frame(), r1) - reg(frame(), r2)); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::MULI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("MULI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, reg(frame(), r1) * reg(frame(), r2)); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::DIVI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + if (reg(frame(), r2) == 0) + frame().pc = frame().bs->size() - 1; + else + assign(frame(), r3, reg(frame(), r1) / reg(frame(), r2)); + assign(frame(), r3, reg(frame(), r1) / reg(frame(), r2)); + DBG_INTERPRETER("DIVI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")" + << " " << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::MODI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + if (reg(frame(), r2) == 0) + frame().pc = frame().bs->size() - 1; + else + assign(frame(), r3, reg(frame(), r1) % reg(frame(), r2)); + DBG_INTERPRETER("MODI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")" + << " " << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::INCI: { + int r1 = frame().bs->reg(frame().pc); + assign(frame(), r1, reg(frame(), r1) + 1); + DBG_INTERPRETER("INCI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << "\n"); + } break; + case BytecodeStream::DECI: { + int r1 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("DECI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << "\n"); + assign(frame(), r1, reg(frame(), r1) - 1); + } break; + case BytecodeStream::IMMI: { + Val i = frame().bs->intval(frame().pc); + int r1 = frame().bs->reg(frame().pc); + assign(frame(), r1, i); + DBG_INTERPRETER("IMMI " << i.toString() << " R" << r1 << "(" << reg(frame(), r1).toString() + << ")" + << "\n"); + } break; + case BytecodeStream::CLEAR: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + assert(r1 <= r2); + for (int i = r1; i <= r2; i++) { + assign(frame(), i, 0); + } + DBG_INTERPRETER("CLEAR " + << " R" << r1 << " " << r2 << "\n"); + } break; + case BytecodeStream::LOAD_GLOBAL: { + int i = frame().bs->reg(frame().pc); + int r1 = frame().bs->reg(frame().pc); + globals.cp(this, i, _registers, frame().reg_offset + r1); + DBG_INTERPRETER("LOAD_GLOBAL " << i << " R" << r1 << "(" + << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::STORE_GLOBAL: { + int r1 = frame().bs->reg(frame().pc); + int i = frame().bs->reg(frame().pc); + _registers.cp(this, frame().reg_offset + r1, globals, i); + DBG_INTERPRETER("STORE_GLOBAL R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ")" + << " " << i << "\n"); + } break; + case BytecodeStream::MOV: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("MOV R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")"); + _registers.cp(this, frame().reg_offset + r1, frame().reg_offset + r2); + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::JMP: { + int i = frame().bs->reg(frame().pc); + DBG_INTERPRETER("JMP " << i << "\n"); + frame().pc = i; + } break; + case BytecodeStream::JMPIF: { + int r0 = frame().bs->reg(frame().pc); + int i = frame().bs->reg(frame().pc); + DBG_INTERPRETER("JMPIF R" << r0 << "(" << reg(frame(), r0).toString() << ")" + << " " << i << "\n"); + if (reg(frame(), r0) != 0) { + frame().pc = i; + } + } break; + case BytecodeStream::JMPIFNOT: { + int r0 = frame().bs->reg(frame().pc); + int i = frame().bs->reg(frame().pc); + DBG_INTERPRETER("JMPIFNOT R" << r0 << "(" << reg(frame(), r0).toString() << ")" + << " " << i << "\n"); + if (reg(frame(), r0) == 0) { + frame().pc = i; + } + } break; + case BytecodeStream::EQI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("EQI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assert(reg(frame(), r1) == reg(frame(), r2) || + reg(frame(), r1).toInt() != reg(frame(), r2).toInt()); + assign(frame(), r3, reg(frame(), r1) == reg(frame(), r2)); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::LTI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("LTI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, (reg(frame(), r1) < reg(frame(), r2))); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::LEI: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("LEI R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, (reg(frame(), r1) <= reg(frame(), r2))); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::AND: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("AND R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, (reg(frame(), r1) != 0 && reg(frame(), r2) != 0)); + DBG_INTERPRETER(" " << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::OR: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("OR R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, (reg(frame(), r1) != 0 || reg(frame(), r2) != 0)); + DBG_INTERPRETER(" " << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::NOT: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("NOT R" << r1); + assign(frame(), r2, (reg(frame(), r1) == 0)); + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString() << ")" + << "\n"); + } break; + case BytecodeStream::XOR: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("XOR R" << r1 << "(" << reg(frame(), r1).toString() << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assign(frame(), r3, ((reg(frame(), r1) != 0) ^ (reg(frame(), r2) != 0))); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString() << ")" + << "\n"); + } break; + case BytecodeStream::ISPAR: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("ISPAR R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ")"); + Val v = Val::follow_alias(reg(frame(), r1), this); + if (v.isInt()) { + assign(frame(), r2, 1); + } else if (v.isVar()) { + assign(frame(), r2, 0); + } else { + assert(v.isVec()); + Val ret = v.toVec()->isPar(); + assign(frame(), r2, ret); + } + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString() << ")" + << "\n"); + } break; + case BytecodeStream::ISEMPTY: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("ISEMPTY R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ")"); + assert(reg(frame(), r1).isInt() || reg(frame(), r1).isVec()); + assign(frame(), r2, (reg(frame(), r1).isInt() || reg(frame(), r1).size() == 0)); + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString() << ")" + << "\n"); + } break; + case BytecodeStream::LENGTH: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("LENGTH R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ")"); + assert(reg(frame(), r1).isVec()); + if (!reg(frame(), r1).toVec()->hasIndexSet()) { + assign(frame(), r2, reg(frame(), r1).size()); + } else { + assign(frame(), r2, reg(frame(), r1)[0].size()); + } + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString() << ")" + << "\n"); + } break; + case BytecodeStream::GET_VEC: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("GET_VEC R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ")" + << " R" << r2 << "(" << reg(frame(), r2).toString() << ")"); + assert(reg(frame(), r1).isVec()); + assert(reg(frame(), r2).isInt()); + assert(reg(frame(), r2) > 0 && reg(frame(), r2) <= reg(frame(), r1).size()); + Val v = Val::follow_alias(reg(frame(), r1)[reg(frame(), r2).toInt() - 1], this); + assign(frame(), r3, v); + DBG_INTERPRETER(" R" << r3 << "(" << v.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::GET_ARRAY: { + Val n = frame().bs->intval(frame().pc); + int r1 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("GET_ARRAY " << n.toString() << " R" << r1 << "(" + << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")"); + std::vector idx(n.toInt()); + for (int i = 0; i < n; i++) { + int rr = frame().bs->reg(frame().pc); + DBG_INTERPRETER(" R" << rr << "(" << reg(frame(), rr).toString(DBG_TRIM_OUTPUT) << ")"); + idx[i] = reg(frame(), rr); + } + int r_res = frame().bs->reg(frame().pc); + int r_cond = frame().bs->reg(frame().pc); + assert(reg(frame(), r1).isVec()); + + bool success = true; + Val v = Val(-2000); + + Val content = reg(frame(), r1).toVec()->raw_data(); + if (reg(frame(), r1).toVec()->hasIndexSet()) { + // N-Dimensional array representation + Val index_set = reg(frame(), r1).toVec()->index_set(); + std::vector> dimensions; + Val realdim = 1; + for (int i = 0; i < index_set.size(); i += 2) { + Val a = index_set[i]; + Val b = index_set[i + 1]; + dimensions.emplace_back(a, b); + realdim *= b - a + 1; + } + + Val realidx = 0; + for (int i = 0; i < idx.size(); i++) { + Val ix = idx[i]; + if (ix < dimensions[i].first || ix > dimensions[i].second) { + success = false; + break; + } + realdim /= dimensions[i].second - dimensions[i].first + 1; + realidx += (ix - dimensions[i].first) * realdim; + } + assert(realidx >= 0 && realidx < content.size()); + + if (success) { + v = Val::follow_alias(content[realidx.toInt()], this); + } + } else { + // Compact array representation (index set 1..n) + success = 1 <= idx[0] && idx[0] <= reg(frame(), r1).size(); + if (success) { + v = Val::follow_alias(content[idx[0].toInt() - 1], this); + } + } + + assign(frame(), r_res, v); + assign(frame(), r_cond, Val(success)); + DBG_INTERPRETER(" R" << r_res << "(" << v.toString(DBG_TRIM_OUTPUT) << ") R" << r_cond + << "(" << Val(success).toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::LB: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("LB R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")"); + Val v = Val::follow_alias(reg(frame(), r1), this); + assign(frame(), r2, v.lb()); + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::UB: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("UB R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")"); + Val v = Val::follow_alias(reg(frame(), r1), this); + assign(frame(), r2, v.ub()); + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::DOM: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("DOM R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")"); + Val v = Val::follow_alias(reg(frame(), r1), this); + if (v.isInt()) { + assign(frame(), r2, Val(Vec::a(this, newIdent(), {v, v}))); + } else if (v.isVar()) { + Variable* var = v.toVar(); + assign(frame(), r2, Val(var->domain())); + } else { + throw Error("Error: dom on invalid type"); + } + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::MAKE_SET: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("MAKE_SET R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ")"); + Val v1 = Val::follow_alias(reg(frame(), r1), this); + assert(reg(frame(), r1).isVec()); + Vec* a1 = v1.toVec(); + + std::vector result; + if (a1->size() > 0) { + std::vector vals(a1->size()); + for (int i = 0; i < a1->size(); i++) vals[i] = (*a1)[i]; + + std::sort(vals.begin(), vals.end()); + Val l(vals[0]); + Val u(vals[0]); + for (int i = 1; i < vals.size(); ++i) { + if (u + 1 < vals[i]) { + result.emplace_back(l); + result.emplace_back(u); + l = vals[i]; + } + u = vals[i]; + } + result.emplace_back(l); + result.emplace_back(u); + } + Val result_val(Vec::a(this, newIdent(), result)); + assign(frame(), r2, result_val); + DBG_INTERPRETER(" R" << r2 << "(" << result_val.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::INTERSECTION: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("INTERSECTION R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ") R" << r2 << "(" + << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")"); + Val v1 = Val::follow_alias(reg(frame(), r1), this); + Val v2 = Val::follow_alias(reg(frame(), r2), this); + Val result_val; + if (v1.isInt()) { + result_val = v2; + } else if (v2.isInt()) { + result_val = v1; + } else { + Vec* s1 = v1.toVec(); + Vec* s2 = v2.toVec(); + VecSetRanges vsr1(s1); + VecSetRanges vsr2(s2); + Ranges::Inter inter(vsr1, vsr2); + std::vector result; + for (; inter(); ++inter) { + result.emplace_back(inter.min()); + result.emplace_back(inter.max()); + } + result_val = Val(Vec::a(this, newIdent(), result)); + } + assign(frame(), r3, result_val); + DBG_INTERPRETER(" R" << r3 << "(" << result_val.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::UNION: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("UNION R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) + << ") R" << r2 << "(" + << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")"); + Val v1 = Val::follow_alias(reg(frame(), r1), this); + Val v2 = Val::follow_alias(reg(frame(), r2), this); + Val result_val; + if (v1.isInt()) { + result_val = v1; + } else if (v2.isInt()) { + result_val = v2; + } else { + Vec* s1 = v1.toVec(); + Vec* s2 = v2.toVec(); + VecSetRanges vsr1(s1); + VecSetRanges vsr2(s2); + Ranges::Union union_r(vsr1, vsr2); + std::vector result; + for (; union_r(); ++union_r) { + result.emplace_back(union_r.min()); + result.emplace_back(union_r.max()); + } + result_val = Val(Vec::a(this, newIdent(), result)); + } + assign(frame(), r3, result_val); + DBG_INTERPRETER(" R" << r3 << "(" << result_val.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::DIFF: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("DIFF R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ") R" + << r2 << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")"); + Val v1 = Val::follow_alias(reg(frame(), r1), this); + Val v2 = Val::follow_alias(reg(frame(), r2), this); + Val result_val; + Vec* s1 = v1.toVec(); + Vec* s2 = v2.toVec(); + VecSetRanges vsr1(s1); + VecSetRanges vsr2(s2); + Ranges::Diff diff_r(vsr1, vsr2); + std::vector result; + for (; diff_r(); ++diff_r) { + result.emplace_back(diff_r.min()); + result.emplace_back(diff_r.max()); + } + result_val = Val(Vec::a(this, newIdent(), result)); + assign(frame(), r3, result_val); + DBG_INTERPRETER(" R" << r3 << "(" << result_val.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::INTERSECT_DOMAIN: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("INTERSECT_DOMAIN R" + << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ") R" << r2 + << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")"); + Val v1 = Val::follow_alias(reg(frame(), r1), this); + Val v2 = reg(frame(), r2); + assert(v2.isVec()); + + Val result_val; + Vec* s2 = v2.toVec(); + if (v1.isVar()) { + Vec* s1 = v1.toVar()->domain(); + VecSetRanges vsr1(s1); + VecSetRanges vsr2(s2); + Ranges::Inter inter(vsr1, vsr2); + std::vector result; + for (; inter(); ++inter) { + result.emplace_back(inter.min()); + result.emplace_back(inter.max()); + } + if (result.empty()) { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + break; + } + v1.toVar()->domain(this, result, true); + Val v1a = Val::follow_alias(v1); + if (v1a.isVar()) { + result_val = Val(v1a.toVar()->domain()); + } else { + result_val = Val(Vec::a(this, newIdent(), {v1a, v1a})); + } + } else { + Ranges::Const vsr1(v1, v1); + VecSetRanges vsr2(s2); + if (Ranges::subset(vsr1, vsr2)) { + result_val = Val(Vec::a(this, newIdent(), {v1, v1})); + } else { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + break; + } + } + assign(frame(), r3, result_val); + DBG_INTERPRETER(" R" << r3 << "(" << result_val.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } break; + case BytecodeStream::RET: { + DBG_INTERPRETER("RET"); + if (frame().cse_frame_depth < _cse_stack.size()) { + CSEFrame& info = _cse_stack.back(); + DBG_INTERPRETER(" %-- " << info.proc << " (" << _procs[info.proc].name << ") " + << BytecodeProc::mode_to_string[info.mode]); + } + DBG_INTERPRETER("\n"); + assert(!_stack.empty()); + execute_ret: + if (_stack.size() == 1) { + // Always leave final frame on the stack + // Copy remaining constraints into toplevel + assert(_agg.size() == 1); + for (Constraint* c : _agg[0].constraints) { + root()->addDefinition(this, c); + } + _agg[0].constraints.clear(); + return; + } + + DBG_CALLTRACE("<"); + for (size_t i = 0; i < _stack.size() - 1; ++i) { + DBG_CALLTRACE("--"); + } + DBG_CALLTRACE(" " << (_stack.back()._mode == BytecodeProc::ROOT || + _stack.back()._mode == BytecodeProc::ROOT_NEG + ? "POSTED" + : (_cse_stack.back().stack_size == _agg.back().size() - 1 + ? _agg[_agg.size() - 1].back().toString(DBG_TRIM_OUTPUT) + : "")) + << "\n"); + + while (_cse_stack.size() > frame().cse_frame_depth) { + CSEFrame& entry = _cse_stack.back(); + int nargs = _procs[entry.proc].nargs; + if (entry.mode == BytecodeProc::ROOT || entry.mode == BytecodeProc::ROOT_NEG) { + Val v = Val(1); + cse_insert(entry.proc, entry.getKey(), entry.mode, v); + } else if (entry.stack_size == _agg.back().size() - 1) { + Val ret = _agg[_agg.size() - 1].back(); + cse_insert(entry.proc, entry.getKey(), entry.mode, ret); + } else { + entry.destroy(*this); + } + _cse_stack.pop_back(); + } + + _registers.resize(this, frame().reg_offset); + _stack.pop_back(); + } break; + case BytecodeStream::CALL: { + char mode_c = frame().bs->chr(frame().pc); + int code = frame().bs->reg(frame().pc); + bool cse = frame().bs->chr(frame().pc); + assert(code >= 0); + assert(code < _procs.size()); + assert(mode_c >= 0); + assert(mode_c <= BytecodeProc::MAX_MODE); + auto mode = static_cast(mode_c); + int n = _procs[code].nargs; + DBG_INTERPRETER("CALL " << BytecodeProc::mode_to_string[mode] << " " << code << "(" + << _procs[code].name << ")" << (cse ? "" : " no_cse")); + for (size_t i = 0; i < _stack.size(); ++i) { + DBG_CALLTRACE("--"); + } + DBG_CALLTRACE("> " << _procs[code].name << "("); + + // Allocate new frame on the stack + _stack.emplace_back(_procs[code].mode[mode], code, mode); + frame().reg_offset = _registers.size(); + _registers.resize(this, frame().reg_offset + frame().bs->maxRegister() + 1); + // Copy arguments + for (int i = 0; i < n; i++) { + int r = _stack[_stack.size() - 2].bs->reg(_stack[_stack.size() - 2].pc); + assign(frame(), i, reg(_stack[_stack.size() - 2], r)); + DBG_INTERPRETER(" R" << r << "(" << reg(frame(), i).toString(DBG_TRIM_OUTPUT) << ")" + << ((i + 1 < n) ? "" : "\n")); + DBG_CALLTRACE(reg(frame(), i).toString(DBG_TRIM_OUTPUT) << ((i + 1 < n) ? ", " : ")\n")); + } + + auto immediate_return = [&] { + _registers.resize(this, frame().reg_offset); + _stack.pop_back(); + }; + + // Lookup for Common Subexpression Elimination + size_t cse_depth = _cse_stack.size(); + if (cse) { + _cse_stack.emplace_back(*this, code, mode, _registers.iter_n(frame().reg_offset), + _registers.iter_n(frame().reg_offset + n), n, _agg.back().size()); + // Lookup item in CSE + auto lookup = cse_find(code, _cse_stack.back().getKey(), mode); + if (lookup.second) { + _cse_stack.back().destroy(*this); + _cse_stack.pop_back(); + if (mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG) { + assert(lookup.first.isInt()); + if (lookup.first.toInt() != 1) { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + } + } else { + pushAgg(lookup.first, -1); + } + immediate_return(); + break; + } + } + + if (_procs[code].mode[mode].size() == 0 || _procs[code].delay) { + DBG_INTERPRETER((_procs[code].delay ? "--- Delayed CALL\n" : "--- FZN Builtin\n")); + DBG_CALLTRACE("<"); + for (size_t i = 0; i < _stack.size(); ++i) { + DBG_CALLTRACE("--"); + } + // this is a FlatZinc builtin + if (code == PrimitiveMap::MK_INTVAR) { + assert(mode == BytecodeProc::ROOT); + Variable* v = Variable::a(this, _registers[frame().reg_offset], true, newIdent()); + pushAgg(Val(v), -1); + DBG_CALLTRACE(" " << Val(v).toString() << "\n"); + } else { + assert(mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG); + auto c = Constraint::a(this, code, mode, _registers.iter_n(frame().reg_offset), + _registers.iter_n(frame().reg_offset + n), n); + if (!(c.first || c.second)) { + // Propagation failed + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + break; + } + if (c.first) { + pushConstraint(c.first); + } + if (cse) { + Val ret(c.second); + cse_insert(code, _cse_stack.back().getKey(), mode, ret); + _cse_stack.pop_back(); + } + DBG_CALLTRACE(" POSTED\n"); + /// TODO: delayed calls + // if (_procs[code].delay) { + // delayed_calls.push_back(c); + // } + } + immediate_return(); + } + } break; + case BytecodeStream::BUILTIN: { + int code = frame().bs->reg(frame().pc); + assert(code >= 0); + DBG_INTERPRETER("BUILTIN " << code << "(" << _procs[code].name << ")"); + assert(code < primitiveMap().size()); + // this is a Interpreter builtin + int n = _procs[code].nargs; + std::vector args(n); + for (int i = 0; i < n; i++) { + int r = frame().bs->reg(frame().pc); + // Do not increase the reference count. Vector is destroyed after + // BUILTIN execution without cleanup + args[i] = reg(frame(), r); + DBG_INTERPRETER(" R" << r << "(" << args[i].toString(DBG_TRIM_OUTPUT) << ")"); + } + DBG_INTERPRETER("\n"); + primitiveMap()[code]->execute(*this, args); + } break; + case BytecodeStream::TCALL: { + char mode_c = frame().bs->chr(frame().pc); + int code = frame().bs->reg(frame().pc); + bool cse = frame().bs->chr(frame().pc); + assert(code >= 0); + assert(code < _procs.size()); + assert(mode_c >= 0); + assert(mode_c <= BytecodeProc::MAX_MODE); + auto mode = static_cast(mode_c); + DBG_INTERPRETER("TCALL " << BytecodeProc::mode_to_string[mode] << " " << code << "(" + << _procs[code].name << ")" << (cse ? "" : " no_cse") << "\n"); + for (size_t i = 0; i < _stack.size() - 1; ++i) { + DBG_CALLTRACE("=="); + } + DBG_CALLTRACE("> " << _procs[code].name << "("); + int nargs = _procs[code].nargs; + for (int i = 0; i < nargs; ++i) { + DBG_CALLTRACE(reg(frame(), i).toString(DBG_TRIM_OUTPUT) + << ((i + 1 < nargs) ? ", " : ")\n")); + } + + if (cse) { + _cse_stack.emplace_back(*this, code, mode, _registers.iter_n(frame().reg_offset), + _registers.iter_n(frame().reg_offset + nargs), nargs, + _agg.back().size()); + bool found; + Val ret; + std::tie(ret, found) = cse_find(code, _cse_stack.back().getKey(), mode); + if (found) { + _cse_stack.back().destroy(*this); + _cse_stack.pop_back(); + // RET with CSE found value + if (mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG) { + assert(ret.isInt()); + if (ret.toInt() != 1) { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + } + } else { + pushAgg(ret, -1); + } + // TODO: Does this actually "return" correctly?? It seems the code would continue; + while (_cse_stack.size() > frame().cse_frame_depth) { + CSEFrame& entry = _cse_stack.back(); + int nargs = _procs[entry.proc].nargs; + cse_insert(entry.proc, entry.getKey(), entry.mode, ret); + _cse_stack.pop_back(); + } + _registers.resize(this, frame().reg_offset); + _stack.pop_back(); + break; + } + } + if (_procs[code].mode[mode].size() == 0 || _procs[code].delay) { + DBG_INTERPRETER((_procs[code].delay ? "--- Delayed CALL\n" : "--- FZN Builtin\n")); + // this is a FlatZinc builtin + assert(mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG); + auto c = Constraint::a(this, code, mode, _registers.iter_n(frame().reg_offset), + _registers.iter_n(frame().reg_offset + nargs), nargs); + if (!(c.first || c.second)) { + // Propagation failed + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + break; + } + if (c.first) { + pushConstraint(c.first); + } + if (cse) { + Val ret(c.second); + // TODO: Does this actually "return" correctly?? It seems this would not set all + // necessary values in the _cse_stack + cse_insert(code, _cse_stack.back().getKey(), mode, ret); + _cse_stack.pop_back(); + } + /// TODO: delayed calls + // if (_procs[code].delay) { + // delayed_calls.push_back(def); + // } + goto execute_ret; + } else { + // Replace frame with new procedure + frame()._pred = code; + frame()._mode = mode; + frame().bs = &_procs[code].mode[mode]; + _registers.resize(this, frame().reg_offset + frame().bs->maxRegister() + 1); + frame().pc = 0; + } + } break; + case BytecodeStream::ITER_ARRAY: { + int r1 = frame().bs->reg(frame().pc); + int l = frame().bs->reg(frame().pc); + DBG_INTERPRETER("ITER_ARRAY " << r1 << " " << l << "\n"); + assert(reg(frame(), r1).isVec()); + if (reg(frame(), r1).toVec()->hasIndexSet()) { + assert(reg(frame(), r1).size() == 2); + assert(reg(frame(), r1)[0].isVec()); + + Vec* v(reg(frame(), r1)[0].toVec()); + _loops.push_back(LoopState(v, l)); + } else { + // Compact array representation (index set 1..n) + Vec* v(reg(frame(), r1).toVec()); + _loops.push_back(LoopState(v, l)); + } + } break; + case BytecodeStream::ITER_VEC: { + int r1 = frame().bs->reg(frame().pc); + int l = frame().bs->reg(frame().pc); + DBG_INTERPRETER("ITER_VEC " << r1 << " " << l << "\n"); + assert(reg(frame(), r1).isVec()); + Vec* v(reg(frame(), r1).toVec()); + _loops.push_back(LoopState(v, l)); + } break; + case BytecodeStream::ITER_RANGE: { + int r1 = frame().bs->reg(frame().pc); + int r2 = frame().bs->reg(frame().pc); + int lbl = frame().bs->reg(frame().pc); + + Val lb = Val::follow_alias(reg(frame(), r1), this); + Val ub = Val::follow_alias(reg(frame(), r2), this); + assert(lb.isInt()); + assert(ub.isInt()); + DBG_INTERPRETER("ITER_RANGE " << r1 << " " << r2 << " " << lbl << "\n"); + _loops.push_back(LoopState(lb.toInt(), ub.toInt(), lbl)); + } break; + case BytecodeStream::ITER_NEXT: { + int r1 = frame().bs->reg(frame().pc); + DBG_INTERPRETER("ITER_NEXT"); + assert(_loops.size() > 0); + LoopState& outer(_loops.back()); + if (outer.pos < outer.end) { + Val v; + if (outer.is_range) { + v = outer.pos; + outer.pos++; + } else { + Val* ptr(reinterpret_cast(outer.pos)); + v = Val::follow_alias(*ptr); + outer.pos += sizeof(Val*); + } + assign(frame(), r1, v); + DBG_INTERPRETER(" R" << r1 << "(" << v.toString(DBG_TRIM_OUTPUT) << ")" + << "\n"); + } else { + frame().pc = outer.exit_pc; + _loops.pop_back(); + DBG_INTERPRETER(" (EXIT " << outer.exit_pc << ")\n"); + } + } break; + case BytecodeStream::ITER_BREAK: { + int num = frame().bs->intval(frame().pc).toInt(); + DBG_INTERPRETER("ITER_BREAK " << num << "\n"); + assert(_loops.size() >= num); + auto it(_loops.end() - num); + frame().pc = it->exit_pc; + _loops.erase(it, _loops.end()); + } break; + case BytecodeStream::TRACE: { + int r = frame().bs->reg(frame().pc); + DBG_INTERPRETER("TRACE " << r << "\n"); + std::cerr << reg(frame(), r).toString() << "\n"; + // std::cerr << frame().reg.e(r)->cast(); + // std::cerr << frame()().reg.e(r) << "\n"; + } break; + case BytecodeStream::ABORT: { + DBG_INTERPRETER("ABORT\n"); + + _registers.resize(this, 0); + _stack.clear(); + // TODO: Should the Aggregation stack be emptied? + + if (_status == ROGER) { + _status = ABORTED; + } + return; + } + case BytecodeStream::PUSH: { + int r = frame().bs->reg(frame().pc); + DBG_INTERPRETER("PUSH R" << r << " (" << reg(frame(), r).toString(DBG_TRIM_OUTPUT) + << ")\n"); + assert(!_agg.empty()); + _agg.back().push(this, reg(frame(), r)); + } break; + case BytecodeStream::POP: { + int r = frame().bs->reg(frame().pc); + assert(!_agg.empty()); + assert(!_agg.back().empty()); + assign(frame(), r, _agg.back().back()); + DBG_INTERPRETER("POP R" << r << " (" << reg(frame(), r).toString(DBG_TRIM_OUTPUT) << ")\n"); + _agg.back().pop(this); + } break; + case BytecodeStream::POST: { + int r = frame().bs->reg(frame().pc); + DBG_INTERPRETER("POST R" << r << " (" << reg(frame(), r).toString(DBG_TRIM_OUTPUT) + << ")\n"); + Val v1 = Val::follow_alias(reg(frame(), r), this); + if (v1.isInt()) { + if (v1 == 0) { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + } + } else { + bool success = v1.toVar()->setVal(this, 1); + if (!success) { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + frame().pc = frame().bs->size() - 1; + } + /// TODO: check why this is adding a ref + /// Shouldn't be necessary + // v1.toVar()->addRef(this); + } + } break; + case BytecodeStream::OPEN_AGGREGATION: { + int r = frame().bs->chr(frame().pc); + DBG_INTERPRETER("OPEN_AGGREGATION " << AggregationCtx::symbol_to_string[r] << ", depth " + << _agg.size() + 1 << "\n"); + assert(r >= 0 && r <= AggregationCtx::VCTX_OTHER); + if (r == AggregationCtx::VCTX_OTHER || r == AggregationCtx::VCTX_VEC || _agg.empty() || + _agg.back().symbol != r) { + // Push a new aggregation context + _agg.emplace_back(this, r); + } else { + // Increment depth counter for current aggregation context + _agg.back().n_symbols++; + } + } break; + case BytecodeStream::SIMPLIFY_LIN: { + DBG_INTERPRETER("SIMPLIFY_LIN"); + + int r0 = frame().bs->reg(frame().pc); + int r1 = frame().bs->reg(frame().pc); + Val i = frame().bs->intval(frame().pc); + DBG_INTERPRETER(" R" << r0 << "(" << reg(frame(), r0).toString(DBG_TRIM_OUTPUT) << ")"); + DBG_INTERPRETER(" R" << r1 << "(" << reg(frame(), r1).toString(DBG_TRIM_OUTPUT) << ")"); + DBG_INTERPRETER(" " << i.toString()); + int r2 = frame().bs->reg(frame().pc); + int r3 = frame().bs->reg(frame().pc); + int r4 = frame().bs->reg(frame().pc); + + std::vector coeffs({1, -1}); + std::vector vars({reg(frame(), r0), reg(frame(), r1)}); + Val d = i; + + simplify_linexp(coeffs, vars, d); + + Val coeffs_v = Val(Vec::a(this, newIdent(), coeffs)); + Val vars_v = Val(Vec::a(this, newIdent(), vars)); + assign(frame(), r2, coeffs_v); + assign(frame(), r3, vars_v); + assign(frame(), r4, -d); + DBG_INTERPRETER(" R" << r2 << "(" << reg(frame(), r2).toString(DBG_TRIM_OUTPUT) << ")"); + DBG_INTERPRETER(" R" << r3 << "(" << reg(frame(), r3).toString(DBG_TRIM_OUTPUT) << ")\n"); + DBG_INTERPRETER(" R" << r4 << "(" << reg(frame(), r4).toString(DBG_TRIM_OUTPUT) << ")\n"); + } break; + case BytecodeStream::CLOSE_AGGREGATION: { + DBG_INTERPRETER("CLOSE_AGGREGATION (" + << AggregationCtx::symbol_to_string[_agg.back().symbol] << ", depth " + << _agg.size() << ")\n"); + assert(!_agg.empty()); + // Decrement depth counter for current aggregation context + _agg.back().n_symbols--; + if (_agg.back().n_symbols == 0) { + // Result produced by this aggregation + Variable* result = nullptr; + + assert(_agg.size() >= 2); + switch (_agg.back().symbol) { + case AggregationCtx::VCTX_AND: { + // Create a conjunction on the definition stack + std::vector args; + args.reserve(_agg.back().size()); + bool isFalse = false; + for (int i = 0; i < _agg.back().size(); i++) { + const Val& v = _agg.back()[i]; + if (v.isInt()) { + if (v == 0) { + // Disjunction is constant false + isFalse = true; + break; + } + } else { + args.push_back(v); + } + } + if (isFalse || args.empty()) { + /// TODO: check, what if _agg.size()==2 as below? + // Conjunction is constant true or false + pushAgg(!isFalse, -2); + } else if (_agg.size() == 2) { + // Push into root context + for (Val v : args) { + auto success = v.toVar()->setVal(this, 1); + // FIXME: Deal with unsuccessful setVal + assert(success); + } + pushAgg(1, -2); + + // Why do we need a definition? If this is in ROOT, then all arguments should be + // true + /* Definition* d = + * Definition::a(this,boolean_domain(),false,PrimitiveMap::FORALL,BytecodeProc::ROOT, + */ + /* {Val(Vec::a(this,newIdent(),args))},-1); */ + /* if (defs) { */ + /* d->appendBefore(this, defs); */ + /* } else { */ + /* defs = d; */ + /* } */ + } else if (args.size() == 1) { + pushAgg(args[0], -2); + } else { + Vec* arr = Vec::a(this, newIdent(), args); + result = Variable::a(this, boolean_domain(), false, newIdent()); + auto def_c = Constraint::a(this, PrimitiveMap::FORALL, BytecodeProc::ROOT, + {Val(arr), Val(result)}); + assert(def_c.first); + result->addRef(this); + result->addDefinition(this, def_c.first); + pushAgg(Val(result), -2); + RefCountedObject::rmRef(this, result); + } + } break; + case AggregationCtx::VCTX_OR: { + // Create a clause on the definition stack, and push a reference + // to it onto the aggregation stack + std::vector pos; + std::vector neg; + bool isTrue = false; + for (int i = 0; i < _agg.back().size(); i++) { + const Val& v = Val::follow_alias(_agg.back()[i]); + if (v.isInt()) { + if (v != 0) { + // Disjunction is constant true + isTrue = true; + break; + } + } else { + Variable* cur = v.toVar(); + if (Constraint* defby = cur->defined_by()) { + if (defby->pred() == PrimitiveMap::BOOLNOT) { + if (Val::follow_alias(defby->arg(0)) == v) { + neg.push_back(Val::follow_alias(defby->arg(1))); + } else { + neg.push_back(Val::follow_alias(defby->arg(0))); + } + continue; + } + } + pos.push_back(v); + } + } + if (isTrue || (pos.empty() && neg.empty())) { + // Disjunction is constant true or false + pushAgg(isTrue, -2); + } else if (pos.size() == 1 && neg.empty()) { + if (_agg.size() == 2) { + auto success = pos[0].toVar()->setVal(this, 1); + // FIXME: Deal with unsuccessful setVal + assert(success); + pushAgg(1, -2); + } else { + pushAgg(pos[0], -2); + } + } else { + // Push into root context + Vec* vpos = Vec::a(this, newIdent(), pos); + Vec* vneg = Vec::a(this, newIdent(), neg); + if (_agg.size() == 2) { + auto c = Constraint::a(this, PrimitiveMap::CLAUSE, BytecodeProc::ROOT, + {Val(vpos), Val(vneg)}, 0, 1, true); + assert(c.first); + root()->addDefinition(this, c.first); + pushAgg(1, -2); + } else { + result = Variable::a(this, boolean_domain(), false, newIdent()); + auto def_c = Constraint::a(this, PrimitiveMap::CLAUSE_REIF, BytecodeProc::ROOT, + {Val(vpos), Val(vneg), Val(result)}, 0, 1, true); + assert(def_c.first); + result->addRef(this); + result->addDefinition(this, def_c.first); + pushAgg(Val(result), -2); + RefCountedObject::rmRef(this, result); + } + } + } break; + case AggregationCtx::VCTX_VEC: { + // Create a vector on the aggregation stack + _agg[_agg.size() - 2].push(this, _agg.back().createVec(this, newIdent())); + } break; + case AggregationCtx::VCTX_OTHER: + // When closing a VCTX_OTHER context, it should contain at most one value + assert(_agg.back().size() <= 1); + if (_agg.back().size() == 1) { + if (_agg.back()[0].isVar()) { + result = _agg.back()[0].toVar(); + } + // push value onto surrounding context + pushAgg(_agg.back()[0], -2); + } + break; + } + + _agg.back().destroyStack(this); + if (result && !_agg.back().constraints.empty()) { + // Aggregation produced constraints and exactly one return value, which is a variable + if (result->timestamp() >= _agg.back().def_ident_start) { + // the definition was produced by the current frame, so + // attach all constraints to it + for (Constraint* c : _agg.back().constraints) { + result->addDefinition(this, c); + } + _agg.back().constraints.clear(); + // INVARIANT: The result of aggregation is not referenced by any of the registers. + assert(std::none_of(_registers.cbegin() + frame().reg_offset, _registers.cend(), + [result](Val v) { return v.contains(Val(result)); })); + result->binding(this, false); + } + } + if (!_agg.back().constraints.empty()) { + if (_agg.size() == 2) { + // only one frame left, so move all constraints into root context + for (Constraint* c : _agg.back().constraints) { + root()->addDefinition(this, c); + } + } else { + // Move definitions to parent aggregation + for (Constraint* c : _agg.back().constraints) { + _agg[_agg.size() - 2].constraints.push_back(c); + } + } + _agg.back().constraints.clear(); + } + _agg.pop_back(); + } + } break; + } + assert(!frame().bs->eos(frame().pc)); + } +} + +void Interpreter::dumpState(std::ostream& os) { + Variable::dump(root(), _procs, os); + // if (!_agg.empty()) { + // Definition::dump(_agg.back().def_stack, _procs, os, true); + // } +} +void Interpreter::dumpState(const BytecodeFrame& bf, std::ostream& os) { + for (unsigned int i = bf.reg_offset; i < bf.reg_offset + bf.bs->maxRegister(); i++) { + os << " R" << i << " = " << _registers[i].toString() << "\n"; + } +} + +void Interpreter::dumpState() { dumpState(std::cerr); } + +Interpreter::~Interpreter(void) { + globals.destroy(this); + _registers.resize(this, 0); + _stack.clear(); + for (auto& a : _agg) { + a.destroyStack(this); + // a.destroyDef(this); /// TODO: replace with what? Just delete all constraints? + } + assert(cse.size() == _procs.size()); + for (int i = 0; i < _procs.size(); ++i) { + switch (_procs[i].nargs) { + case 1: { + auto table = static_cast>*>(cse[i]); + table->destroy(this); + break; + } + case 2: { + auto table = static_cast>*>(cse[i]); + table->destroy(this); + break; + } + case 3: { + auto table = static_cast>*>(cse[i]); + table->destroy(this); + break; + } + case 4: { + auto table = static_cast>*>(cse[i]); + table->destroy(this); + break; + } + default: { + auto table = static_cast*>(cse[i]); + table->destroy(this); + break; + } + } + } + RefCountedObject::rmRef(this, infinite_dom); + RefCountedObject::rmRef(this, boolean_dom); + RefCountedObject::rmRef(this, true_dom); +} + +void Interpreter::call(int code, std::vector&& args) { + if (_status != ROGER) { + return; + } + assert(code >= 0); + assert(code < _procs.size()); + int n = _procs[code].nargs; + const bool cse = true; + BytecodeProc::Mode mode = BytecodeProc::ROOT; + DBG_INTERPRETER("Interpreter::call " << BytecodeProc::mode_to_string[mode] << " " << code << "(" + << _procs[code].name << ")" << (cse ? "" : " no_cse")); + DBG_CALLTRACE("\n> " << _procs[code].name << "("); + assert(n == args.size()); + for (int i = 0; i < n; i++) { + DBG_INTERPRETER(" R" << i << "(" << args[i].toString(DBG_TRIM_OUTPUT) << ")"); + DBG_CALLTRACE(args[i].toString(DBG_TRIM_OUTPUT) << ((i + 1 < n) ? ", " : ")\n")); + } + DBG_INTERPRETER("\n"); + + // Lookup for Common Subexpression Elimination + size_t cse_depth = _cse_stack.size(); + if (cse) { + _cse_stack.emplace_back(*this, code, mode, args.cbegin(), args.cend(), n, _agg.back().size()); + // Lookup item in CSE + auto lookup = cse_find(code, _cse_stack.back().getKey(), mode); + if (lookup.second) { + _cse_stack.back().destroy(*this); + _cse_stack.pop_back(); + if (mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG) { + assert(lookup.first.isInt()); + if (lookup.first.toInt() != 1) { + _status = INCONSISTENT; + // Invariant: Last instruction in the frame is always an ABORT instruction + return; + } + } else { + // pushAgg(lookup.first, -1); + assert(false); // Delayed calls can only be ROOT mode + } + return; + } + } + + if (_procs[code].mode[mode].size() == 0) { + DBG_INTERPRETER("--- FZN Builtin\n"); + // this is a FlatZinc builtin + assert(code != PrimitiveMap::MK_INTVAR); + assert(mode == BytecodeProc::ROOT || mode == BytecodeProc::ROOT_NEG); + auto c = Constraint::a(this, code, mode, args); + if (!(c.first || c.second)) { + // Propagation failed + _status = INCONSISTENT; + return; + } + if (c.first) { + pushConstraint(c.first); + } + if (cse) { + Val ret(c.second); + cse_insert(code, _cse_stack.back().getKey(), mode, ret); + _cse_stack.pop_back(); + } + return; + } + + // Make the next instruction RET + _stack.back().pc--; + _stack.emplace_back(_procs[code].mode[mode], code, mode); + BytecodeFrame& newFrame = _stack.back(); + newFrame.reg_offset = _registers.size(); + _registers.resize(this, newFrame.reg_offset + newFrame.bs->maxRegister() + 1); + for (int i = 0; i < args.size(); i++) { + assign(newFrame, i, args[i]); + } + newFrame.cse_frame_depth = cse_depth; + + return run(); +} + +bool Interpreter::runDelayed() { + std::unordered_map wave = std::move(delayed_constraints); + delayed_constraints.clear(); + for (auto pair : wave) { + // Only run delayed constraints that have a definition. + Constraint* c = pair.first; + assert(c->delayed()); + if (_procs[c->pred()].mode[BytecodeProc::ROOT].size() == 0) { + continue; + } + + // Remove placeholder constraint from variable + Val alias = Val::follow_alias(Val(pair.second)); + Variable* v = alias.isVar() ? alias.toVar() : root(); + // TODO: This might be quite expensive. Should we store _definitions differently? + auto pos = std::find(std::begin(v->_definitions), std::end(v->_definitions), c); + assert(pos != v->_definitions.end()); + v->_definitions.erase(pos); + + // Copy arguments into vector + std::vector args(c->size()); + for (int i = 0; i < c->size(); ++i) { + args[i] = c->arg(i); + } + + // Execute constraint + std::swap(v, _root_var); + call(c->pred(), std::move(args)); + std::swap(v, _root_var); + + // Stop if execution caused an error + if (_status != ROGER) { + break; + } + } + return !delayed_constraints.empty(); +} + +size_t Trail::save_state(MiniZinc::Interpreter* interpreter) { + trail_size.emplace_back(var_list_trail.size(), obj_trail.size(), alias_trail.size(), + domain_trail.size(), def_trail.size()); + timestamp_trail.push_back(interpreter->_identCount); + for (int i = 0; i < interpreter->_procs.size(); ++i) { + switch (interpreter->_procs[i].nargs) { + case 1: { + auto table = static_cast>*>(interpreter->cse[i]); + table->push(interpreter, !last_operation_pop); + break; + } + case 2: { + auto table = static_cast>*>(interpreter->cse[i]); + table->push(interpreter, !last_operation_pop); + break; + } + case 3: { + auto table = static_cast>*>(interpreter->cse[i]); + table->push(interpreter, !last_operation_pop); + break; + } + case 4: { + auto table = static_cast>*>(interpreter->cse[i]); + table->push(interpreter, !last_operation_pop); + break; + } + default: { + auto table = static_cast*>(interpreter->cse[i]); + table->push(interpreter, !last_operation_pop); + break; + } + } + } + last_operation_pop = false; + return len(); +} + +void Trail::untrail(MiniZinc::Interpreter* interpreter) { + assert(len() > 0); + assert(interpreter->_stack.size() == 1); + size_t vlt_size, ot_size, at_size, dt_size, deft_size; + std::tie(vlt_size, ot_size, at_size, dt_size, deft_size) = trail_size.back(); + trail_size.pop_back(); + int timestamp = timestamp_trail.back(); + timestamp_trail.pop_back(); + // Reconstruct destroyed items + while (obj_trail.size() > ot_size) { + auto obj = obj_trail.back(); + switch (obj->rcoType()) { + case RefCountedObject::VAR: + static_cast(obj)->reconstruct(interpreter); + break; + case RefCountedObject::VEC: + static_cast(obj)->reconstruct(interpreter); + break; + default: + assert(false); + } + obj_trail.pop_back(); + } + // Restore hedge pointers back to their previous versions + while (var_list_trail.size() > vlt_size) { + auto entry = var_list_trail.back(); + *entry.first = entry.second; + var_list_trail.pop_back(); + } + // Restore original definitions for created aliases + while (alias_trail.size() > at_size) { + Variable* var; + Val dom; + std::tie(var, dom) = alias_trail.back(); + var->unalias(interpreter, dom); + dom.rmMemRef(interpreter); + alias_trail.pop_back(); + } + // Restore original domains + while (domain_trail.size() > dt_size) { + Variable* var; + Vec* dom; + std::tie(var, dom) = domain_trail.back(); + Val nd(dom); + nd.addRef(interpreter); + var->_domain.rmRef(interpreter); + var->_domain = nd; + RefCountedObject::rmMemRef(interpreter, dom); + domain_trail.pop_back(); + } + // Remove all additions/changes to the CSE table + for (int i = 0; i < interpreter->_procs.size(); ++i) { + switch (interpreter->_procs[i].nargs) { + case 1: { + auto table = static_cast>*>(interpreter->cse[i]); + table->pop(interpreter); + break; + } + case 2: { + auto table = static_cast>*>(interpreter->cse[i]); + table->pop(interpreter); + break; + } + case 3: { + auto table = static_cast>*>(interpreter->cse[i]); + table->pop(interpreter); + break; + } + case 4: { + auto table = static_cast>*>(interpreter->cse[i]); + table->pop(interpreter); + break; + } + default: { + auto table = static_cast*>(interpreter->cse[i]); + table->pop(interpreter); + break; + } + } + } + // Undo all changes to definitions + while (def_trail.size() > deft_size) { + Variable* var; + Constraint* con; + bool remove; + std::tie(var, con, remove) = def_trail.back(); + if (remove) { + assert(var->definitions().back() == con); + var->_definitions.pop_back(); + } else { + var->_definitions.push_back(con); + } + def_trail.pop_back(); + } + // Remove all newly created variables + Variable* back = interpreter->root()->prev(); + while (back->timestamp() > timestamp) { + Variable* rem = back; + back = back->prev(); + rem->destroy(interpreter); + Variable::free(rem); + } + // TODO: Should we remove newly created propagators?? + // Reset the timestamp count to its previous value + interpreter->_identCount = timestamp; + last_operation_pop = true; +} + +void Interpreter::optimize(void) {} + +} // namespace MiniZinc diff --git a/lib/interpreter/bytecode.cpp b/lib/interpreter/bytecode.cpp new file mode 100644 index 0000000..6c7b023 --- /dev/null +++ b/lib/interpreter/bytecode.cpp @@ -0,0 +1,250 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +const std::string BytecodeProc::mode_to_string[] = {"ROOT", "ROOT_NEG", "FUN", + "FUN_NEG", "IMP", "IMP_NEG"}; + +std::string BytecodeStream::toString(const std::vector& procs) const { + std::ostringstream oss; + int pc = 0; + while (pc < _bs.size()) { + int cur_pc = pc; + switch (instr(pc)) { + case BytecodeStream::ADDI: { + oss << "ADDI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::SUBI: { + oss << "SUBI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::MULI: { + oss << "MULI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::DIVI: { + oss << "DIVI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::MODI: { + oss << "MODI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::INCI: { + oss << "INCI R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::DECI: { + oss << "DECI R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::IMMI: { + oss << "IMMI " << intval(pc).toIntVal() << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::CLEAR: { + oss << "CLEAR " + << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::LOAD_GLOBAL: { + oss << "LOAD_GLOBAL " << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::STORE_GLOBAL: { + oss << "STORE_GLOBAL R" << reg(pc) << " " << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::MOV: { + oss << "MOV R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::JMP: { + oss << "JMP " << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::JMPIF: { + oss << "JMPIF R" << reg(pc) << " " << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::JMPIFNOT: { + oss << "JMPIFNOT R" << reg(pc) << " " << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::EQI: { + oss << "EQI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::LTI: { + oss << "LTI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::LEI: { + oss << "LEI R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::AND: { + oss << "AND R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::OR: { + oss << "OR R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::NOT: { + oss << "NOT R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::XOR: { + oss << "XOR R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::ISPAR: { + oss << "ISPAR R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::ISEMPTY: { + oss << "ISEMPTY R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::LENGTH: { + oss << "LENGTH R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::GET_VEC: { + oss << "GET_VEC R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc + << "\n"; + } break; + case BytecodeStream::GET_ARRAY: { + oss << "GET_ARRAY "; + long long int n = intval(pc).toInt(); + oss << n; + for (long long int i = 0; i < (n + 3); ++i) { + oss << " R" << reg(pc); + } + oss << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::LB: { + oss << "LB R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::UB: { + oss << "UB R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::DOM: { + oss << "DOM R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::MAKE_SET: { + oss << "MAKE_SET R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::INTERSECTION: { + oss << "INTERSECTION R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc + << "\n"; + } break; + case BytecodeStream::UNION: { + oss << "UNION R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc + << "\n"; + } break; + case BytecodeStream::DIFF: { + oss << "DIFF R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::INTERSECT_DOMAIN: { + oss << "INTERSECT_DOMAIN R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " + << cur_pc << "\n"; + } break; + case BytecodeStream::RET: { + oss << "RET" + << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::CALL: { + auto m = static_cast(chr(pc)); + int p = reg(pc); + bool cse = chr(pc); + assert(!procs.empty()); + oss << "CALL " << BytecodeProc::mode_to_string[m] << " " << procs[p].name + << (cse ? "" : " no_cse") << " "; + oss << procs[p].nargs; + for (int i = 0; i < procs[p].nargs; i++) { + oss << " R" << reg(pc); + } + oss << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::BUILTIN: { + int p = reg(pc); + oss << "BUILTIN " << p << " "; + oss << procs[p].nargs; + for (int i = 0; i < procs[p].nargs; i++) { + oss << " R" << reg(pc); + } + oss << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::TCALL: { + auto m = static_cast(chr(pc)); + int p = reg(pc); + bool cse = chr(pc); + + if (procs.empty()) { + oss << "TCALL " << BytecodeProc::mode_to_string[m] << " " << p << (cse ? "" : " no_cse") + << " % " << cur_pc << "\n"; + } else { + oss << "TCALL " << BytecodeProc::mode_to_string[m] << " " << procs[p].name + << (cse ? "" : " no_cse") << " % " << cur_pc << "\n"; + } + } break; + case BytecodeStream::ITER_ARRAY: { + oss << "ITER_ARRAY" << reg(pc) << " " << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::ITER_VEC: { + oss << "ITER_VEC " << reg(pc) << " " << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::ITER_RANGE: { + oss << "ITER_RANGE " << reg(pc) << " " << reg(pc) << " " << reg(pc) << " % " << cur_pc + << "\n"; + } break; + case BytecodeStream::ITER_NEXT: { + oss << "ITER_NEXT R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::ITER_BREAK: { + oss << "ITER_BREAK " << intval(pc).toString() << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::OPEN_AGGREGATION: { + oss << "OPEN_AGGREGATION "; + int p = chr(pc); + switch (p) { + case AggregationCtx::VCTX_AND: + oss << "AND"; + break; + case AggregationCtx::VCTX_OR: + oss << "OR"; + break; + case AggregationCtx::VCTX_VEC: + oss << "VEC"; + break; + case AggregationCtx::VCTX_OTHER: + oss << "OTHER"; + break; + default: + oss << "ERROR"; + assert(false); + break; + } + oss << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::CLOSE_AGGREGATION: { + oss << "CLOSE_AGGREGATION" + << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::SIMPLIFY_LIN: { + oss << "SIMPLIFY_LIN R" << reg(pc) << " R" << reg(pc) << " " << intval(pc).toIntVal() + << " R" << reg(pc) << " R" << reg(pc) << " R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::PUSH: { + oss << "PUSH R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::POP: { + oss << "POP R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::POST: { + oss << "POST R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::TRACE: { + oss << "TRACE R" << reg(pc) << " % " << cur_pc << "\n"; + } break; + case BytecodeStream::ABORT: { + oss << "ABORT" + << " % " << cur_pc << "\n"; + } break; + } + } + return oss.str(); +} + +} // namespace MiniZinc diff --git a/lib/interpreter/constraint.cpp b/lib/interpreter/constraint.cpp new file mode 100644 index 0000000..e36f5e6 --- /dev/null +++ b/lib/interpreter/constraint.cpp @@ -0,0 +1,95 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +namespace MiniZinc { + +Constraint::Constraint(Interpreter* interpreter, int pred, char mode, arg_iter arg_start, + arg_iter arg_end, size_t nargs, Val ann, Val defines, bool delayed) + : _pred(pred), _mode(mode), _size(nargs), _scheduled(0), _delayed(delayed) { + _ann.addRef(interpreter); + int i = 0; + for (auto it = arg_start; it != arg_end; ++it) { + new (&_args[i]) Val(*it); + _args[i].addRef(interpreter); + if (!_delayed && pred > PrimitiveMap::MAX_LIN) { + _args[i].finalizeLin(interpreter); + } + ++i; + } + assert(i == nargs); +} + +Constraint::Constraint(Interpreter* interpreter, int pred, char mode, const std::vector& args, + Val ann, Val defines, bool delayed) + : _pred(pred), _mode(mode), _size(args.size()), _scheduled(0), _delayed(delayed) { + _ann.addRef(interpreter); + for (unsigned int i = 0; i < args.size(); i++) { + new (&_args[i]) Val(args[i]); + _args[i].addRef(interpreter); + if (!_delayed && pred > PrimitiveMap::MAX_LIN) { + _args[i].finalizeLin(interpreter); + } + } +} + +std::pair Constraint::a(Interpreter* interpreter, int pred, char mode, + arg_iter arg_start, arg_iter arg_end, size_t nargs, + Val defines, Val ann, bool delayed) { + Constraint* c = static_cast( + ::malloc(sizeof(Constraint) + sizeof(Val) * (std::max(0, static_cast(nargs) - 1)))); + c = new (c) Constraint(interpreter, pred, mode, arg_start, arg_end, nargs, ann, defines, delayed); + PropStatus ps = interpreter->subscribe(c); + if (ps == PS_ENTAILED || ps == PS_FAILED) { + interpreter->unsubscribe(c); + c->destroy(interpreter); + ::free(c); + return {nullptr, ps == PS_ENTAILED}; + } + return {c, true}; +} + +std::pair Constraint::a(Interpreter* interpreter, int pred, char mode, + const std::vector& args, Val defines, Val ann, + bool delayed) { + Constraint* c = static_cast(::malloc( + sizeof(Constraint) + sizeof(Val) * (std::max(0, static_cast(args.size()) - 1)))); + c = new (c) Constraint(interpreter, pred, mode, args, ann, defines, delayed); + PropStatus ps = interpreter->subscribe(c); + if (ps == PS_ENTAILED || ps == PS_FAILED) { + interpreter->unsubscribe(c); + c->destroy(interpreter); + ::free(c); + return {nullptr, ps == PS_ENTAILED}; + } + return {c, true}; +} + +void Constraint::destroy(Interpreter* interpreter) { + for (unsigned int i = 0; i < _size; i++) { + _args[i].rmRef(interpreter); + } + if (delayed()) { + interpreter->remove_delayed(this); + } +} + +void Constraint::reconstruct(Interpreter* interpreter) { + for (unsigned int i = 0; i < _size; i++) { + _args[i].addRef(interpreter); + } +} + +} // namespace MiniZinc diff --git a/lib/interpreter/primitives.cpp b/lib/interpreter/primitives.cpp new file mode 100644 index 0000000..dfb979a --- /dev/null +++ b/lib/interpreter/primitives.cpp @@ -0,0 +1,307 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +namespace BytecodePrimitives { +IntPlus int_plus; +IntMinus int_minus; +IntSum int_sum; +IntTimes int_times; + +IntLinEq int_lin_eq; +IntLinEqReif int_lin_eq_reif; +IntLinLe int_lin_le; +IntLinLeReif int_lin_le_reif; + +MkIntVar mk_intvar; +BoolNot boolnot; +OpNot opnot; +Clause clause; +ClauseReif clause_reif; +Forall forall; +Exists exists; +Uniform uniform; +Sol sol; +Sort sort; +SortBy sortby; +IntMax intmax; +Infinity infinity; +InfiniteDomain inf_dom; +BooleanDomain bool_dom; +SliceXd slice_xd; +ArrayXd array_xd; +IndexSet index_set; + +PrimitiveMap::Primitive* AllPrimitives[] = { + &int_plus, &int_minus, + &int_sum, &int_times, + + &int_lin_eq, &int_lin_eq_reif, + &int_lin_le, &int_lin_le_reif, + + &mk_intvar, &boolnot, + &opnot, &clause, + &clause_reif, &forall, + &exists, &uniform, + &sol, &sort, + &sortby, &intmax, + &infinity, &inf_dom, + &bool_dom, &slice_xd, + &array_xd, &index_set, +}; +} // namespace BytecodePrimitives + +PrimitiveMap::PrimitiveMap(void) : _p(PrimitiveMap::MAX_ID + 1) { + for (PrimitiveMap::Primitive* p : BytecodePrimitives::AllPrimitives) { + _p[p->ident()] = p; + } + for (Primitive* p : _p) { + _s.insert(std::make_pair(p->name(), p)); + } + _n.resize(_s.size()); + for (auto& entry : _s) { + _n[entry.second->ident()] = entry.first; + } +} + +PrimitiveMap& primitiveMap(void) { + static PrimitiveMap _pm; + return _pm; +} + +namespace BytecodePrimitives { + +void Uniform::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 2); + assert(args[0].isInt() && args[1].isInt()); + + std::uniform_int_distribution<> dis(args[0].toInt(), args[1].toInt()); + Val rnd = dis(generator); + i.pushAgg(rnd, -1); +} + +void Sol::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 1); + if (args[0].isVar()) { + auto it = i.solutions.find(args[0].timestamp()); + assert(it != i.solutions.end()); + i.pushAgg(Val(it->second), -1); + } else { + assert(args[0].isInt()); + i.pushAgg(args[0], -1); + } +}; + +void Sort::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 1); + + Val al = args[0].toVec()->raw_data(); + std::vector ai(al.size()); + for (int j = 0; j < al.size(); j++) { + ai[j] = al[j].toInt(); + } + std::stable_sort(ai.begin(), ai.end()); + + std::vector sorted(al.size()); + for (int j = 0; j < al.size(); j++) { + sorted[j] = Val(ai[j]); + } + Vec* al_sorted = Vec::a(&i, i.newIdent(), sorted); + + i.pushAgg(Val(al_sorted), -1); +}; + +void SortBy::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 2); + + Val al = args[0].toVec()->raw_data(); + Val order_e = args[1].toVec()->raw_data(); + std::vector order(order_e.size()); + std::vector a(order_e.size()); + for (int j = 0; j < order.size(); j++) { + a[j] = j; + order[j] = order_e[j]; + } + struct Ord { + std::vector& order; + explicit Ord(std::vector& order0) : order(order0) {} + bool operator()(int i, int j) { return order[i] < order[j]; } + } _ord(order); + std::stable_sort(a.begin(), a.end(), _ord); + std::vector sorted(a.size()); + for (int j = sorted.size(); j--;) { + sorted[j] = al[a[j]]; + } + Vec* al_sorted = Vec::a(&i, i.newIdent(), sorted); + + i.pushAgg(Val(al_sorted), -1); +}; + +void Infinity::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 1); + assert(args[0].isInt()); + + if (args[0] > 0) { + i.pushAgg(Val::infinity(), -1); + } else { + i.pushAgg(-Val::infinity(), -1); + } +} + +void InfiniteDomain::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 0); + i.pushAgg(i.infinite_domain(), -1); +} + +void BooleanDomain::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 0); + i.pushAgg(i.boolean_domain(), -1); +}; + +void SliceXd::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 3); + assert(args[0].isVec() && args[1].isVec() && args[2].isVec()); + + Val content = args[0].toVec()->raw_data(); + Val selection = args[1].toVec()->raw_data(); + Val new_idxs = args[2].toVec()->raw_data(); + + std::vector idxs(args[1].size()); + std::vector slice; + // Initialise indexes to the index lower bound + if (args[0].toVec()->hasIndexSet()) { + Val index_set = args[0].toVec()->index_set(); + assert(index_set.size() / 2 == selection.size()); + for (int j = 0; j < idxs.size(); ++j) { + idxs[j] = index_set[j * 2]; + } + } else { + assert(selection.size() == 1); + idxs[0] = 1; + } + + // Walk through array and make slice selection + int level = idxs.size() - 1; + int it = 0; + while (level >= 0) { + bool in_slice = true; + for (int k = 0; k < idxs.size(); ++k) { + assert(selection[k].size() == 2); + in_slice = in_slice && selection[k][0] <= idxs[k] && idxs[k] <= selection[k][1]; + } + + assert(it < content.size()); + if (in_slice) { + slice.push_back(content[it]); + } + it++; + + while (level >= 0) { + if (args[0].toVec()->hasIndexSet()) { + Val index_set = args[0].toVec()->index_set(); + if (idxs[level] < index_set[level * 2 + 1]) { + idxs[level]++; + level = idxs.size() - 1; + break; + } else { + idxs[level] = index_set[level * 2]; + level--; + } + } else { + assert(level == 0); + if (idxs[0] < content.size()) { + idxs[0]++; + // No need to reset level, there is only one. + break; + } else { + // Done (no need to reset index values). + level--; + } + } + } + } + + // Format new index sets + std::vector dom; + dom.reserve(new_idxs.size() * 2); + for (int j = 0; j < new_idxs.size(); ++j) { + assert(new_idxs[j].size() == 2); + dom.push_back(new_idxs[j][0]); + dom.push_back(new_idxs[j][1]); + } + + Vec* values = Vec::a(&i, i.newIdent(), slice); + Vec* idx = Vec::a(&i, i.newIdent(), dom); + Vec* nv = Vec::a(&i, i.newIdent(), {Val(values), Val(idx)}, true); + + i.pushAgg(Val(nv), -1); +} + +void ArrayXd::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 2); + assert(args[0].isVec()); + + // Array Elements + Val arr = args[0].toVec()->raw_data(); + + if (args[1].isInt()) { + assert(args[1].toInt() == 0); + i.pushAgg(arr, -1); + return; + } + + assert(args[1].isVec()); + assert(args[1].size() % 2 == 0); + // Check if the index sets actually match up + int prod = 1; + for (int i = 0; i < args[1].size(); i += 2) { + prod *= (args[1][i + 1].toInt() - args[1][i].toInt() + 1); + } + if (arr.size() != prod) { + throw std::runtime_error("ArrayXd cardinality mismatch"); + } + + Val ret = arr; + if (args[1].size() != 0 && !(args[1].size() == 2 && args[1][0].toInt() == 1)) { + ret = Val(Vec::a(&i, i.newIdent(), {arr, args[1]}, true)); + } + i.pushAgg(ret, -1); +} + +void IndexSet::execute(Interpreter& i, const std::vector& args) { + assert(args.size() == 2); + assert(args[0].isVec() && args[1].isInt()); + + Val ret; + if (!args[0].toVec()->hasIndexSet()) { + assert(args[1].toInt() == 1 || args[1] == 0); + ret = Val(Vec::a(&i, i.newIdent(), {Val(1), Val(args[0].size())})); + } else if (args[1] == 0) { + ret = args[0].toVec()->index_set(); + } else { + assert(args[0].size() == 2); + assert(args[0][1].isVec()); + assert(args[0][1].size() / 2 >= args[1].toInt()); + Val index_sets = args[0].toVec()->index_set(); + ret = Val( + Vec::a(&i, i.newIdent(), + {index_sets[(args[1].toInt() - 1) * 2], index_sets[(args[1].toInt() - 1) * 2 + 1]})); + } + i.pushAgg(ret, -1); +} + +} // namespace BytecodePrimitives +} // namespace MiniZinc diff --git a/lib/interpreter/values.cpp b/lib/interpreter/values.cpp new file mode 100644 index 0000000..c5076bc --- /dev/null +++ b/lib/interpreter/values.cpp @@ -0,0 +1,311 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +namespace MiniZinc { + +std::tuple, std::vector, Val> simplify_linexp(Val v) { + std::vector coeffs = {Val(1)}; + std::vector vars = {v}; + Val d = 0; + simplify_linexp(coeffs, vars, d); + return {coeffs, vars, d}; +}; + +void simplify_linexp(std::vector& coeffs, std::vector& vars, Val& d) { + assert(coeffs.size() == vars.size()); + std::vector> defs; + defs.reserve(vars.size()); + for (int j = vars.size() - 1; j >= 0; --j) { + if (coeffs[j] != 0) { + defs.emplace_back(coeffs[j], vars[j]); + } + } + coeffs.clear(); + vars.clear(); + + std::vector idx; + while (!defs.empty()) { + Val coeff = defs.back().first; + Val stacktop = Val::follow_alias(defs.back().second); + defs.pop_back(); + if (coeff == 0) continue; + if (stacktop.isInt()) { + d += coeff * stacktop; + } else { + Variable* cur = stacktop.toVar(); + if (Constraint* defby = cur->defined_by()) { + switch (defby->pred()) { + case PrimitiveMap::INT_PLUS: { + assert(stacktop == Val::follow_alias(defby->arg(2))); + for (int i = 0; i < 2; ++i) { + Val arg = Val::follow_alias(defby->arg(i)); + if (arg.isInt()) { + d += coeff * arg; + } else { + defs.emplace_back(coeff, arg); + } + } + continue; + } + case PrimitiveMap::INT_MINUS: { + assert(stacktop == Val::follow_alias(defby->arg(2))); + Val lhs = Val::follow_alias(defby->arg(0)); + if (lhs.isInt()) { + d += coeff * lhs; + } else { + defs.emplace_back(coeff, lhs); + } + Val rhs = Val::follow_alias(defby->arg(1)); + if (rhs.isInt()) { + d += coeff * -rhs; + } else { + defs.emplace_back(-coeff, rhs); + } + continue; + } + case PrimitiveMap::INT_SUM: { + assert(stacktop == Val::follow_alias(defby->arg(1))); + Val arr = defby->arg(0).toVec()->raw_data(); + for (int i = 0; i < arr.size(); ++i) { + Val arg = Val::follow_alias(arr[i]); + if (arg.isInt()) { + d += coeff * arg; + } else { + defs.emplace_back(coeff, arg); + } + } + continue; + } + case PrimitiveMap::INT_TIMES: { + assert(stacktop == Val::follow_alias(defby->arg(2))); + Val lhs = Val::follow_alias(defby->arg(0)); + Val rhs = Val::follow_alias(defby->arg(1)); + if (lhs.isInt()) { + if (rhs.isInt()) { + // both constants, compute result + d += coeff * lhs * rhs; + } else { + defs.emplace_back(coeff * lhs, rhs); + } + continue; + } + if (rhs.isInt()) { + defs.emplace_back(coeff * rhs, lhs); + continue; + } + break; + } + default: { + } + } + } + if (coeff != 0) { + coeffs.emplace_back(coeff); + vars.emplace_back(cur); + idx.push_back(idx.size()); + } + } + } + + if (coeffs.size() > 1) { + // Find and merge duplicate variables + class CmpValIdx { + public: + std::vector& x; + explicit CmpValIdx(std::vector& x0) : x(x0) {} + bool operator()(int i, int j) const { return x[i].timestamp() < x[j].timestamp(); } + }; + std::sort(idx.begin(), idx.end(), CmpValIdx(vars)); + std::vector coeffs_simple; + coeffs_simple.reserve(coeffs.size()); + std::vector vars_simple; + vars_simple.reserve(vars.size()); + + int ci = 0; + coeffs_simple.push_back(coeffs[idx[0]]); + vars_simple.push_back(vars[idx[0]]); + bool foundDuplicates = false; + for (unsigned int i = 1; i < idx.size(); i++) { + if (vars[idx[i]].timestamp() == vars_simple[ci].timestamp()) { + coeffs_simple[ci] += coeffs[idx[i]]; + foundDuplicates = true; + } else { + coeffs_simple.push_back(coeffs[idx[i]]); + vars_simple.push_back(vars[idx[i]]); + ci++; + } + } + int j = 0; + for (unsigned int i = 0; i < coeffs_simple.size(); i++) { + if (coeffs_simple[i] != 0) { + coeffs[j] = coeffs_simple[i]; + vars[j++] = vars_simple[i]; + } + } + coeffs.resize(j); + vars.resize(j); + } +} + +std::string Val::toString(bool trim) const { + std::ostringstream oss; + Val v = follow_alias(*this); + if (v.isInt()) { + oss << v.toIntVal(); + } else if (v.isVar()) { + if (v.timestamp() >= 0) { + oss << "X" << timestamp() << "("; + } + oss << v.lb().toString() << "," << v.ub().toString(); + if (v.toVar()->timestamp() >= 0) { + oss << ")"; + } + } else { + oss << "X" << v.toVec()->timestamp() << "["; + if (!trim || v.size() <= 4) { + for (size_t i = 0; i < v.size(); i++) { + oss << v[i].toString(trim); + if (i < v.size() - 1) oss << ","; + } + } else { + oss << "<->"; + } + oss << "]"; + } + return oss.str(); +} + +// WARNING: Provide interpreter variable only if v is an uncopied reference +// with a strong reference count. The reference (and the reference count of +// the RCO) will be adjusted in case of an alias. +Val Val::follow_alias(const Val& v, Interpreter* interpreter) { + if (v.isVar() && v.toVar()->aliased()) { + Val nval = v; + while (nval.isVar() && nval.toVar()->aliased()) { + nval = nval.toVar()->alias(); + } + if (interpreter && !interpreter->trail.is_trailed(v.toVar())) { + Val& mut_v = const_cast(v); + nval.addRef(interpreter); + mut_v.rmRef(interpreter); + mut_v._v = nval._v; + } + return nval; + } else { + return v; + } +} + +Val Val::lb() const { + if (isInt()) { + return *this; + } else if (isVec()) { + // Assume it is a set (sorted Vec of integer ranges) + assert(operator[](0).isInt()); + return operator[](0); + } else { + return toVar()->lb(); + } +} + +Val Val::ub() const { + if (isInt()) { + return *this; + } else if (isVec()) { + // Assume it is a set (sorted Vec of integer ranges) + assert(operator[](size() - 1).isInt()); + return operator[](size() - 1); + } else { + return toVar()->ub(); + } +} + +void Val::finalizeLin(Interpreter* interpreter) { + if (isInt()) { + return; + } + if (isVar()) { + Constraint* c = this->toVar()->defined_by(); + if (c && c->pred() <= PrimitiveMap::PARTIAL_LINEAR) { + // Create linear equation + std::vector coeffs = {Val(1)}; + std::vector vars = {*this}; + Val d = 0; + simplify_linexp(coeffs, vars, d); + + Constraint* nc = nullptr; + if (vars.empty()) { + bool succes = this->toVar()->setVal(interpreter, d); + assert(succes); + } else if (c->pred() == PrimitiveMap::INT_TIMES && vars.size() == 1 && vars[0] == *this) { + // Times operation between two variables. Just leave it as it is. + return; + } else { + assert(std::none_of(vars.begin(), vars.end(), [&](Val v) { return v == *this; })); + coeffs.push_back(Val(-1)); + vars.push_back(*this); + + Vec* ncoeffs = Vec::a(interpreter, interpreter->newIdent(), coeffs); + ncoeffs->addRef(interpreter); + Vec* nvars = Vec::a(interpreter, interpreter->newIdent(), vars); + nvars->addRef(interpreter); + + std::vector args = {Val(ncoeffs), Val(nvars), Val(-d)}; + FixedKey<3> cse_key(*interpreter, args); + BytecodeProc::Mode mode = BytecodeProc::ROOT; + auto lookup = interpreter->cse_find(PrimitiveMap::INT_LIN_EQ, cse_key, mode); + if (lookup.second) { + // CSE Match found (perform cleanup) + assert(lookup.first.isInt()); + cse_key.destroy(*interpreter); + + // This is the assumption that the linear expression doesn't exist + // in negated form in the CSE. If this would happen then the state + // should have been marked inconsistent. + assert(lookup.first.toInt() == 1); + } else { + bool b; + std::tie(nc, b) = Constraint::a(interpreter, PrimitiveMap::INT_LIN_EQ, mode, args); + assert(nc || b); + Val cse_val(1); + interpreter->cse_insert(PrimitiveMap::INT_LIN_EQ, cse_key, mode, cse_val); + } + + RefCountedObject::rmRef(interpreter, ncoeffs); + RefCountedObject::rmRef(interpreter, nvars); + } + // Need to check whether variable still has a definition + // (may have been aliased during the construction of nc) + c = this->toVar()->defined_by(); + this->toVar()->_definitions.clear(); + if (nc) { + this->toVar()->addDefinition(interpreter, nc); + } + + if (c) { + // still had a definition, so destroy it + // FIXME: c->destroy will remove a non-existing reference to this. + this->toVar()->addRef(interpreter); + c->destroy(interpreter); + ::free(c); + } + } + } else { + this->toVec()->finalizeLin(interpreter); + } +} + +} // namespace MiniZinc diff --git a/lib/interpreter/variable.cpp b/lib/interpreter/variable.cpp new file mode 100644 index 0000000..639fecf --- /dev/null +++ b/lib/interpreter/variable.cpp @@ -0,0 +1,454 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include + +namespace MiniZinc { + +Variable::Variable(Interpreter* interpreter, Val domain, int ident) + : RefCountedObject(RefCountedObject::VAR, ident), + _prev(this), + _next(this), + _domain(domain), + _binding(true), + _aliased(false) { + assert(_domain.isVec()); + _domain.addRef(interpreter); + _ann.addRef(interpreter); + addRef(interpreter); +} + +Variable::Variable(Interpreter* interpreter, Val domain, bool binding, int ident, Val ann) + : RefCountedObject(RefCountedObject::VAR, ident), + _prev(this), + _next(this), + _domain(domain), + _ann(ann), + _binding(binding), + _aliased(false) { + assert(_domain.isVec()); + _domain.addRef(interpreter); + _ann.addRef(interpreter); + if (binding) addRef(interpreter); + // insert into root variable list + Variable* v = interpreter->root(); + interpreter->trail.trail_ptr(this, &_prev); + _prev = v->_prev; + interpreter->trail.trail_ptr(this, &_next); + _next = v; + interpreter->trail.trail_ptr(v->_prev, &v->_prev->_next); + v->_prev->_next = this; + interpreter->trail.trail_ptr(v, &v->_prev); + v->_prev = this; +} + +bool Variable::setMin(Interpreter* interpreter, Val i, bool binding) { + assert(!aliased()); + assert(_domain.isVec()); + assert(_domain.size() % 2 == 0); + size_t j = 0; + while (j < _domain.size() && _domain[j] < i) { + ++j; + } + if (j == 0) { + return true; + } + if (j == _domain.size()) { + Val ndom(Vec::a(interpreter, interpreter->newIdent(), {})); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return false; + } + std::vector dom; + if (j % 2 == 1) { + dom.emplace_back(i); + } + for (; j < _domain.size(); ++j) { + dom.push_back(_domain[j]); + } + Val ndom(Vec::a(interpreter, interpreter->newIdent(), dom)); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return true; +} + +bool Variable::setMax(Interpreter* interpreter, Val i, bool binding) { + assert(!aliased()); + assert(_domain.isVec()); + assert(_domain.size() % 2 == 0); + int j = _domain.size() - 1; + while (j >= 0 && _domain[j] > i) { + --j; + } + if (j == _domain.size() - 1) { + return true; + } + if (j < 0) { + Val ndom(Vec::a(interpreter, interpreter->newIdent(), {})); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return false; + } + std::vector dom; + for (size_t k = 0; k <= j; ++k) { + dom.push_back(_domain[k]); + } + if (j % 2 == 0) { + dom.emplace_back(i); + } + Val ndom(Vec::a(interpreter, interpreter->newIdent(), dom)); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return true; +} + +bool Variable::setVal(Interpreter* interpreter, Val i, bool binding) { + assert(!aliased()); + assert(_domain.isVec()); + assert(_domain.size() % 2 == 0); + for (int j = 0; j < _domain.size(); j += 2) { + if (_domain[j] <= i && i <= _domain[j + 1]) { + this->binding(interpreter, binding); + alias(interpreter, i); + return true; + } + } + Val ndom(Vec::a(interpreter, interpreter->newIdent(), {})); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return false; +} + +bool Variable::intersectDom(Interpreter* interpreter, const std::vector& dom, bool binding) { + if (!isBounded()) { + Val ndom(Vec::a(interpreter, interpreter->newIdent(), dom)); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return true; + } + assert(!aliased()); + assert(_domain.isVec()); + assert(_domain.size() % 2 == 0); + VecSetRanges vsr1(_domain.toVec()); + StdVecSetRanges vsr2(&dom); + Ranges::Inter inter(vsr1, vsr2); + std::vector result; + for (; inter(); ++inter) { + result.emplace_back(inter.min()); + result.emplace_back(inter.max()); + } + Val ndom(Vec::a(interpreter, interpreter->newIdent(), result)); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding); + ndom.rmRef(interpreter); + return !result.empty(); +} + +bool Variable::intersectDom(Interpreter* interpreter, Val dom, bool binding) { + assert(!dom.isVar()); + if (dom.isInt()) { + return setVal(interpreter, dom); + } + // TODO: Allocation is not really necessary; + std::vector vdom(dom.size()); + for (int i = 0; i < dom.size(); ++i) { + vdom[i] = dom[i]; + } + return intersectDom(interpreter, vdom, binding); +} + +void Variable::domain(Interpreter* interpreter, const Val& newDomain, bool binding0) { + assert(!aliased()); + assert(!newDomain.isRCO() || newDomain.toRCO()->alive()); + assert(_domain.isVec()); + interpreter->trail.trail_domain(interpreter, this, _domain.toVec()); + SubscriptionEvent sev; + if (newDomain.isInt()) { + alias(interpreter, newDomain); + sev = SEV_VAL; + } else if (newDomain.size() == 2 && newDomain[0] == newDomain[1]) { + alias(interpreter, newDomain[0]); + sev = SEV_VAL; + } else { + Val nd = newDomain; + nd.addRef(interpreter); + _domain.rmRef(interpreter); + _domain = newDomain; + sev = SEV_DOM; + } + for (auto& s : _subscriptions) { + if (sev == SEV_VAL || s.second == SES_ANY) { + interpreter->schedule(s.first, sev); + } + } + binding(interpreter, binding0); +} +void Variable::domain(Interpreter* interpreter, const std::vector& newDomain, bool binding0) { + if (!isBounded()) { + Val ndom(Vec::a(interpreter, interpreter->newIdent(), newDomain)); + ndom.addRef(interpreter); + domain(interpreter, ndom, binding0); + ndom.rmRef(interpreter); + } else { + bool did_update = false; + if (newDomain.size() != _domain.size()) { + did_update = true; + } else { + for (int i = 0; i < newDomain.size(); i++) { + if (newDomain[i] != _domain[i]) { + did_update = true; + break; + } + } + } + if (did_update) { + Val ndv(Vec::a(interpreter, interpreter->newIdent(), newDomain)); + ndv.addRef(interpreter); + domain(interpreter, ndv, binding0); + ndv.rmRef(interpreter); + } + } +} + +void Variable::destroy(MiniZinc::Interpreter* interpreter) { + _model_ref_count = (1u << 31u) - 1u; + _domain.rmRef(interpreter); + _ann.rmRef(interpreter); + interpreter->trail.trail_ptr(_prev, &(_prev->_next)); + _prev->_next = _next; + interpreter->trail.trail_ptr(_next, &(_next->_prev)); + _next->_prev = _prev; + interpreter->trail.trail_ptr(this, &_next); + _next = this; + interpreter->trail.trail_ptr(this, &_prev); + _prev = this; + + for (auto c : _definitions) { + c->destroy(interpreter); + } + _model_ref_count = 0; +} + +void Variable::reconstruct(Interpreter* interpreter) { + /// TODO: what about subscriptions? + assert(_model_ref_count == 0); + _ann.addRef(interpreter); + _domain.addRef(interpreter); + for (auto c : _definitions) { + c->reconstruct(interpreter); + } + _model_ref_count = 0; +} + +void Variable::addDefinition(Interpreter* interpreter, Constraint* c) { + _definitions.push_back(c); + if (this != interpreter->root()) { + // Remove reference counts for this variable from each argument in c + for (int i = 0; i < c->size(); i++) { + if (c->arg(i).isVar()) { + if (c->arg(i).timestamp() == _timestamp) { + RefCountedObject::rmRef(interpreter, this); + } + } else if (c->arg(i).isVec()) { + Val content = c->arg(i).toVec()->raw_data(); + bool hasVar = false; + for (int j = 0; j < content.size(); j++) { + if (content[j].isVar() && content[j].timestamp() == _timestamp) { + hasVar = true; + break; + } + } + if (hasVar) { + if (!c->arg(i).unique() || !content.unique()) { + // make vectors unique so that we can safely decrement reference counts + std::vector vals(content.size()); + for (int i = 0; i < vals.size(); i++) { + vals[i] = content[i]; + } + Val v = Val(Vec::a(interpreter, interpreter->newIdent(), vals)); + if (c->arg(i).toVec()->hasIndexSet()) { + v = Val(Vec::a(interpreter, interpreter->newIdent(), + {v, c->arg(i).toVec()->index_set()})); + } + c->arg(interpreter, i, Val(v)); + } + for (int j = 0; j < content.size(); j++) { + if (content[j].timestamp() == _timestamp) { + RefCountedObject::rmRef(interpreter, this); + } + } + } + } + } + } + if (c->delayed()) { + interpreter->register_delayed(c, this); + } + interpreter->trail.trail_add_def(this, c); +} + +void Variable::alias(Interpreter* interpreter, Val v) { + assert(!_aliased); + assert(!v.isVar() || v.toVar() != this); + // Move defining constraints to current context + + for (Constraint* c : _definitions) { + for (int i = 0; i < c->size(); ++i) { + Val arg = c->arg(i); + if (arg.isVec()) { + _model_ref_count += arg.toVec()->count(Val(this)); + } else { + _model_ref_count += (arg == Val(this)); + } + } + interpreter->trail.trail_rm_def(this, c); + interpreter->pushConstraint(c); + } + _definitions.clear(); + + // Destroy old domain + _domain.rmRef(interpreter); + _ann.rmRef(interpreter); + _ann = 0; + + // Reset reference count for binding status + // TODO: this may be incorrect if we are aliasing a variable with + // TODO: binding domain to one with non-binding domain + binding(interpreter, false); + + // Transfer subscriptions to new value and schedule propagators + for (auto& s : _subscriptions) { + if (v.isVar()) { + v.toVar()->subscribe(s.first, s.second); + } + if (s.second == SES_VALUNIFY || s.second == SES_ANY) { + interpreter->schedule(s.first, SEV_UNIFY); + } + } + _subscriptions.clear(); + + // Set Alias + _aliased = true; + _domain = v; + v.addRef(interpreter); + interpreter->trail.trail_alias(interpreter, this); +} + +void Variable::unalias(Interpreter* interpreter, Val dom) { + /// TODO!!! + // auto ref_count = _ref_count; + // _pred = proc; + // _size = size; + // _args[0].destroy(interpreter); + // _args[0] = arg0; + // for (int i = 0; i < _size; ++i) { + // _args[i].construct(interpreter); + // } + // // TODO: Transfer back subscriptions moved on aliasing? + // interpreter->subscribe(this); + // _ann.construct(interpreter); + // _domain->addRef(interpreter); + // _ref_count = ref_count; +} + +void Variable::binding(Interpreter* interpreter, bool f) { + if (!_binding && f) { + addRef(interpreter); + } else if (_binding && !f) { + RefCountedObject::rmRef(interpreter, this); + } + _binding = f; +} + +void Variable::subscribe(Constraint* c, const SubscriptionEventSet& events) { + Variable* sub = this; + while (sub && sub->aliased()) { + if (sub->_domain.isVar()) { + sub = sub->_domain.toVar(); + } else { + sub = nullptr; + } + } + if (sub) { + sub->_subscriptions.insert(std::make_pair(c, events)); + } +} +void Variable::unsubscribe(Constraint* c) { + Variable* sub = this; + while (sub && sub->aliased()) { + if (sub->_domain.isVar()) { + sub = sub->_domain.toVar(); + } else { + sub = nullptr; + } + } + if (sub) { + sub->_subscriptions.erase(c); + } +} + +void Variable::dump(Variable* head, const std::vector& bs, std::ostream& os) { + Variable* d = head; + do { + d = d->next(); + if (d != head) { + if (d->timestamp() >= 0) { + os << d->timestamp() << "("; + } + os << d << "." << d->_model_ref_count; + if (d->timestamp() >= 0) { + os << ")"; + } + os << ":\t"; + if (d->aliased()) { + os << " alias " << d->alias().toString() << "\n"; + } else { + if (d->domain()) { + if (d->_binding) { + os << " binding"; + } + os << " domain: " << Val(d->domain()).toString(); + } + } + os << "\n"; + if (!d->_subscriptions.empty()) { + os << " subscriptions: "; + for (auto& s : d->_subscriptions) { + os << s.first << " "; + } + os << "\n"; + } + } + for (Constraint* c : d->_definitions) { + if (d != head) { + os << " "; + } + os << c << " " << bs[c->pred()].name << "("; + for (int i = 0; i < c->size(); i++) { + os << c->arg(i).toString(); + if (i < c->size() - 1) os << ", "; + } + os << ")\n"; + } + } while (d != head); +} + +} // namespace MiniZinc diff --git a/lib/interpreter/vector.cpp b/lib/interpreter/vector.cpp new file mode 100644 index 0000000..2b5e058 --- /dev/null +++ b/lib/interpreter/vector.cpp @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +bool Vec::isPar() const { + assert(alive()); + for (int i = 0; i < this->size(); ++i) { + Val v = this->operator[](i); + if (v.isVec() && (!v.toVec()->isPar())) { + return false; + } + if (v.isVar()) { + return false; + } + } + return true; +} + +} // namespace MiniZinc diff --git a/lib/json_parser.cpp b/lib/json_parser.cpp new file mode 100644 index 0000000..b7ab2ad --- /dev/null +++ b/lib/json_parser.cpp @@ -0,0 +1,520 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include +#include + +using namespace std; + +namespace MiniZinc { + +class JSONParser::Token { +public: + TokenT t; + +protected: + Token(TokenT t0) : t(t0) {} + +public: + Token(void) : t(T_EOF) {} + std::string s; + int i; + double d; + bool b; + Token(std::string s0) : t(T_STRING), s(s0) {} + Token(int i0) : t(T_INT), i(i0), d(i0) {} + Token(double d0) : t(T_FLOAT), d(d0) {} + Token(bool b0) : t(T_BOOL), i(b0), d(b0), b(b0) {} + static Token listOpen() { return Token(T_LIST_OPEN); } + static Token listClose() { return Token(T_LIST_CLOSE); } + static Token objOpen() { return Token(T_OBJ_OPEN); } + static Token objClose() { return Token(T_OBJ_CLOSE); } + static Token comma() { return Token(T_COMMA); } + static Token colon() { return Token(T_COLON); } + static Token eof() { return Token(T_EOF); } + static Token null() { return Token(T_NULL); } + string toString(void) { + switch (t) { + case T_LIST_OPEN: + return "["; + case T_LIST_CLOSE: + return "]"; + case T_OBJ_OPEN: + return "{"; + case T_OBJ_CLOSE: + return "}"; + case T_COMMA: + return ","; + case T_COLON: + return ":"; + case T_STRING: + return "\"" + s + "\""; + case T_INT: { + std::stringstream ss; + ss << i; + return ss.str(); + } + case T_FLOAT: { + std::stringstream ss; + ss << d; + return ss.str(); + } + case T_BOOL: + return b ? "true" : "false"; + case T_NULL: + return "null"; + case T_EOF: + return "eof"; + } + } +}; + +Location JSONParser::errLocation(void) const { + Location loc(filename, line, column, line, column); + return loc; +} + +JSONParser::Token JSONParser::readToken(istream& is) { + string result; + char buf[1]; + enum { S_NOTHING, S_STRING, S_STRING_ESCAPE, S_INT, S_FLOAT } state; + state = S_NOTHING; + while (is.good()) { + is.read(buf, sizeof(buf)); + column += sizeof(buf); + if (is.eof()) return Token::eof(); + if (!is.good()) throw JSONError(env, errLocation(), "tokenization failed"); + switch (state) { + case S_NOTHING: + switch (buf[0]) { + case '\n': + line++; + column = 0; + // fall through + case ' ': + case '\t': + case '\r': + break; + case '[': + return Token::listOpen(); + case ']': + return Token::listClose(); + case '{': + return Token::objOpen(); + case '}': + return Token::objClose(); + case ',': + return Token::comma(); + case ':': + return Token::colon(); + case '"': + result = ""; + state = S_STRING; + break; + case 't': { + char rest[3]; + is.read(rest, sizeof(rest)); + column += sizeof(rest); + if (!is.good() || std::strncmp(rest, "rue", 3) != 0) + throw JSONError(env, errLocation(), "unexpected token `" + string(rest) + "'"); + state = S_NOTHING; + return Token(true); + } break; + case 'f': { + char rest[4]; + is.read(rest, sizeof(rest)); + column += sizeof(rest); + if (!is.good() || std::strncmp(rest, "alse", 4) != 0) + throw JSONError(env, errLocation(), "unexpected token `" + string(rest) + "'"); + state = S_NOTHING; + return Token(false); + } break; + case 'n': { + char rest[3]; + is.read(rest, sizeof(rest)); + column += sizeof(rest); + if (!is.good() || std::strncmp(rest, "ull", 3) != 0) + throw JSONError(env, errLocation(), "unexpected token `" + string(rest) + "'"); + state = S_NOTHING; + return Token::null(); + } break; + default: + if ((buf[0] >= '0' && buf[0] <= '9') || (buf[0] == '-')) { + result = buf[0]; + state = S_INT; + } else { + throw JSONError(env, errLocation(), "unexpected token `" + string(1, buf[0]) + "'"); + } + break; + } + break; + case S_STRING_ESCAPE: + switch (buf[0]) { + case 'n': + result += "\n"; + break; + case 't': + result += "\t"; + break; + case '"': + result += "\""; + break; + case '\\': + result += "\\"; + break; + default: + result += "\\"; + result += buf[0]; + break; + } + state = S_STRING; + break; + case S_STRING: + if (buf[0] == '"') { + state = S_NOTHING; + return Token(result); + } + if (buf[0] == '\\') { + state = S_STRING_ESCAPE; + } else { + result += buf[0]; + } + break; + case S_INT: + if (buf[0] == '.') { + result += buf[0]; + state = S_FLOAT; + } else if (buf[0] >= '0' && buf[0] <= '9') { + result += buf[0]; + } else { + is.unget(); + std::istringstream iss(result); + int v; + iss >> v; + state = S_NOTHING; + return Token(v); + } + break; + case S_FLOAT: + if (buf[0] >= '0' && buf[0] <= '9') { + result += buf[0]; + } else { + is.unget(); + std::istringstream iss(result); + double v; + iss >> v; + state = S_NOTHING; + return Token(v); + } + break; + } + } + throw JSONError(env, errLocation(), "unexpected token `" + string(result) + "'"); +} + +void JSONParser::expectToken(istream& is, JSONParser::TokenT t) { + Token rt = readToken(is); + if (rt.t != t) { + throw JSONError(env, errLocation(), "unexpected token"); + } +} + +string JSONParser::expectString(istream& is) { + Token rt = readToken(is); + if (rt.t != T_STRING) { + throw JSONError(env, errLocation(), "unexpected token, expected string"); + } + return rt.s; +} + +JSONParser::Token JSONParser::parseEnumString(istream& is) { + Token next = readToken(is); + if (next.t != T_STRING) { + throw JSONError(env, errLocation(), "invalid enum object"); + } + if (next.s.empty()) { + throw JSONError(env, errLocation(), "invalid enum identifier"); + } + size_t nonIdChar = + next.s.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"); + size_t nonIdBegin = next.s.find_first_of("0123456789_"); + if (nonIdChar != std::string::npos || nonIdBegin == 0) { + next.s = "'" + next.s + "'"; + } + return next; +} + +Expression* JSONParser::parseObject(istream& is) { + // precondition: found T_OBJ_OPEN + Token objid = readToken(is); + if (objid.t != T_STRING) throw JSONError(env, errLocation(), "invalid object"); + expectToken(is, T_COLON); + if (objid.s == "set") { + expectToken(is, T_LIST_OPEN); + vector elems; + TokenT listT = T_COLON; // dummy marker + for (Token next = readToken(is); next.t != T_LIST_CLOSE; next = readToken(is)) { + switch (next.t) { + case T_COMMA: + break; + case T_INT: + if (listT == T_STRING || listT == T_OBJ_OPEN) + throw JSONError(env, errLocation(), "invalid set literal"); + if (listT != T_FLOAT) listT = T_INT; + elems.push_back(next); + break; + case T_FLOAT: + if (listT == T_STRING || listT == T_OBJ_OPEN) + throw JSONError(env, errLocation(), "invalid set literal"); + listT = T_FLOAT; + elems.push_back(next); + break; + case T_STRING: + if (listT != T_COLON && listT != T_STRING) + throw JSONError(env, errLocation(), "invalid set literal"); + listT = T_STRING; + elems.push_back(next); + break; + case T_BOOL: + if (listT == T_STRING || listT == T_OBJ_OPEN) + throw JSONError(env, errLocation(), "invalid set literal"); + if (listT == T_COLON) listT = T_BOOL; + elems.push_back(next); + break; + case T_OBJ_OPEN: { + if (listT != T_COLON && listT != T_OBJ_OPEN) + throw JSONError(env, errLocation(), "invalid set literal"); + listT = T_OBJ_OPEN; + Token enumid = readToken(is); + if (enumid.t != T_STRING || enumid.s != "e") + throw JSONError(env, errLocation(), "invalid enum object"); + expectToken(is, T_COLON); + Token next = parseEnumString(is); + expectToken(is, T_OBJ_CLOSE); + elems.push_back(next); + break; + } + default: + throw JSONError(env, errLocation(), "invalid set literal"); + } + } + expectToken(is, T_OBJ_CLOSE); + vector elems_e(elems.size()); + switch (listT) { + case T_COLON: + break; + case T_BOOL: + for (unsigned int i = 0; i < elems.size(); i++) { + elems_e[i] = new BoolLit(Location().introduce(), elems[i].b); + } + break; + case T_INT: + for (unsigned int i = 0; i < elems.size(); i++) { + elems_e[i] = IntLit::a(elems[i].i); + } + break; + case T_FLOAT: + for (unsigned int i = 0; i < elems.size(); i++) { + elems_e[i] = FloatLit::a(elems[i].d); + } + break; + case T_STRING: + for (unsigned int i = 0; i < elems.size(); i++) { + elems_e[i] = new StringLit(Location().introduce(), elems[i].s); + } + break; + case T_OBJ_OPEN: + for (unsigned int i = 0; i < elems.size(); i++) { + elems_e[i] = new Id(Location().introduce(), ASTString(elems[i].s), NULL); + } + break; + default: + break; + } + return new SetLit(Location().introduce(), elems_e); + } else if (objid.s == "e") { + Token next = parseEnumString(is); + expectToken(is, T_OBJ_CLOSE); + return new Id(Location().introduce(), ASTString(next.s), NULL); + } else { + throw JSONError(env, errLocation(), "invalid object"); + } +} + +ArrayLit* JSONParser::parseArray(std::istream& is) { + // precondition: opening parenthesis has been read + vector exps; + vector > dims; + dims.emplace_back(1, 0); + vector hadDim; + hadDim.push_back(false); + Token next; + for (;;) { + next = readToken(is); + if (next.t != T_LIST_OPEN) break; + dims.emplace_back(1, 0); + hadDim.push_back(false); + } + int curDim = static_cast(dims.size()) - 1; + for (;;) { + switch (next.t) { + case T_LIST_CLOSE: + hadDim[curDim] = true; + curDim--; + if (curDim < 0) { + goto list_done; + } else if (!hadDim[curDim]) { + dims[curDim].second++; + } + break; + case T_LIST_OPEN: + curDim++; + break; + case T_COMMA: + break; + case T_INT: + if (!hadDim[curDim]) { + dims[curDim].second++; + } + exps.push_back(IntLit::a(next.i)); + break; + case T_FLOAT: + if (!hadDim[curDim]) { + dims[curDim].second++; + } + exps.push_back(FloatLit::a(next.d)); + break; + case T_STRING: + if (!hadDim[curDim]) { + dims[curDim].second++; + } + exps.push_back(new StringLit(Location().introduce(), next.s)); + break; + case T_BOOL: + if (!hadDim[curDim]) { + dims[curDim].second++; + } + exps.push_back(new BoolLit(Location().introduce(), next.b)); + break; + case T_NULL: + if (!hadDim[curDim]) { + dims[curDim].second++; + } + exps.push_back(constants().absent); + break; + case T_OBJ_OPEN: + if (!hadDim[curDim]) { + dims[curDim].second++; + } + exps.push_back(parseObject(is)); + break; + default: + throw JSONError(env, errLocation(), "cannot parse JSON file"); + break; + } + next = readToken(is); + } +list_done: + unsigned int expectedSize = 1; + for (auto& d : dims) { + expectedSize *= d.second; + } + if (exps.size() != expectedSize) { + throw JSONError(env, errLocation(), "mismatch in array dimensions"); + /// TODO: check each individual sub-array + } + return new ArrayLit(Location().introduce(), exps, dims); +} + +Expression* JSONParser::parseExp(std::istream& is) { + Token next = readToken(is); + switch (next.t) { + case T_INT: + return IntLit::a(next.i); + break; + case T_FLOAT: + return FloatLit::a(next.d); + case T_STRING: + return new StringLit(Location().introduce(), next.s); + case T_BOOL: + return new BoolLit(Location().introduce(), next.b); + case T_NULL: + return constants().absent; + case T_OBJ_OPEN: + return parseObject(is); + case T_LIST_OPEN: + return parseArray(is); + default: + throw JSONError(env, errLocation(), "cannot parse JSON file"); + break; + } +} + +void JSONParser::parse(Model* m, std::istream& is) { + line = 0; + column = 0; + expectToken(is, T_OBJ_OPEN); + for (;;) { + string ident = expectString(is); + expectToken(is, T_COLON); + Expression* e = parseExp(is); + if (ident[0] != '_') { + AssignI* ai = new AssignI(Location().introduce(), ident, e); + m->addItem(ai); + } + Token next = readToken(is); + if (next.t == T_OBJ_CLOSE) break; + if (next.t != T_COMMA) throw JSONError(env, errLocation(), "cannot parse JSON file"); + } +} + +void JSONParser::parse(Model* m, const std::string& filename0) { + filename = filename0; + ifstream is; + is.open(filename, ios::in); + if (!is.good()) { + throw JSONError(env, Location().introduce(), "cannot open file " + filename); + } + parse(m, is); +} + +void JSONParser::parseFromString(Model* m, const std::string& data) { + istringstream iss(data); + line = 0; + column = 0; + parse(m, iss); +} + +namespace { +bool isJSON(std::istream& is) { + while (is.good()) { + char c = is.get(); + if (c == '{') return true; + if (c != ' ' && c != '\n' && c != '\t' && c != '\r') return false; + } + return false; +} +} // namespace + +bool JSONParser::stringIsJSON(const std::string& data) { + std::istringstream iss(data); + return isJSON(iss); +} + +bool JSONParser::fileIsJSON(const std::string& filename) { + ifstream is; + is.open(filename, ios::in); + return isJSON(is); +} + +} // namespace MiniZinc diff --git a/lib/lexer.lxx b/lib/lexer.lxx new file mode 100644 index 0000000..b5a7521 --- /dev/null +++ b/lib/lexer.lxx @@ -0,0 +1,412 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%option reentrant +%option bison-bridge bison-locations +%option noyywrap +%option stack + +%{ +#if defined __GNUC__ +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wdeprecated" +#elif defined _MSC_VER +#pragma warning(push, 1) +#endif + +namespace MiniZinc{ class ParserLocation; } +#define YYLTYPE MiniZinc::ParserLocation +#define YYLTYPE_IS_DECLARED 1 +#define YYLTYPE_IS_TRIVIAL 0 + +#include + +int utf8len(const char* s) { + int l=0; + for (int i=0; s[i] != '\0'; i++) + if ((s[i] & 0xc0) != 0x80) + l++; + return l; +} + +int yy_input_proc(char* buf, int size, yyscan_t yyscanner); +#define YY_INPUT(buf, result, max_size) \ + result = yy_input_proc(buf, max_size, yyscanner); + +#define YY_USER_ACTION \ + { MiniZinc::ParserState* parm = \ + static_cast(yyget_extra(yyscanner)); \ + yylloc->first_line(yylloc->last_line()); \ + yylloc->first_column(yylloc->last_column()+1); \ + if(parm->hadNewline) { \ + parm->hadNewline=false; \ + parm->lineStartPos += parm->nTokenNextStart; \ + parm->nTokenNextStart=1; \ + yylloc->last_line(yylloc->last_line()+1); \ + yylloc->first_line(yylloc->last_line()); \ + yylloc->first_column(1); \ + } \ + if(yytext[0] == '\n') { \ + parm->hadNewline=true; \ + parm->nTokenNextStart+=0; \ + } else { \ + parm->nTokenNextStart+=yyleng; \ + } \ + yylloc->last_column(yylloc->first_column()+utf8len(yytext)-1); \ + } + +bool hexstrtointval(const char* s, long long int& v) { + std::istringstream iss(s); + iss >> std::hex >> v; + return !iss.fail(); +} + +bool octstrtointval(const char* s, long long int& v) { + std::istringstream iss(s); + iss >> std::oct >> v; + return !iss.fail(); +} + +bool fast_strtointval(const char* s, long long int& v) { + MiniZinc::IntVal x = 0; + try { + for (; *s != '\0'; ++s) { + x = (x*10) + (*s - '0'); + } + } catch (MiniZinc::ArithmeticError&) { + return false; + } + v = x.toInt(); + return true; +} + +bool strtofloatval(const char* s, double& v) { + std::istringstream iss(s); + iss >> v; + return !iss.fail(); +} + +void clearBuffer(void* parm) { + MiniZinc::ParserState* pp = + static_cast(parm); + pp->stringBuffer = ""; +} + +void appendBufferString(void* parm, const char* s) { + MiniZinc::ParserState* pp = + static_cast(parm); + pp->stringBuffer += s; +} + +void appendBufferChar(void* parm, char s) { + MiniZinc::ParserState* pp = + static_cast(parm); + pp->stringBuffer += s; +} + +char* bufferData(void* parm) { + MiniZinc::ParserState* pp = + static_cast(parm); + return strdup(pp->stringBuffer.c_str()); +} + +%} + +%x string +%x string_quote +%x multilinecomment +%x doccomment +%x doccomment_file +%s bracket_exp +%s quoted_exp + +%% + +<*>\x0 { return MZN_INVALID_NULL; } + +\xa { } +[ \f\xd\t] { /* ignore whitespace */ } + +"/**" { yy_push_state(doccomment,yyscanner); clearBuffer(yyget_extra(yyscanner)); } +{ + "*/" { yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_DOC_COMMENT; } + [^*\xa]+ { appendBufferString(yyget_extra(yyscanner), yytext); } + "*" { appendBufferString(yyget_extra(yyscanner), yytext); } + \xa { appendBufferString(yyget_extra(yyscanner), yytext); } +} + +"/***" { yy_push_state(doccomment_file,yyscanner); clearBuffer(yyget_extra(yyscanner)); } +{ + "*/" { yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_DOC_FILE_COMMENT; } + [^*\xa]+ { appendBufferString(yyget_extra(yyscanner), yytext); } + "*" { appendBufferString(yyget_extra(yyscanner), yytext); } + \xa { appendBufferString(yyget_extra(yyscanner), yytext); } +} + +"/*" { yy_push_state(multilinecomment,yyscanner); } +{ + "*/" { yy_pop_state(yyscanner); } + [^*\xa]+ { } + "*" { } + \xa { } +} + + +"[" { return MZN_LEFT_BRACKET; } +"[|" { return MZN_LEFT_2D_BRACKET; } +"]" { return MZN_RIGHT_BRACKET; } +"|]" { return MZN_RIGHT_2D_BRACKET; } +%[^\xa]* { /* ignore comments */ } + +"true" { yylval->iValue = 1; return MZN_BOOL_LITERAL; } +"false" { yylval->iValue = 0; return MZN_BOOL_LITERAL; } + +0[xX]([0-9a-fA-F]*\.[0-9a-fA-F]+|[0-9a-fA-F]+\.)([pP][+-]?[0-9]+)|(0[xX][0-9a-fA-F]+[pP][+-]?[0-9]+) { + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } + +0[xX][0-9A-Fa-f]+ { + if (hexstrtointval(yytext+2, yylval->iValue)) + return MZN_INTEGER_LITERAL; + else + return MZN_INVALID_INTEGER_LITERAL; + } +0o[0-7]+ { + if (octstrtointval(yytext+2, yylval->iValue)) + return MZN_INTEGER_LITERAL; + else + return MZN_INVALID_INTEGER_LITERAL; + } +[0-9]+ { + if (fast_strtointval(yytext, yylval->iValue)) + return MZN_INTEGER_LITERAL; + else + return MZN_INVALID_INTEGER_LITERAL; + } + +[0-9]+\.[0-9]+ { + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } +[0-9]+\.[0-9]+[Ee][+-]?[0-9]+ { + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } +[0-9]+[Ee][+-]?[0-9]+ { + if (strtofloatval(yytext, yylval->dValue)) + return MZN_FLOAT_LITERAL; + else + return MZN_INVALID_FLOAT_LITERAL; + } +[:;|{},\[\]\.] { + return *yytext; } +\.\. { return MZN_DOTDOT; } +"'\.\.'" { return MZN_DOTDOT_QUOTED; } +:: { return MZN_COLONCOLON; } +_ { return MZN_UNDERSCORE; } +"ann" { return MZN_ANN; } +"annotation" { return MZN_ANNOTATION; } +"any" { return MZN_ANY; } +"array" { return MZN_ARRAY; } +"bool" { return MZN_BOOL; } +"case" { return MZN_CASE; } +"constraint" { return MZN_CONSTRAINT; } +"default" { return MZN_DEFAULT; } +"div" { return MZN_IDIV; } +"'div'" { return MZN_IDIV_QUOTED; } +"diff" { return MZN_DIFF; } +"'diff'" { return MZN_DIFF_QUOTED; } +"else" { return MZN_ELSE; } +"elseif" { return MZN_ELSEIF; } +"endif" { return MZN_ENDIF; } +"enum" { return MZN_ENUM; } +"float" { return MZN_FLOAT; } +"function" { return MZN_FUNCTION; } +"if" { return MZN_IF; } +"include" { return MZN_INCLUDE; } +"infinity" { return MZN_INFINITY; } +"intersect" { return MZN_INTERSECT; } +"'intersect'" { return MZN_INTERSECT_QUOTED; } +"in" { return MZN_IN; } +"'in'" { return MZN_IN_QUOTED; } +"int" { return MZN_INT; } +"let" { return MZN_LET; } +"list" { return MZN_LIST; } +"maximize" { yylval->bValue = false; return MZN_MAXIMIZE; } +"minimize" { yylval->bValue = true; return MZN_MINIMIZE; } +"mod" { return MZN_MOD; } +"'mod'" { return MZN_MOD_QUOTED; } +"not" { return MZN_NOT; } +"'not'" { return MZN_NOT_QUOTED; } +"of" { return MZN_OF; } +"output" { return MZN_OUTPUT; } +"opt" { return MZN_OPT; } +"par" { return MZN_PAR; } +"predicate" { return MZN_PREDICATE; } +"record" { return MZN_RECORD; } +"satisfy" { return MZN_SATISFY; } +"set" { return MZN_SET; } +"solve" { return MZN_SOLVE; } +"string" { return MZN_STRING; } +"subset" { return MZN_SUBSET; } +"'subset'" { return MZN_SUBSET_QUOTED; } +"superset" { return MZN_SUPERSET; } +"'superset'" { return MZN_SUPERSET_QUOTED; } +"symdiff" { return MZN_SYMDIFF; } +"'symdiff'" { return MZN_SYMDIFF_QUOTED; } +"test" { return MZN_TEST; } +"then" { return MZN_THEN; } +"tuple" { return MZN_TUPLE; } +"type" { return MZN_TYPE; } +"union" { return MZN_UNION; } +"'union'" { return MZN_UNION_QUOTED; } +"var" { return MZN_VAR; } +"variant_record" { return MZN_VARIANT_RECORD; } +"where" { return MZN_WHERE; } +"xor" { return MZN_XOR; } +"'xor'" { return MZN_XOR_QUOTED; } +"+" { return MZN_PLUS; } +"'+'" { return MZN_PLUS_QUOTED; } +"-" { return MZN_MINUS; } +"'-'" { return MZN_MINUS_QUOTED; } +"*" { return MZN_MULT; } +"'*'" { return MZN_MULT_QUOTED; } +"/" { return MZN_DIV; } +"'/'" { return MZN_DIV_QUOTED; } +"^" { return MZN_POW; } +"'^'" { return MZN_POW_QUOTED; } +"++" { return MZN_PLUSPLUS; } +"'++'" { return MZN_PLUSPLUS_QUOTED; } +"<>" { return MZN_ABSENT; } +"<" { return MZN_LE; } +"'<'" { return MZN_LE_QUOTED; } +"<=" { return MZN_LQ; } +"'<='" { return MZN_LQ_QUOTED; } +">" { return MZN_GR; } +"'>'" { return MZN_GR_QUOTED; } +">=" { return MZN_GQ; } +"'>='" { return MZN_GQ_QUOTED; } +"==" { return MZN_EQ; } +"'=='" { return MZN_EQ_QUOTED; } +"=" { return MZN_EQ; } +"'='" { return MZN_EQ_QUOTED; } +"!=" { return MZN_NQ; } +"'!='" { return MZN_NQ_QUOTED; } +"->" { return MZN_IMPL; } +"'->'" { return MZN_IMPL_QUOTED; } +"<-" { return MZN_RIMPL; } +"'<-'" { return MZN_RIMPL_QUOTED; } +"<->" { return MZN_EQUIV; } +"'<->'" { return MZN_EQUIV_QUOTED; } +"\\/" { return MZN_OR; } +"'\\/'" { return MZN_OR_QUOTED; } +"/\\" { return MZN_AND; } +"'/\\'" { return MZN_AND_QUOTED; } + +"~+" { return MZN_WEAK_PLUS; } +"~*" { return MZN_WEAK_MULT; } +"~=" { return MZN_WEAK_EQ; } +"~-" { return MZN_WEAK_MINUS; } + +"'~"[+*=-]"'" { + yylval->sValue = strdup(yytext+1); + yylval->sValue[strlen(yytext)-2] = 0; + return MZN_IDENTIFIER; } + +"_objective" { yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; } + +[A-Za-z][A-Za-z0-9_]* { + yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; } +"'"[^\\'\xa\xd\x0]*"'" { + yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; } +_[A-Za-z][A-Za-z0-9_]* { + MiniZinc::ParserState* parm = + static_cast(yyget_extra(yyscanner)); + if (parm->isFlatZinc) { + yylval->sValue = strdup(yytext); return MZN_IDENTIFIER; + } else { + return FLATZINC_IDENTIFIER; + } + } + +"\xE2\x88\x80" { yylval->sValue = strdup("forall"); return MZN_IDENTIFIER; } +"\xE2\x88\x83" { yylval->sValue = strdup("exists"); return MZN_IDENTIFIER; } +"\xE2\x88\x88" { return MZN_IN; } +"\xE2\x8A\x86" { return MZN_SUBSET; } +"\xE2\x8A\x87" { return MZN_SUPERSET; } +"\xE2\x88\x9E" { return MZN_INFINITY; } +"\xC2\xAC" { return MZN_NOT; } +"\xE2\x86\x90" { return MZN_RIMPL; } +"\xE2\x86\x92" { return MZN_IMPL; } +"\xE2\x86\x94" { return MZN_EQUIV; } +"\xE2\x88\xA7" { return MZN_AND; } +"\xE2\x88\xA8" { return MZN_OR; } +"\xE2\x89\xA0" { return MZN_NQ; } +"\xE2\x89\xA4" { return MZN_LQ; } +"\xE2\x89\xA5" { return MZN_GQ; } +"\xE2\x88\xAA" { return MZN_UNION; } +"\xE2\x88\xA9" { return MZN_INTERSECT; } + +$$[A-Za-z][A-Za-z0-9_]* { + yylval->sValue = strdup(yytext+1); return MZN_TI_ENUM_IDENTIFIER; } + +$[A-Za-z][A-Za-z0-9_]* { + yylval->sValue = strdup(yytext+1); return MZN_TI_IDENTIFIER; } + +"(" { yy_push_state(bracket_exp,yyscanner); return *yytext; } +")" { yy_pop_state(yyscanner); return *yytext; } +")" { yy_pop_state(yyscanner); yy_pop_state(yyscanner); yy_push_state(string_quote,yyscanner); + clearBuffer(yyget_extra(yyscanner)); } + +\" { yy_push_state(string,yyscanner); clearBuffer(yyget_extra(yyscanner)); } +[^\\"\xa\xd\x0]* { appendBufferString(yyget_extra(yyscanner), yytext); } +\\n { appendBufferChar(yyget_extra(yyscanner), '\n'); } +\\t { appendBufferChar(yyget_extra(yyscanner), '\t'); } +\\[\\'] { appendBufferChar(yyget_extra(yyscanner), yytext[1]); } +\\[\\"] { appendBufferChar(yyget_extra(yyscanner), yytext[1]); } +\\"(" { yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_push_state(quoted_exp,yyscanner); return MZN_STRING_QUOTE_START; } +\\"(" { yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_push_state(quoted_exp,yyscanner); return MZN_STRING_QUOTE_MID; } +\" { yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_STRING_LITERAL; } +\" { yylval->sValue = bufferData(yyget_extra(yyscanner)); + yy_pop_state(yyscanner); return MZN_STRING_QUOTE_END; } +. { return (unsigned char)yytext[0]; } +[\xa\xd\x0] { return MZN_END_OF_LINE_IN_STRING; } +<> { yy_pop_state(yyscanner); return MZN_UNTERMINATED_STRING; } + +`[A-Za-z][A-Za-z0-9_]*` { + yylval->sValue = strdup(yytext+1); + yylval->sValue[strlen(yytext)-2] = 0; + return MZN_QUOTED_IDENTIFIER; } + +. { return (unsigned char)yytext[0]; } + +%% +int yy_input_proc(char* buf, int size, yyscan_t yyscanner) { + MiniZinc::ParserState* parm = + static_cast(yyget_extra(yyscanner)); + return parm->fillBuffer(buf, size); + // work around warning that yyunput is unused + yyunput (0,buf,yyscanner); +} diff --git a/lib/miniz.c b/lib/miniz.c new file mode 100644 index 0000000..6da2762 --- /dev/null +++ b/lib/miniz.c @@ -0,0 +1,7081 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char* ptr, size_t buf_len) { + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that + * balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8* ptr, size_t buf_len) { + static const mz_uint32 s_crc_table[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D}; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8* pByte_buf = (const mz_uint8*)ptr; + + while (buf_len >= 4) { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void* p) { MZ_FREE(p); } + +void* miniz_def_alloc_func(void* opaque, size_t items, size_t size) { + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +void miniz_def_free_func(void* opaque, void* address) { + (void)opaque, (void)address; + MZ_FREE(address); +} +void* miniz_def_realloc_func(void* opaque, void* address, size_t items, size_t size) { + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char* mz_version(void) { return MZ_VERSION; } + +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) { + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, + MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, + int strategy) { + tdefl_compressor* pComp; + mz_uint comp_flags = + TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || + ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor*)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state*)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) { + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, + ((tdefl_compressor*)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) { + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, + pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) { + mz_status = MZ_STREAM_ERROR; + break; + } else if (defl_status == TDEFL_STATUS_DONE) { + mz_status = MZ_STREAM_END; + break; + } else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) { + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true + * upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, + 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char* pDest, mz_ulong* pDest_len, const unsigned char* pSource, + mz_ulong source_len, int level) { + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char* pDest, mz_ulong* pDest_len, const unsigned char* pSource, + mz_ulong source_len) { + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } + +typedef struct { + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) { + inflate_state* pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state*)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } + +int mz_inflate(mz_streamp pStream, int flush) { + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to + * hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, + pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END + : MZ_OK; + } + + for (;;) { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, + pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in + the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying + more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) { + /* The output buffer MUST be large to hold the remaining uncompressed data when + * flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte + * on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || + (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) { + if (!pStream) return MZ_STREAM_ERROR; + if (pStream->state) { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char* pDest, mz_ulong* pDest_len, const unsigned char* pSource, + mz_ulong source_len) { + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char* mz_error(int err) { + static struct { + int m_err; + const char* m_pDesc; + } s_error_descs[] = {{MZ_OK, ""}, + {MZ_STREAM_END, "stream end"}, + {MZ_NEED_DICT, "need dictionary"}, + {MZ_ERRNO, "file error"}, + {MZ_STREAM_ERROR, "stream error"}, + {MZ_DATA_ERROR, "data error"}, + {MZ_MEM_ERROR, "out of memory"}, + {MZ_BUF_ERROR, "buf error"}, + {MZ_VERSION_ERROR, "version error"}, + {MZ_PARAM_ERROR, "parameter error"}}; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, + 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, 273, 273, 273, 273, 273, 273, + 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, + 276, 276, 276, 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 285}; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0}; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17}; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, + 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct { + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, + tdefl_sym_freq* pSyms1) { + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { + const mz_uint32* pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq* t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, + * alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq* A, int n) { + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) { + if (leaf >= n || A[root].m_key < A[leaf].m_key) { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) { + while (root >= 0 && (int)A[root].m_key == dpth) { + used++; + root--; + } + while (avbl > used) { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int* pNum_codes, int code_list_len, + int max_code_size) { + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor* d, int table_num, int table_len, + int code_size_limit, int static_table) { + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) { + for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; + } else { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16* pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = \ + (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +static void tdefl_start_dynamic_block(tdefl_compressor* d) { + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, + packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], + packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } else { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } else if (++rle_repeat_count == 6) { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { + TDEFL_RLE_PREV_CODE_SIZE(); + } else { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor* d) { + mz_uint i; + mz_uint8* p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) *p++ = 8; + for (; i <= 255; ++i) *p++ = 9; + for (; i <= 279; ++i) *p++ = 7; + for (; i <= 287; ++i) *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, + 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, + 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor* d) { + mz_uint flags; + mz_uint8* pLZ_codes; + mz_uint8* pOutput_buf = d->m_pOutput_buf; + mz_uint8* pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { + if (flags == 1) flags = *pLZ_codes++ | 0x100; + + if (flags & 1) { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16*)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], + d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], + s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } else { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; + + *(mz_uint64*)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor* d) { + mz_uint flags; + mz_uint8* pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { + if (flags == 1) flags = *pLZ_codes++ | 0x100; + if (flags & 1) { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], + d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], + s_tdefl_len_extra[match_len]); + + if (match_dist < 512) { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } else { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } else { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS \ + */ + +static mz_bool tdefl_compress_block(tdefl_compressor* d, mz_bool static_block) { + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor* d, int flush) { + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8* pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && + (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8* pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && + ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) + ? ((mz_uint8*)d->m_pOut_buf + d->m_out_buf_ofs) + : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block( + d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw + * block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && + ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting + into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) { + if (flush == TDEFL_FINISH) { + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } else { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { + if (d->m_pPut_buf_func) { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8*)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } else if (pOutput_buf_start == d->m_output_buf) { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8*)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } else { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) { + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static inline mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) { + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16*)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor* d, mz_uint lookahead_pos, + mz_uint max_dist, mz_uint max_match_len, + mz_uint* pMatch_dist, mz_uint* pMatch_len) { + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, + probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), + s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) return; + for (;;) { + for (;;) { + if (--num_probes_left == 0) return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) break; + q = (const mz_uint16*)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) continue; + p = s; + probe_len = 32; + do { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (--probe_len > 0)); + if (!probe_len) { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } else if ((probe_len = ((mz_uint)(p - s) * 2) + + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor* d, mz_uint lookahead_pos, + mz_uint max_dist, mz_uint max_match_len, + mz_uint* pMatch_dist, mz_uint* pMatch_len) { + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, + probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) return; + for (;;) { + for (;;) { + if (--num_probes_left == 0) return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) break; + if (probe_len > match_len) { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor* d) { + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. + * Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, + dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, + num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = + (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, + MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; + + while (lookahead_size >= 4) { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8* pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32*)pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & + TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && + ((*(const mz_uint32*)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == + first_trigram)) { + const mz_uint16* p = (const mz_uint16*)pCur_dict; + const mz_uint16* q = (const mz_uint16*)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16*)pCur_dict) * 2) + + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q); + if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || + ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } else { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && + (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16*)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } else { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor* d, mz_uint8 lit) { + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor* d, mz_uint match_len, + mz_uint match_dist) { + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && + (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor* d) { + const mz_uint8* pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, + ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ + d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = + (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8* pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } else { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = + ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ + (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & + (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) { + if (d->m_dict[cur_pos + cur_match_len] != c) break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } else { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, + &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || + (cur_pos == cur_match_dist) || + ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) { + if (cur_match_len > d->m_saved_match_len) { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } else { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } else { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } else { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && + (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || + (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor* d) { + if (d->m_pIn_buf_size) { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8*)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8*)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor* d, const void* pIn_buf, size_t* pIn_buf_size, + void* pOut_buf, size_t* pOut_buf_size, tdefl_flush flush) { + if (!d) { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8*)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || + (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || + (pIn_buf_size && *pIn_buf_size && !pIn_buf) || + (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { + if (pIn_buf_size) *pIn_buf_size = 0; + if (pOut_buf_size) *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == + 0)) { + if (!tdefl_compress_fast(d)) return d->m_prev_return_status; + } else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8*)pIn_buf, + d->m_pSrc - (const mz_uint8*)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { + if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor* d, const void* pIn_buf, size_t in_buf_size, + tdefl_flush flush) { + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor* d, tdefl_put_buf_func_ptr pPut_buf_func, + void* pPut_buf_user, int flags) { + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = + d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = + d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor* d) { return d->m_prev_return_status; } + +mz_uint32 tdefl_get_adler32(tdefl_compressor* d) { return d->m_adler32; } + +mz_bool tdefl_compress_mem_to_output(const void* pBuf, size_t buf_len, + tdefl_put_buf_func_ptr pPut_buf_func, void* pPut_buf_user, + int flags) { + tdefl_compressor* pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; + pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = + succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct { + size_t m_size, m_capacity; + mz_uint8* m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void* pBuf, int len, void* pUser) { + tdefl_output_buffer* p = (tdefl_output_buffer*)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) { + size_t new_capacity = p->m_capacity; + mz_uint8* pNew_buf; + if (!p->m_expandable) return MZ_FALSE; + do { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void* tdefl_compress_mem_to_heap(const void* pSrc_buf, size_t src_buf_len, size_t* pOut_len, + int flags) { + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, + flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void* pOut_buf, size_t out_buf_len, const void* pSrc_buf, + size_t src_buf_len, int flags) { + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) return 0; + out_buf.m_pBuf = (mz_uint8*)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, + flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = {0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more + * compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | + ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer \ + (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: + https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function + pass pngcheck. */ +void* tdefl_write_image_to_png_file_in_memory_ex(const void* pImage, int w, int h, int num_chans, + size_t* pLen_out, mz_uint level, mz_bool flip) { + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = {0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; + tdefl_compressor* pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, + s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, + TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; + mz_uint8 pnghdr[41] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, + 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x44, 0x41, 0x54}; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) ((mz_uint8*)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, + &out_buf)) { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void* tdefl_write_image_to_png_file_in_memory(const void* pImage, int w, int h, int num_chans, + size_t* pLen_out) { + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on + * MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor* tdefl_compressor_alloc() { + return (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor* pComp) { MZ_FREE(pComp); } + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) + */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do { \ + for (;;) { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do { \ + while (pIn_buf_cur >= pIn_buf_end) { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) \ + ? TINFL_STATUS_NEEDS_MORE_INPUT \ + : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do { \ + if (num_bits < (mz_uint)(n)) { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do { \ + if (num_bits < (mz_uint)(n)) { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input + * buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code + * (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it + * reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would + * initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read + * another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate + * (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we + * also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this + * code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | \ + (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor* r, const mz_uint8* pIn_buf_next, + size_t* pIn_buf_size, mz_uint8* pOut_buf_start, + mz_uint8* pOut_buf_next, size_t* pOut_buf_size, + const mz_uint32 decomp_flags) { + static const int s_length_base[31] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, + 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, + 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const int s_length_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0}; + static const int s_dist_base[32] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; + static const int s_dist_extra[32] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, + 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + static const mz_uint8 s_length_dezigzag[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, + 11, 4, 12, 3, 13, 2, 14, 1, 15}; + static const int s_min_table_sizes[3] = {257, 1, 4}; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) + ? (size_t)-1 + : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, + dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to + * hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || + ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || + ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != + (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + ? TINFL_STATUS_NEEDS_MORE_INPUT + : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN( + MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), + counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } else if (r->m_type == 3) { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } else { + if (r->m_type == 1) { + mz_uint8* p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) *p++ = 8; + for (; i <= 255; ++i) *p++ = 9; + for (; i <= 279; ++i) *p++ = 7; + for (; i <= 287; ++i) *p++ = 8; + } else { + for (counter = 0; counter < 3; counter++) { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) { + int tree_next, tree_cur; + tinfl_huff_table* pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, + s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], + r->m_table_sizes[1]); + } + } + for (;;) { + mz_uint8* pSrc; + for (;;) { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) break; + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } else { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else { + code_len = TINFL_FAST_LOOKUP_BITS; + do { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else { + code_len = TINFL_FAST_LOOKUP_BITS; + do { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && + (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { + while (counter--) { + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = + pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) { + const mz_uint8* pSrc_end = pSrc + (counter & ~7); + do { + ((mz_uint32*)pOut_buf_cur)[0] = ((const mz_uint32*)pSrc)[0]; + ((mz_uint32*)pOut_buf_cur)[1] = ((const mz_uint32*)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) { + if (counter) { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on + * gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte + * alignment part, and the Adler32 check shouldn't ever need to worry about reading from the + * bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib + streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { + for (counter = 0; counter < 4; ++counter) { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate + * streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make + * forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && + (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && + (status >= 0)) { + const mz_uint8* ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && + (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void* tinfl_decompress_mem_to_heap(const void* pSrc_buf, size_t src_buf_len, size_t* pOut_len, + int flags) { + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, + new_out_buf_capacity; + tinfl_status status = tinfl_decompress( + &decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, + pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void* pOut_buf, size_t out_buf_len, const void* pSrc_buf, + size_t src_buf_len, int flags) { + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress( + &decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, + &out_buf_len, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void* pIn_buf, size_t* pIn_buf_size, + tinfl_put_buf_func_ptr pPut_buf_func, void* pPut_buf_user, + int flags) { + int result = 0; + tinfl_decompressor decomp; + mz_uint8* pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress( + &decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, + &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +tinfl_decompressor* tinfl_decompressor_alloc() { + tinfl_decompressor* pDecomp = (tinfl_decompressor*)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor* pDecomp) { MZ_FREE(pDecomp); } + +#ifdef __cplusplus +} +#endif +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void* +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE* mz_fopen(const char* pFilename, const char* pMode) { + FILE* pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE* mz_freopen(const char* pPath, const char* pMode, FILE* pStream) { + FILE* pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__GNUC__) && _LARGEFILE64_SOURCE +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message( \ + "Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform + * endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum { + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct { + void* m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag { + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed + * to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE* m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void* m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) \ + (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array* pArray, mz_uint index) { + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ + ((element_type*)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ + ((element_type*)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array* pArray, mz_uint32 element_size) { + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive* pZip, mz_zip_array* pArray) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive* pZip, mz_zip_array* pArray, + size_t min_new_capacity, mz_uint growing) { + void* pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; + if (growing) { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, + new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive* pZip, mz_zip_array* pArray, + size_t new_capacity, mz_uint growing) { + if (new_capacity > pArray->m_capacity) { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive* pZip, mz_zip_array* pArray, + size_t new_size, mz_uint growing) { + if (new_size > pArray->m_capacity) { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive* pZip, mz_zip_array* pArray, + size_t n) { + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive* pZip, mz_zip_array* pArray, + const void* pElements, size_t n) { + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; + memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, + n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16* pDOS_time, mz_uint16* pDOS_date) { +#ifdef _MSC_VER + struct tm tm_struct; + struct tm* tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm* tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = + (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char* pFilename, MZ_TIME_T* pTime) { + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) + * unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char* pFilename, MZ_TIME_T access_time, + MZ_TIME_T modified_time) { + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive* pZip, mz_zip_error err_num) { + if (pZip) pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive* pZip, mz_uint flags) { + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state*)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array* pCentral_dir_array, + const mz_zip_array* pCentral_dir_offsets, + mz_uint l_index, mz_uint r_index) { + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), + *pE; + const mz_uint8* pR = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), + r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by + * mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive* pZip) { + mz_zip_internal_state* pState = pZip->m_pState; + const mz_zip_array* pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array* pCentral_dir = &pState->m_central_dir; + mz_uint32* pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) { + mz_uint64 child, root = start; + for (;;) { + if ((child = (root << 1U) + 1U) >= size) break; + child += (((child + 1U) < size) && + (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], + pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], + pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) break; + start--; + } + + end = size - 1; + while (end > 0) { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) { + if ((child = (root << 1U) + 1U) >= end) break; + child += (((child + 1U) < end) && + mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], + pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], + pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive* pZip, mz_uint32 record_sig, + mz_uint32 record_size, mz_int64* pOfs) { + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8* pBuf = (mz_uint8*)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) break; + } + } + + if (i >= 0) { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive* pZip, mz_uint flags) { + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8* p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8* pBuf = (mz_uint8*)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8* pZip64_locator = (mz_uint8*)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8* pZip64_end_of_central_dir = (mz_uint8*)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file + * to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= + (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, + pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { + zip64_end_of_central_dir_ofs = + MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > + (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, + pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) { + mz_uint32 zip64_total_num_of_disks = + MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of + * files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold + * the unsorted central dir file record offsets, and possibly another to hold the sorted + * indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, + MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, + pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != + cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on + * each record */ + p = (const mz_uint8*)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || + (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = + (mz_uint32)(p - (const mz_uint8*)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) { + const mz_uint8* pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + + do { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended + * information field so mark it as zip64 anyway (this can occur with infozip's zip + * util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || + (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive* pZip) { + if (pZip) MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive* pZip, mz_bool set_last_error) { + mz_bool status = MZ_TRUE; + + if (!pZip) return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) { + if (set_last_error) pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) { + mz_zip_internal_state* pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { + if (MZ_FCLOSE(pState->m_pFile) == EOF) { + if (set_last_error) pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive* pZip) { + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive* pZip, mz_uint64 size, mz_uint flags) { + if ((!pZip) || (!pZip->m_pRead)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) { + mz_zip_archive* pZip = (mz_zip_archive*)pOpaque; + size_t s = + (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8*)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive* pZip, const void* pMem, size_t size, mz_uint flags) { + if (!pMem) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void*)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void* pOpaque, mz_uint64 file_ofs, void* pBuf, size_t n) { + mz_zip_archive* pZip = (mz_zip_archive*)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || + (((cur_ofs != (mz_int64)file_ofs)) && + (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive* pZip, const char* pFilename, mz_uint32 flags) { + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive* pZip, const char* pFilename, mz_uint flags, + mz_uint64 file_start_ofs, mz_uint64 archive_size) { + mz_uint64 file_size; + MZ_FILE* pFile; + + if ((!pZip) || (!pFilename) || + ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive* pZip, MZ_FILE* pFile, mz_uint64 archive_size, + mz_uint flags) { + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8* mz_zip_get_cdh(mz_zip_archive* pZip, mz_uint file_index) { + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) return NULL; + return &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive* pZip, mz_uint file_index) { + mz_uint m_bit_flag; + const mz_uint8* p = mz_zip_get_cdh(pZip, file_index); + if (!p) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive* pZip, mz_uint file_index) { + mz_uint bit_flag; + mz_uint method; + + const mz_uint8* p = mz_zip_get_cdh(pZip, file_index); + if (!p) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive* pZip, mz_uint file_index) { + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8* p = mz_zip_get_cdh(pZip, file_index); + if (!p) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't + * correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check + * for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive* pZip, mz_uint file_index, + const mz_uint8* pCentral_dir_header, + mz_zip_archive_file_stat* pStat, + mz_bool* pFound_zip64_extra_data) { + mz_uint n; + const mz_uint8* p = pCentral_dir_header; + + if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), + MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, + p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), + n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge + * files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == + MZ_UINT32_MAX) { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) { + const mz_uint8* pExtra_data = + p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + const mz_uint8* pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char* pA, const char* pB, mz_uint len, + mz_uint flags) { + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array* pCentral_dir_array, + const mz_zip_array* pCentral_dir_offsets, + mz_uint l_index, const char* pR, mz_uint r_len) { + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), + *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive* pZip, const char* pFilename, + mz_uint32* pIndex) { + mz_zip_internal_state* pState = pZip->m_pState; + const mz_zip_array* pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array* pCentral_dir = &pState->m_central_dir; + mz_uint32* pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) *pIndex = 0; + + if (size) { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the + * loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, + filename_len); + if (!comp) { + if (pIndex) *pIndex = file_index; + return MZ_TRUE; + } else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive* pZip, const char* pName, const char* pComment, + mz_uint flags) { + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive* pZip, const char* pName, const char* pComment, + mz_uint flags, mz_uint32* pIndex) { + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && + (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) { + const mz_uint8* pHeader = &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char* pFilename = (const char*)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) continue; + if (comment_len) { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), + file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char* pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || + (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { + int ofs = filename_len - 1; + do { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && + (mz_zip_string_equal(pName, pFilename, filename_len, flags))) { + if (pIndex) *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive* pZip, mz_uint file_index, void* pBuf, + size_t buf_size, mz_uint flags, void* pUser_read_buf, + size_t user_read_buf_size) { + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, + read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void* pRead_buf; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8* pLocal_header = (mz_uint8*)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || + ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && + (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = + (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, (size_t)file_stat.m_uncomp_size) != + file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8*)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } else if (pUser_read_buf) { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) return MZ_FALSE; + pRead_buf = (mz_uint8*)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } else { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do { + /* The size_t cast here should be OK because we've verified that the output buffer is >= + * file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != + read_buf_avail) { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8*)pRead_buf + read_buf_ofs, &in_buf_size, + (mz_uint8*)pBuf, (mz_uint8*)pBuf + out_buf_ofs, &out_buf_size, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | + (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, (size_t)file_stat.m_uncomp_size) != + file_stat.m_crc32) { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive* pZip, const char* pFilename, + void* pBuf, size_t buf_size, mz_uint flags, + void* pUser_read_buf, + size_t user_read_buf_size) { + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, + pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive* pZip, mz_uint file_index, void* pBuf, + size_t buf_size, mz_uint flags) { + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive* pZip, const char* pFilename, void* pBuf, + size_t buf_size, mz_uint flags) { + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, + 0); +} + +void* mz_zip_reader_extract_to_heap(mz_zip_archive* pZip, mz_uint file_index, size_t* pSize, + mz_uint flags) { + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8* p = mz_zip_get_cdh(pZip, file_index); + void* pBuf; + + if (pSize) *pSize = 0; + + if (!p) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) *pSize = (size_t)alloc_size; + return pBuf; +} + +void* mz_zip_reader_extract_file_to_heap(mz_zip_archive* pZip, const char* pFilename, size_t* pSize, + mz_uint flags) { + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) { + if (pSize) *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive* pZip, mz_uint file_index, + mz_file_write_func pCallback, void* pOpaque, + mz_uint flags) { + int status = TINFL_STATUS_DONE; + mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, + cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void* pRead_buf = NULL; + void* pWrite_buf = NULL; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8* pLocal_header = (mz_uint8*)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && + (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 + * stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) { + pRead_buf = (mz_uint8*)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } else { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != + file_stat.m_comp_size) { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8*)pRead_buf, + (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } else { + while (comp_remaining) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != + read_buf_avail) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { + file_crc32 = + (mz_uint32)mz_crc32(file_crc32, (const mz_uint8*)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } else { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } else { + do { + mz_uint8* pWrite_buf_cur = (mz_uint8*)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, + out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != + read_buf_avail) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8*)pRead_buf + read_buf_ofs, + &in_buf_size, (mz_uint8*)pWrite_buf, pWrite_buf_cur, + &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || + (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive* pZip, const char* pFilename, + mz_file_write_func pCallback, void* pOpaque, + mz_uint flags) { + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive* pZip, + mz_uint file_index, + mz_uint flags) { + mz_zip_reader_extract_iter_state* pState; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8* pLocal_header = (mz_uint8*)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && + (pState->file_stat.m_method != MZ_DEFLATED)) { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) { + pState->pRead_buf = (mz_uint8*)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } else { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = + pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } else { + /* Decompression not required - we will be reading directly into user buffer, no temp buf + * required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) { + /* Decompression required, init decompressor */ + tinfl_init(&pState->inflator); + + /* Allocate write buffer */ + if (NULL == + (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive* pZip, + const char* pFilename, + mz_uint flags) { + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, + size_t buf_size) { + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = MZ_MIN(buf_size, pState->comp_remaining); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) { + /* Copy data to caller's buffer */ + memcpy(pvBuf, pState->pRead_buf, copied_to_caller); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } else { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, + copied_to_caller) != copied_to_caller) { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = + (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8*)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } else { + do { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8* pWrite_buf_cur = + (mz_uint8*)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, + out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, + pState->pRead_buf, + (size_t)pState->read_buf_avail) != pState->read_buf_avail) { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress( + &pState->inflator, (const mz_uint8*)pState->pRead_buf + pState->read_buf_ofs, + &in_buf_size, (mz_uint8*)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, + pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN((buf_size - copied_to_caller), pState->out_blk_remain); + + /* Copy data to caller's buffer */ + memcpy((uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ((copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || + (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT))); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) { + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void* pOpaque, mz_uint64 ofs, const void* pBuf, size_t n) { + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive* pZip, mz_uint file_index, + const char* pDst_filename, mz_uint flags) { + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE* pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = + mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) { + if (status) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive* pZip, const char* pArchive_filename, + const char* pDst_filename, mz_uint flags) { + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive* pZip, mz_uint file_index, MZ_FILE* pFile, + mz_uint flags) { + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, + flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive* pZip, const char* pArchive_filename, + MZ_FILE* pFile, mz_uint flags) { + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, + size_t n) { + mz_uint32* p = (mz_uint32*)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8*)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive* pZip, mz_uint file_index, mz_uint flags) { + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state* pState; + const mz_uint8* pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8* pLocal_header = (mz_uint8*)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, + &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, + MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (local_header_filename_len) { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, + file_data_array.m_p, + local_header_filename_len) != local_header_filename_len) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and + * forward slashes in the central dir. Do we care about this? For now, this case will fail + * validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && + ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8* pExtra_data = (const mz_uint8*)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, + local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, + file_data_array.m_p, local_header_extra_len) != local_header_extra_len) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + const mz_uint8* pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! + * (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and + * bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8* pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = + ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, + local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + + local_header_extra_len + file_stat.m_comp_size, + descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != + (sizeof(mz_uint32) * num_descriptor_uint32s)) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } else { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || + (uncomp_size != file_stat.m_uncomp_size)) { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } else { + if ((local_header_crc32 != file_stat.m_crc32) || + (local_header_comp_size != file_stat.m_comp_size) || + (local_header_uncomp_size != file_stat.m_uncomp_size)) { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, + &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive* pZip, mz_uint flags) { + mz_zip_internal_state* pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } else { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check + * for when writing - that's up to the user) */ + if (found_index != i) return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void* pMem, size_t size, mz_uint flags, + mz_zip_error* pErr) { + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) { + if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) { + if (pErr) *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) { + if (!actual_err) actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char* pFilename, mz_uint flags, mz_zip_error* pErr) { + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) { + if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) { + if (pErr) *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) { + if (!actual_err) actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8* p, mz_uint16 v) { + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8* p, mz_uint32 v) { + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8* p, mz_uint64 v) { + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8*)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8*)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8*)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, + size_t n) { + mz_zip_archive* pZip = (mz_zip_archive*)pOpaque; + mz_zip_internal_state* pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) { + void* pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) new_capacity *= 2; + + if (NULL == + (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8*)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive* pZip, mz_bool set_last_error) { + mz_zip_internal_state* pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || + ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) { + if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { + if (MZ_FCLOSE(pState->m_pFile) == EOF) { + if (set_last_error) mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive* pZip, mz_uint64 existing_size, mz_uint flags) { + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) { + if (!pZip->m_pRead) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state*)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive* pZip, mz_uint64 existing_size) { + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive* pZip, size_t size_to_reserve_at_beginning, + size_t initial_allocation_size, mz_uint flags) { + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != + (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { + if (NULL == (pZip->m_pState->m_pMem = + pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive* pZip, size_t size_to_reserve_at_beginning, + size_t initial_allocation_size) { + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void* pOpaque, mz_uint64 file_ofs, const void* pBuf, + size_t n) { + mz_zip_archive* pZip = (mz_zip_archive*)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || + (((cur_ofs != (mz_int64)file_ofs)) && + (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive* pZip, const char* pFilename, + mz_uint64 size_to_reserve_at_beginning) { + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive* pZip, const char* pFilename, + mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { + MZ_FILE* pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) return MZ_FALSE; + + if (NULL == + (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive* pZip, MZ_FILE* pFile, mz_uint flags) { + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive* pZip, const char* pFilename, + mz_uint flags) { + mz_zip_internal_state* pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than + * it's worth. (What about the existing 32-bit data descriptors that could follow the compressed + * data?) */ + if (!pZip->m_pState->m_zip64) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) { + if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } else { + if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > + MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { + if (!pFilename) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen + * as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close + * it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } else if (pState->m_pMem) { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the + * realloc callback. */ + if (pZip->m_pIO_opaque != pZip) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a + write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing + * central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file + * locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive* pZip, const char* pFilename) { + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive* pZip, const char* pArchive_name, const void* pBuf, + size_t buf_size, mz_uint level_and_flags) { + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, + 0); +} + +typedef struct { + mz_zip_archive* m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void* pUser) { + mz_zip_writer_add_state* pState = (mz_zip_writer_add_state*)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, + pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8* pBuf, mz_uint64* pUncomp_size, + mz_uint64* pComp_size, + mz_uint64* pLocal_header_ofs) { + mz_uint8* pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive* pZip, mz_uint8* pDst, + mz_uint16 filename_size, mz_uint16 extra_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, + mz_uint32 uncomp_crc32, mz_uint16 method, + mz_uint16 bit_flags, mz_uint16 dos_time, + mz_uint16 dos_date) { + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header( + mz_zip_archive* pZip, mz_uint8* pDst, mz_uint16 filename_size, mz_uint16 extra_size, + mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir( + mz_zip_archive* pZip, const char* pFilename, mz_uint16 filename_size, const void* pExtra, + mz_uint16 extra_size, const void* pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, + mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, + mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char* user_extra_data, mz_uint user_extra_data_len) { + mz_zip_internal_state* pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) { + if (local_header_ofs > 0xFFFFFFFF) return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header( + pZip, central_dir_header, filename_size, extra_size + user_extra_data_len, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, + local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, + user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char* pArchive_name) { + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, + * cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') return MZ_FALSE; + + while (*pArchive_name) { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; + + pArchive_name++; + } + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive* pZip) { + mz_uint32 n; + if (!pZip->m_file_offset_alignment) return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive* pZip, mz_uint64 cur_file_ofs, + mz_uint32 n) { + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive* pZip, const char* pArchive_name, const void* pBuf, + size_t buf_size, const void* pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, + mz_uint32 uncomp_crc32) { + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, + level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, + NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive* pZip, const char* pArchive_name, + const void* pBuf, size_t buf_size, const void* pComment, + mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, + MZ_TIME_T* last_modified, const char* user_extra_data, + mz_uint user_extra_data_len, + const char* user_extra_data_central, + mz_uint user_extra_data_central_len) { + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, + cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor* pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state* pState; + mz_uint8* pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || + ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || + (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) { + if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } else { + if (pZip->m_total_files == MZ_UINT16_MAX) { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } else { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + + user_extra_data_len + pState->m_central_dir.m_size + + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file + * remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room( + pZip, &pState->m_central_dir, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || + (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) { + if (NULL == (pComp = (tdefl_compressor*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data( + extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, + extra_size + user_extra_data_len, 0, 0, 0, method, + bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != + extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } else { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, + user_extra_data_len, 0, 0, 0, method, bit_flags, + dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, + user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } else if (buf_size) { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, + tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) { + if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } else { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, + local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) { + extra_size = mz_zip_writer_create_zip64_extra_data( + extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, + pExtra_data, extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, bit_flags, + dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_cfile(mz_zip_archive* pZip, const char* pArchive_name, MZ_FILE* pSrc_file, + mz_uint64 size_to_add, const MZ_TIME_T* pFile_time, + const void* pComment, mz_uint16 comment_size, + mz_uint level_and_flags, const char* user_extra_data, + mz_uint user_extra_data_len, const char* user_extra_data_central, + mz_uint user_extra_data_central_len) { + mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, + uncomp_size = size_to_add, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8* pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state* pState; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || + (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) { + if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } else { + if (pZip->m_total_files == MZ_UINT16_MAX) { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + + user_extra_data_len + pState->m_central_dir.m_size + + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + + user_extra_data_central_len) > 0xFFFFFFFF) { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (uncomp_size <= 3) level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (uncomp_size && level) { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data( + extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, + extra_size + user_extra_data_len, 0, 0, 0, method, + gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != + extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } else { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, + user_extra_data_len, 0, 0, 0, method, gen_flags, + dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, + user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (uncomp_size) { + mz_uint64 uncomp_remaining = uncomp_size; + void* pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) { + while (uncomp_remaining) { + mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || + (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8*)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } else { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor* pComp = + (tdefl_compressor*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, + tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8*)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, + uncomp_remaining ? flush : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) { + result = MZ_TRUE; + break; + } else if (status != TDEFL_STATUS_OKAY) { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) { + if (comp_size > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } else { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, + local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) { + extra_size = mz_zip_writer_create_zip64_extra_data( + extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, + pExtra_data, extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, + dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive* pZip, const char* pArchive_name, + const char* pSrc_filename, const void* pComment, + mz_uint16 comment_size, mz_uint level_and_flags) { + MZ_FILE* pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T* pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, + pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block( + mz_zip_array* pNew_ext, mz_zip_archive* pZip, const mz_uint8* pExt, uint32_t ext_len, + mz_uint64* pComp_size, mz_uint64* pUncomp_size, mz_uint64* pLocal_header_ofs, + mz_uint32* pDisk_start) { + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { + mz_uint8 new_ext_block[64]; + mz_uint8* pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), + (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8* pExtra_data = pExt; + + do { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive* pZip, mz_zip_archive* pSource_zip, + mz_uint src_file_index) { + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8* pLocal_header = (mz_uint8*)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state* pState; + void* pBuf; + const mz_uint8* pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || + (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is + * possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case + * we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) { + if (pZip->m_total_files == MZ_UINT16_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } else { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, + NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = + local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && + ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { + mz_zip_array file_data_array; + const mz_uint8* pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, + src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + local_header_filename_size, + file_data_array.m_p, + local_header_extra_len) != local_header_extra_len) { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8*)file_data_array.m_p; + + do { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + const mz_uint8* pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64( + pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) { + /* Try to detect if the new archive will most likely wind up too big and bail early + * (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a + * fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = + cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + pState->m_central_dir.m_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy + * it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to + * handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, + src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, + (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } else { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, + sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint32* pSrc_descriptor = + (const mz_uint32*)((const mz_uint8*)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + + mz_write_le32((mz_uint8*)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8*)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8*)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8*)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } else { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) { + /* This is the painful part: We need to write a new central dir header + ext block with updated + * zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8* pSrc_ext = + pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block( + &new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, + &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, + pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, + src_filename_len)) { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, + new_ext_block.m_size)) { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back( + pZip, &pState->m_central_dir, + pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, + src_comment_len)) { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } else { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, + pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, + src_central_dir_following_data_size)) { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive* pZip) { + mz_zip_internal_state* pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) { + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } else { + if ((pZip->m_total_files > MZ_UINT16_MAX) || + ((pZip->m_archive_size + pState->m_central_dir.m_size + + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, + (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, + MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, + MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive* pZip, void** ppBuf, size_t* pSize) { + if ((!ppBuf) || (!pSize)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive* pZip) { + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char* pZip_filename, + const char* pArchive_name, const void* pBuf, + size_t buf_size, const void* pComment, + mz_uint16 comment_size, mz_uint level_and_flags) { + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, + pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char* pZip_filename, + const char* pArchive_name, const void* pBuf, + size_t buf_size, const void* pComment, + mz_uint16 comment_size, mz_uint level_and_flags, + mz_zip_error* pErr) { + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || + ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { + if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) { + if (pErr) *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, + * which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) { + if (pErr) *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } else { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, + level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, + 0)) { + if (pErr) *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) { + if (pErr) *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, + comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. + * (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) { + if (!actual_err) actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) { + if (!actual_err) actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) *pErr = actual_err; + + return status; +} + +void* mz_zip_extract_archive_file_to_heap_v2(const char* pZip_filename, const char* pArchive_name, + const char* pComment, size_t* pSize, mz_uint flags, + mz_zip_error* pErr) { + mz_uint32 file_index; + mz_zip_archive zip_archive; + void* p = NULL; + + if (pSize) *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) { + if (pErr) *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, + flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { + if (pErr) *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) *pErr = zip_archive.m_last_error; + + return p; +} + +void* mz_zip_extract_archive_file_to_heap(const char* pZip_filename, const char* pArchive_name, + size_t* pSize, mz_uint flags) { + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, + NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive* pZip) { + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive* pZip) { + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive* pZip, mz_zip_error err_num) { + mz_zip_error prev_err; + + if (!pZip) return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive* pZip) { + if (!pZip) return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive* pZip) { + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive* pZip) { + mz_zip_error prev_err; + + if (!pZip) return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char* mz_zip_get_error_string(mz_zip_error mz_err) { + switch (mz_err) { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 + * extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive* pZip) { + if ((!pZip) || (!pZip->m_pState)) return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive* pZip) { + if ((!pZip) || (!pZip->m_pState)) return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive* pZip) { return pZip ? pZip->m_total_files : 0; } + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive* pZip) { + if (!pZip) return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive* pZip) { + if ((!pZip) || (!pZip->m_pState)) return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE* mz_zip_get_cfile(mz_zip_archive* pZip) { + if ((!pZip) || (!pZip->m_pState)) return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive* pZip, mz_uint64 file_ofs, void* pBuf, size_t n) { + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive* pZip, mz_uint file_index, char* pFilename, + mz_uint filename_buf_size) { + mz_uint n; + const mz_uint8* p = mz_zip_get_cdh(pZip, file_index); + if (!p) { + if (filename_buf_size) pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive* pZip, mz_uint file_index, + mz_zip_archive_file_stat* pStat) { + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive* pZip) { + if (!pZip) return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || + (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/lib/model.cpp b/lib/model.cpp new file mode 100644 index 0000000..4dd5b44 --- /dev/null +++ b/lib/model.cpp @@ -0,0 +1,488 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include + +#undef MZN_DEBUG_FUNCTION_REGISTRY + +namespace MiniZinc { + +Model::FnEntry::FnEntry(FunctionI* fi0) : t(fi0->params().size()), fi(fi0), isPolymorphic(false) { + for (unsigned int i = 0; i < fi->params().size(); i++) { + t[i] = fi->params()[i]->type(); + isPolymorphic |= (t[i].bt() == Type::BT_TOP); + } +} + +bool Model::FnEntry::operator<(const Model::FnEntry& f) const { + assert(!compare(*this, f) || !compare(f, *this)); + return compare(*this, f); +} + +bool Model::FnEntry::compare(const Model::FnEntry& e1, const Model::FnEntry& e2) { + if (e1.t.size() < e2.t.size()) { + return true; + } + if (e1.t.size() == e2.t.size()) { + for (unsigned int i = 0; i < e1.t.size(); i++) { + if (e1.t[i] != e2.t[i]) { + if (e1.t[i].isSubtypeOf(e2.t[i], true)) { + return true; + } else { + if (e2.t[i].isSubtypeOf(e1.t[i], true)) return false; + switch (e1.t[i].cmp(e2.t[i])) { + case -1: + return true; + case 1: + return false; + default: + assert(false); + } + } + } + } + } + return false; +} + +Model::Model(void) : _parent(NULL), _solveItem(NULL), _outputItem(NULL) { GC::add(this); } + +Model::~Model(void) { + for (unsigned int j = 0; j < _items.size(); j++) { + Item* i = _items[j]; + if (IncludeI* ii = i->dyn_cast()) { + if (ii->own() && ii->m()) { + delete ii->m(); + ii->m(NULL); + } + } + } + GC::remove(this); +} + +VarDeclIterator Model::begin_vardecls(void) { return VarDeclIterator(this, begin()); } +VarDeclIterator Model::end_vardecls(void) { return VarDeclIterator(this, end()); } +ConstraintIterator Model::begin_constraints(void) { return ConstraintIterator(this, begin()); } +ConstraintIterator Model::end_constraints(void) { return ConstraintIterator(this, end()); } +FunctionIterator Model::begin_functions(void) { return FunctionIterator(this, begin()); } +FunctionIterator Model::end_functions(void) { return FunctionIterator(this, end()); } + +SolveI* Model::solveItem() { return _solveItem; } + +OutputI* Model::outputItem() { return _outputItem; } + +void Model::addItem(Item* i) { + _items.push_back(i); + if (i->isa()) { + Model* m = this; + while (m->_parent) m = m->_parent; + m->_solveItem = i->cast(); + } else if (i->isa()) { + Model* m = this; + while (m->_parent) m = m->_parent; + m->_outputItem = i->cast(); + } +} + +void Model::setOutputItem(OutputI* oi) { + Model* m = this; + while (m->_parent) m = m->_parent; + m->_outputItem = oi; +} + +namespace { +/// Return lowest possible base type given other type-inst restrictions +Type::BaseType lowestBt(const Type& t) { + if (t.st() == Type::ST_SET && t.ti() == Type::TI_VAR) return Type::BT_INT; + return Type::BT_BOOL; +} +/// Return highest possible base type given other type-inst restrictions +Type::BaseType highestBt(const Type& t) { + if (t.st() == Type::ST_SET && t.ti() == Type::TI_VAR) return Type::BT_INT; + if (t.ti() == Type::TI_VAR || t.st() == Type::ST_SET) return Type::BT_FLOAT; + return Type::BT_ANN; +} +} // namespace + +void Model::addPolymorphicInstances(Model::FnEntry& fe, std::vector& entries) { + entries.push_back(fe); + if (fe.isPolymorphic) { + FnEntry cur = fe; + std::vector > type_ids; + + // First step: initialise all type variables to bool + // and collect them in the stack vector + for (unsigned int i = 0; i < cur.t.size(); i++) { + if (cur.t[i].bt() == Type::BT_TOP) { + std::vector t; + for (unsigned int j = i; j < cur.t.size(); j++) { + assert(cur.fi->params()[i]->ti()->domain() && + cur.fi->params()[i]->ti()->domain()->isa()); + if (cur.fi->params()[j]->ti()->domain() && + cur.fi->params()[j]->ti()->domain()->isa()) { + TIId* id0 = cur.fi->params()[i]->ti()->domain()->cast(); + TIId* id1 = cur.fi->params()[j]->ti()->domain()->cast(); + if (id0->v() == id1->v()) { + // Found parameter with same type variable + // Initialise to lowest concrete base type (bool) + cur.t[j].bt(lowestBt(cur.t[j])); + t.push_back(&cur.t[j]); + } + } + } + type_ids.push_back(t); + } + } + + std::vector stack; + for (unsigned int i = 0; i < type_ids.size(); i++) stack.push_back(i); + int final_id = static_cast(type_ids.size()) - 1; + + while (!stack.empty()) { + if (stack.back() == final_id) { + // If this instance isn't in entries yet, add it + bool alreadyDefined = false; + for (unsigned int i = 0; i < entries.size(); i++) { + if (entries[i].t == cur.t) { + alreadyDefined = true; + break; + } + } + if (!alreadyDefined) { + entries.push_back(cur); + } + } + + Type& back_t = *type_ids[stack.back()][0]; + if (back_t.bt() == highestBt(back_t) && back_t.st() == Type::ST_SET) { + // last type, remove this item + stack.pop_back(); + } else { + if (back_t.bt() == highestBt(back_t)) { + // Create set type for current item + for (unsigned int i = 0; i < type_ids[stack.back()].size(); i++) { + type_ids[stack.back()][i]->st(Type::ST_SET); + type_ids[stack.back()][i]->bt(lowestBt(*type_ids[stack.back()][i])); + } + } else { + // Increment type of current item + Type::BaseType nextType = static_cast(back_t.bt() + 1); + for (unsigned int i = 0; i < type_ids[stack.back()].size(); i++) { + type_ids[stack.back()][i]->bt(nextType); + } + } + // Reset types of all further items and push them + for (unsigned int i = stack.back() + 1; i < type_ids.size(); i++) { + for (unsigned int j = 0; j < type_ids[i].size(); j++) { + type_ids[i][j]->bt(lowestBt(*type_ids[i][j])); + } + stack.push_back(i); + } + } + } + } +} + +void Model::registerFn(EnvI& env, FunctionI* fi) { + Model* m = this; + while (m->_parent) m = m->_parent; + FnMap::iterator i_id = m->fnmap.find(fi->id()); + if (i_id == m->fnmap.end()) { + // new element + std::vector v; + FnEntry fe(fi); + addPolymorphicInstances(fe, v); + m->fnmap.insert(std::pair >(fi->id(), v)); + } else { + // add to list of existing elements + std::vector& v = i_id->second; + for (unsigned int i = 0; i < v.size(); i++) { + if (v[i].fi->params().size() == fi->params().size()) { + bool alleq = true; + for (unsigned int j = 0; j < fi->params().size(); j++) { + Type t1 = v[i].fi->params()[j]->type(); + Type t2 = fi->params()[j]->type(); + t1.enumId(0); + t2.enumId(0); + if (t1 != t2) { + alleq = false; + break; + } + } + if (alleq) { + if (v[i].fi->e() && fi->e() && !v[i].isPolymorphic) { + throw TypeError( + env, fi->loc(), + "function with the same type already defined in " + v[i].fi->loc().toString()); + } else { + if (fi->e() || v[i].isPolymorphic) v[i] = fi; + return; + } + } + } + } + FnEntry fe(fi); + addPolymorphicInstances(fe, v); + } + if (fi->id() == "mzn_reverse_map_var") { + if (fi->params().size() != 1 || fi->ti()->type() != Type::varbool()) { + throw TypeError(env, fi->loc(), + "functions called `mzn_reverse_map_var` must have a single argument and " + "return type var bool"); + } + Type t = fi->params()[0]->type(); + revmapmap.insert(std::pair(t.toInt(), fi)); + } +} + +FunctionI* Model::matchFn(EnvI& env, const ASTString& id, const std::vector& t, + bool strictEnums) { + if (id == constants().var_redef->id()) return constants().var_redef; + Model* m = this; + while (m->_parent) m = m->_parent; + FnMap::iterator i_id = m->fnmap.find(id); + if (i_id == m->fnmap.end()) { + return NULL; + } + std::vector& v = i_id->second; + for (unsigned int i = 0; i < v.size(); i++) { + std::vector& fi_t = v[i].t; +#ifdef MZN_DEBUG_FUNCTION_REGISTRY + std::cerr << "try " << *v[i].fi; +#endif + if (fi_t.size() == t.size()) { + bool match = true; + for (unsigned int j = 0; j < t.size(); j++) { + if (!env.isSubtype(t[j], fi_t[j], strictEnums)) { +#ifdef MZN_DEBUG_FUNCTION_REGISTRY + std::cerr << t[j].toString(env) << " does not match " << fi_t[j].toString(env) << "\n"; +#endif + match = false; + break; + } + } + if (match) { + return v[i].fi; + } + } + } + return NULL; +} + +void Model::mergeStdLib(EnvI& env, Model* m) const { + for (FnMap::const_iterator it = fnmap.begin(); it != fnmap.end(); ++it) { + for (std::vector::const_iterator cit = it->second.begin(); cit != it->second.end(); + ++cit) { + if ((*cit).fi->from_stdlib()) { + m->registerFn(env, (*cit).fi); + } + } + } +} + +void Model::sortFn(void) { + Model* m = this; + while (m->_parent) m = m->_parent; + for (FnMap::iterator it = m->fnmap.begin(); it != m->fnmap.end(); ++it) { + // Sort all functions by type + std::sort(it->second.begin(), it->second.end()); + } +} + +void Model::fixFnMap(void) { + Model* m = this; + while (m->_parent) m = m->_parent; + for (FnMap::iterator it = m->fnmap.begin(); it != m->fnmap.end(); ++it) { + for (unsigned int i = 0; i < it->second.size(); i++) { + for (unsigned int j = 0; j < it->second[i].t.size(); j++) { + if (it->second[i].t[j].isunknown()) { + it->second[i].t[j] = it->second[i].fi->params()[j]->type(); + } + } + } + } +} + +void Model::checkFnOverloading(EnvI& env) { + Model* m = this; + while (m->_parent) m = m->_parent; + for (FnMap::iterator it = m->fnmap.begin(); it != m->fnmap.end(); ++it) { + std::vector& fs = it->second; + for (unsigned int i = 0; i < fs.size() - 1; i++) { + FunctionI* cur = fs[i].fi; + for (unsigned int j = i + 1; j < fs.size(); j++) { + FunctionI* cmp = fs[j].fi; + if (cur == cmp || cur->params().size() != cmp->params().size()) break; + bool allEqual = true; + for (unsigned int i = 0; i < cur->params().size(); i++) { + Type t1 = cur->params()[i]->type(); + Type t2 = cmp->params()[i]->type(); + t1.enumId(0); + t2.enumId(0); + if (t1 != t2) { + allEqual = false; + break; + } + } + if (allEqual) + throw TypeError(env, cur->loc(), + "unsupported type of overloading. \nFunction/predicate with equivalent " + "signature defined in " + + cmp->loc().toString()); + } + } + } +} + +FunctionI* Model::matchFn(EnvI& env, const ASTString& id, const std::vector& args, + bool strictEnums) const { + if (id == constants().var_redef->id()) return constants().var_redef; + const Model* m = this; + while (m->_parent) m = m->_parent; + FnMap::const_iterator it = m->fnmap.find(id); + if (it == m->fnmap.end()) { + return NULL; + } + const std::vector& v = it->second; + std::vector matched; + Expression* botarg = NULL; + for (unsigned int i = 0; i < v.size(); i++) { + const std::vector& fi_t = v[i].t; +#ifdef MZN_DEBUG_FUNCTION_REGISTRY + std::cerr << "try " << *v[i].fi; +#endif + if (fi_t.size() == args.size()) { + bool match = true; + for (unsigned int j = 0; j < args.size(); j++) { + if (!env.isSubtype(args[j]->type(), fi_t[j], strictEnums)) { +#ifdef MZN_DEBUG_FUNCTION_REGISTRY + std::cerr << args[j]->type().toString(env) << " does not match " << fi_t[j].toString(env) + << "\n"; +#endif + match = false; + break; + } + if (args[j]->type().isbot() && fi_t[j].bt() != Type::BT_TOP) { + botarg = args[j]; + } + } + if (match) { + if (botarg) + matched.push_back(v[i].fi); + else + return v[i].fi; + } + } + } + if (matched.empty()) return NULL; + if (matched.size() == 1) return matched[0]; + Type t = matched[0]->ti()->type(); + t.ti(Type::TI_PAR); + for (unsigned int i = 1; i < matched.size(); i++) { + if (!env.isSubtype(t, matched[i]->ti()->type(), strictEnums)) + throw TypeError(env, botarg->loc(), "ambiguous overloading on return type of function"); + } + return matched[0]; +} + +FunctionI* Model::matchFn(EnvI& env, Call* c, bool strictEnums) const { + if (c->id() == constants().var_redef->id()) return constants().var_redef; + const Model* m = this; + while (m->_parent) m = m->_parent; + FnMap::const_iterator it = m->fnmap.find(c->id()); + if (it == m->fnmap.end()) { + return NULL; + } + const std::vector& v = it->second; + std::vector matched; + Expression* botarg = NULL; + for (unsigned int i = 0; i < v.size(); i++) { + const std::vector& fi_t = v[i].t; +#ifdef MZN_DEBUG_FUNCTION_REGISTRY + std::cerr << "try " << *v[i].fi; +#endif + if (fi_t.size() == c->n_args()) { + bool match = true; + for (unsigned int j = 0; j < c->n_args(); j++) { + if (!env.isSubtype(c->arg(j)->type(), fi_t[j], strictEnums)) { +#ifdef MZN_DEBUG_FUNCTION_REGISTRY + std::cerr << c->arg(j)->type().toString(env) << " does not match " + << fi_t[j].toString(env) << "\n"; + std::cerr << "Wrong argument is " << *c->arg(j); +#endif + match = false; + break; + } + if (c->arg(j)->type().isbot() && fi_t[j].bt() != Type::BT_TOP) { + botarg = c->arg(j); + } + } + if (match) { + if (botarg) + matched.push_back(v[i].fi); + else + return v[i].fi; + } + } + } + if (matched.empty()) return NULL; + if (matched.size() == 1) return matched[0]; + Type t = matched[0]->ti()->type(); + t.ti(Type::TI_PAR); + for (unsigned int i = 1; i < matched.size(); i++) { + if (!env.isSubtype(t, matched[i]->ti()->type(), strictEnums)) + throw TypeError(env, botarg->loc(), "ambiguous overloading on return type of function"); + } + return matched[0]; +} + +FunctionI* Model::matchRevMap(EnvI& env, const Type& t0) const { + const Model* m = this; + while (m->_parent) m = m->_parent; + Type t = t0; + t.enumId(0); + RevMapperMap::const_iterator it = revmapmap.find(t.toInt()); + if (it != revmapmap.end()) { + return it->second; + } else { + return NULL; + } +} + +Item*& Model::operator[](int i) { + assert(i < _items.size()); + return _items[i]; +} +const Item* Model::operator[](int i) const { + assert(i < _items.size()); + return _items[i]; +} +unsigned int Model::size(void) const { return static_cast(_items.size()); } + +std::vector::iterator Model::begin(void) { return _items.begin(); } + +std::vector::const_iterator Model::begin(void) const { return _items.begin(); } + +std::vector::iterator Model::end(void) { return _items.end(); } + +std::vector::const_iterator Model::end(void) const { return _items.end(); } + +void Model::compact(void) { + struct { + bool operator()(const Item* i) { return i->removed(); } + } isremoved; + _items.erase(remove_if(_items.begin(), _items.end(), isremoved), _items.end()); +} + +} // namespace MiniZinc diff --git a/lib/optimize.cpp b/lib/optimize.cpp new file mode 100644 index 0000000..61c2471 --- /dev/null +++ b/lib/optimize.cpp @@ -0,0 +1,1605 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace MiniZinc { + +void VarOccurrences::add_idx(VarDeclI* i, int idx_i) { idx.insert(i->e()->id(), idx_i); } +void VarOccurrences::add_idx(VarDecl* e, int idx_i) { + assert(find(e) == -1); + idx.insert(e->id(), idx_i); +} +int VarOccurrences::find(VarDecl* vd) { + IdMap::iterator it = idx.find(vd->id()); + return it == idx.end() ? -1 : it->second; +} +void VarOccurrences::remove(VarDecl* vd) { idx.remove(vd->id()); } + +void VarOccurrences::add(VarDecl* v, Item* i) { + IdMap::iterator vi = _m.find(v->id()->decl()->id()); + if (vi == _m.end()) { + Items items; + items.insert(i); + _m.insert(v->id()->decl()->id(), items); + } else { + vi->second.insert(i); + } +} + +int VarOccurrences::remove(VarDecl* v, Item* i) { + IdMap::iterator vi = _m.find(v->id()->decl()->id()); + assert(vi != _m.end()); + vi->second.erase(i); + return static_cast(vi->second.size()); +} + +void VarOccurrences::removeAllOccurrences(VarDecl* v) { + IdMap::iterator vi = _m.find(v->id()->decl()->id()); + assert(vi != _m.end()); + vi->second.clear(); +} + +void VarOccurrences::unify(EnvI& env, Model* m, Id* id0_0, Id* id1_0) { + Id* id0 = id0_0->decl()->id(); + Id* id1 = id1_0->decl()->id(); + + VarDecl* v0 = id0->decl(); + VarDecl* v1 = id1->decl(); + + if (v0 == v1) return; + + int v0idx = find(v0); + assert(v0idx != -1); + env.flat_removeItem(v0idx); + + IdMap::iterator vi0 = _m.find(v0->id()); + if (vi0 != _m.end()) { + IdMap::iterator vi1 = _m.find(v1->id()); + if (vi1 == _m.end()) { + _m.insert(v1->id(), vi0->second); + } else { + vi1->second.insert(vi0->second.begin(), vi0->second.end()); + } + _m.remove(v0->id()); + } + + remove(v0); + id0->redirect(id1); +} + +void VarOccurrences::clear(void) { + _m.clear(); + idx.clear(); +} + +int VarOccurrences::occurrences(VarDecl* v) { + IdMap::iterator vi = _m.find(v->id()->decl()->id()); + return (vi == _m.end() ? 0 : static_cast(vi->second.size())); +} + +int VarOccurrences::usages(VarDecl* v) { + auto vi = _m.find(v->id()->decl()->id()); + if (vi == _m.end()) { + return 0; + } + int count = 0; + for (Item* i : vi->second) { + auto vd = i->dyn_cast(); + if (vd && vd->e() && vd->e() && vd->e()->e() && + (vd->e()->e()->isa() || vd->e()->e()->isa())) { + count += usages(vd->e()); + } else { + count++; + } + } + return count; +} + +void CollectOccurrencesI::vVarDeclI(VarDeclI* v) { + CollectOccurrencesE ce(vo, v); + topDown(ce, v->e()); +} +void CollectOccurrencesI::vConstraintI(ConstraintI* ci) { + CollectOccurrencesE ce(vo, ci); + topDown(ce, ci->e()); + for (ExpressionSetIter it = ci->e()->ann().begin(); it != ci->e()->ann().end(); ++it) + topDown(ce, *it); +} +void CollectOccurrencesI::vSolveI(SolveI* si) { + CollectOccurrencesE ce(vo, si); + topDown(ce, si->e()); + for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++si) topDown(ce, *it); +} + +bool isOutput(VarDecl* vd) { + for (ExpressionSetIter it = vd->ann().begin(); it != vd->ann().end(); ++it) { + if (*it) { + if (*it == constants().ann.output_var) return true; + if (Call* c = (*it)->dyn_cast()) { + if (c->id() == constants().ann.output_array) return true; + } + } + } + return false; +} + +void unify(EnvI& env, std::vector& deletedVarDecls, Id* id0, Id* id1) { + if (id0->decl() != id1->decl()) { + if (isOutput(id0->decl())) { + std::swap(id0, id1); + } + + if (id0->decl()->e() != NULL && !Expression::equal(id0->decl()->e(), id1->decl()->id())) { + Expression* rhs = id0->decl()->e(); + + VarDeclI* vdi1 = (*env.flat())[env.vo.find(id1->decl())]->cast(); + CollectOccurrencesE ce(env.vo, vdi1); + topDown(ce, rhs); + + id1->decl()->e(rhs); + id0->decl()->e(NULL); + + VarDeclI* vdi0 = (*env.flat())[env.vo.find(id0->decl())]->cast(); + CollectDecls cd(env.vo, deletedVarDecls, vdi0); + topDown(cd, rhs); + } + if (Expression::equal(id1->decl()->e(), id0->decl()->id())) { + VarDeclI* vdi1 = (*env.flat())[env.vo.find(id1->decl())]->cast(); + CollectDecls cd(env.vo, deletedVarDecls, vdi1); + Expression* rhs = id1->decl()->e(); + topDown(cd, rhs); + id1->decl()->e(NULL); + } + // Compute intersection of domains + if (id0->decl()->ti()->domain() != NULL) { + if (id1->decl()->ti()->domain() != NULL) { + if (id0->type().isint() || id0->type().isintset()) { + IntSetVal* isv0 = eval_intset(env, id0->decl()->ti()->domain()); + IntSetVal* isv1 = eval_intset(env, id1->decl()->ti()->domain()); + IntSetRanges isv0r(isv0); + IntSetRanges isv1r(isv1); + Ranges::Inter inter(isv0r, isv1r); + IntSetVal* nd = IntSetVal::ai(inter); + if (nd->size() == 0) { + env.fail(); + } else if (nd->card() != isv1->card()) { + id1->decl()->ti()->domain(new SetLit(Location(), nd)); + if (nd->card() == isv0->card()) { + id1->decl()->ti()->setComputedDomain(id0->decl()->ti()->computedDomain()); + } else { + id1->decl()->ti()->setComputedDomain(false); + } + } + } else if (id0->type().isbool()) { + if (eval_bool(env, id0->decl()->ti()->domain()) != + eval_bool(env, id1->decl()->ti()->domain())) { + env.fail(); + } + } else { + // float + FloatSetVal* isv0 = eval_floatset(env, id0->decl()->ti()->domain()); + FloatSetVal* isv1 = eval_floatset(env, id1->decl()->ti()->domain()); + FloatSetRanges isv0r(isv0); + FloatSetRanges isv1r(isv1); + Ranges::Inter inter(isv0r, isv1r); + FloatSetVal* nd = FloatSetVal::ai(inter); + + FloatSetRanges nd_r(nd); + FloatSetRanges isv1r_2(isv1); + + if (nd->size() == 0) { + env.fail(); + } else if (!Ranges::equal(nd_r, isv1r_2)) { + id1->decl()->ti()->domain(new SetLit(Location(), nd)); + FloatSetRanges nd_r_2(nd); + FloatSetRanges isv0r_2(isv0); + if (Ranges::equal(nd_r_2, isv0r_2)) { + id1->decl()->ti()->setComputedDomain(id0->decl()->ti()->computedDomain()); + } else { + id1->decl()->ti()->setComputedDomain(false); + } + } + } + + } else { + id1->decl()->ti()->domain(id0->decl()->ti()->domain()); + } + } + + // If both variables are output variables, unify them in the output model + if (isOutput(id0->decl())) { + assert(env.output_vo_flat.find(id0->decl()) != -1); + VarDecl* id0_output = + (*env.output)[env.output_vo_flat.find(id0->decl())]->cast()->e(); + assert(env.output_vo_flat.find(id1->decl()) != -1); + VarDecl* id1_output = + (*env.output)[env.output_vo_flat.find(id1->decl())]->cast()->e(); + if (id0_output->e() == NULL) { + id0_output->e(id1_output->id()); + } + } + + env.vo.unify(env, env.flat(), id0, id1); + } +} + +void substituteFixedVars(EnvI& env, Item* ii, std::vector& deletedVarDecls); +void simplifyBoolConstraint(EnvI& env, Item* ii, VarDecl* vd, bool& remove, + std::deque& vardeclQueue, std::deque& constraintQueue, + std::vector& toRemove, + std::unordered_map& nonFixedLiteralCount); + +bool simplifyConstraint(EnvI& env, Item* ii, std::vector& deletedVarDecls, + std::deque& constraintQueue, std::deque& vardeclQueue); + +void pushVarDecl(EnvI& env, VarDeclI* vdi, int vd_idx, std::deque& q) { + if (!vdi->removed() && !vdi->flag()) { + vdi->flag(true); + q.push_back(vd_idx); + } +} +void pushVarDecl(EnvI& env, int vd_idx, std::deque& q) { + pushVarDecl(env, (*env.flat())[vd_idx]->cast(), vd_idx, q); +} + +void pushDependentConstraints(EnvI& env, Id* id, std::deque& q) { + IdMap::iterator it = env.vo._m.find(id->decl()->id()); + if (it != env.vo._m.end()) { + for (VarOccurrences::Items::iterator item = it->second.begin(); item != it->second.end(); + ++item) { + if (ConstraintI* ci = (*item)->dyn_cast()) { + if (!ci->removed() && !ci->flag()) { + ci->flag(true); + q.push_back(ci); + } + } else if (VarDeclI* vdi = (*item)->dyn_cast()) { + if (vdi->e()->id()->decl() != vdi->e()) { + vdi = (*env.flat())[env.vo.find(vdi->e()->id()->decl())]->cast(); + } + if (!vdi->removed() && !vdi->flag() && vdi->e()->e()) { + vdi->flag(true); + q.push_back(vdi); + } + } + } + } +} + +void optimize(Env& env, bool chain_compression) { + if (env.envi().failed()) return; + try { + EnvI& envi = env.envi(); + Model& m = *envi.flat(); + std::vector toAssignBoolVars; + std::vector toRemoveConstraints; + std::vector deletedVarDecls; + + // Queue of constraint and variable items that still need to be optimised + std::deque constraintQueue; + // Queue of variable declarations (indexes into the model) that still need to be optimised + std::deque vardeclQueue; + + std::vector boolConstraints; + + GCLock lock; + + // Phase 0: clean up + // - clear flags for all constraint and variable declaration items + // (flags are used to indicate whether an item is already queued or not) + for (unsigned int i = 0; i < m.size(); i++) { + if (!m[i]->removed()) { + if (ConstraintI* ci = m[i]->dyn_cast()) { + ci->flag(false); + } else if (VarDeclI* vdi = m[i]->dyn_cast()) { + vdi->flag(false); + } + } + } + + // Phase 1: initialise queues + // - remove equality constraints between identifiers + // - remove toplevel forall constraints + // - collect exists, clauses and reified foralls in boolConstraints + // - remove "constraint x" where x is a bool var + // - unify variables that are assigned to an identifier + // - push bool vars that are fixed and have a RHS (to propagate the RHS constraint) + // - push int vars that are fixed (either have a RHS or a singleton domain) + for (unsigned int i = 0; i < m.size(); i++) { + if (m[i]->removed()) continue; + if (ConstraintI* ci = m[i]->dyn_cast()) { + ci->flag(false); + if (!ci->removed()) { + if (Call* c = ci->e()->dyn_cast()) { + if ((c->id() == constants().ids.int_.eq || c->id() == constants().ids.bool_eq || + c->id() == constants().ids.float_.eq || c->id() == constants().ids.set_eq) && + c->arg(0)->isa() && c->arg(1)->isa() && + (c->arg(0)->cast()->decl()->e() == NULL || + c->arg(1)->cast()->decl()->e() == NULL)) { + // Equality constraint between two identifiers: unify + + unify(envi, deletedVarDecls, c->arg(0)->cast(), c->arg(1)->cast()); + { + VarDecl* vd = c->arg(0)->cast()->decl(); + int v0idx = envi.vo.find(vd); + pushVarDecl(envi, m[v0idx]->cast(), v0idx, vardeclQueue); + } + + pushDependentConstraints(envi, c->arg(0)->cast(), constraintQueue); + CollectDecls cd(envi.vo, deletedVarDecls, ci); + topDown(cd, c); + ci->e(constants().lit_true); + envi.flat_removeItem(i); + } else if (c->id() == constants().ids.int_.lin_eq && + Expression::equal(c->arg(2), IntLit::a(0))) { + ArrayLit* al_c = follow_id(c->arg(0))->cast(); + if (al_c->size() == 2 && + (*al_c)[0]->cast()->v() == -(*al_c)[1]->cast()->v()) { + ArrayLit* al_x = follow_id(c->arg(1))->cast(); + if ((*al_x)[0]->isa() && (*al_x)[1]->isa() && + ((*al_x)[0]->cast()->decl()->e() == NULL || + (*al_x)[1]->cast()->decl()->e() == NULL)) { + // Equality constraint between two identifiers: unify + + unify(envi, deletedVarDecls, (*al_x)[0]->cast(), (*al_x)[1]->cast()); + { + VarDecl* vd = (*al_x)[0]->cast()->decl(); + int v0idx = envi.vo.find(vd); + pushVarDecl(envi, m[v0idx]->cast(), v0idx, vardeclQueue); + } + + pushDependentConstraints(envi, (*al_x)[0]->cast(), constraintQueue); + CollectDecls cd(envi.vo, deletedVarDecls, ci); + topDown(cd, c); + ci->e(constants().lit_true); + envi.flat_removeItem(i); + } + } + } else if (c->id() == constants().ids.forall) { + // Remove forall constraints, assign variables inside the forall to true + + ArrayLit* al = follow_id(c->arg(0))->cast(); + for (unsigned int j = al->size(); j--;) { + if (Id* id = (*al)[j]->dyn_cast()) { + if (id->decl()->ti()->domain() == NULL) { + toAssignBoolVars.push_back(envi.vo.idx.find(id->decl()->id())->second); + } else if (id->decl()->ti()->domain() == constants().lit_false) { + env.envi().fail(); + id->decl()->e(constants().lit_true); + } + } // todo: check else case (fixed bool inside a forall at this stage) + } + toRemoveConstraints.push_back(i); + } else if (c->id() == constants().ids.exists || c->id() == constants().ids.clause) { + // Add disjunctive constraints to the boolConstraints list + + boolConstraints.push_back(i); + } + } else if (Id* id = ci->e()->dyn_cast()) { + if (id->decl()->ti()->domain() == constants().lit_false) { + env.envi().fail(); + ci->e(constants().lit_false); + } else { + if (id->decl()->ti()->domain() == NULL) { + toAssignBoolVars.push_back(envi.vo.idx.find(id->decl()->id())->second); + } + toRemoveConstraints.push_back(i); + } + } + } + } else if (VarDeclI* vdi = m[i]->dyn_cast()) { + vdi->flag(false); + if (vdi->e()->e() && vdi->e()->e()->isa() && vdi->e()->type().dim() == 0) { + // unify variable with the identifier it's assigned to + Id* id1 = vdi->e()->e()->cast(); + vdi->e()->e(NULL); + unify(envi, deletedVarDecls, vdi->e()->id(), id1); + pushDependentConstraints(envi, id1, constraintQueue); + } + if (vdi->e()->type().isbool() && vdi->e()->type().isvar() && vdi->e()->type().dim() == 0 && + (vdi->e()->ti()->domain() == constants().lit_true || + vdi->e()->ti()->domain() == constants().lit_false)) { + // push RHS onto constraint queue since this bool var is fixed + pushVarDecl(envi, vdi, i, vardeclQueue); + pushDependentConstraints(envi, vdi->e()->id(), constraintQueue); + } + if (Call* c = Expression::dyn_cast(vdi->e()->e())) { + if (c->id() == constants().ids.forall || c->id() == constants().ids.exists || + c->id() == constants().ids.clause) { + // push reified foralls, exists, clauses + boolConstraints.push_back(i); + } + } + if (vdi->e()->type().isint()) { + if ((vdi->e()->e() && vdi->e()->e()->isa()) || + (vdi->e()->ti()->domain() && vdi->e()->ti()->domain()->isa() && + vdi->e()->ti()->domain()->cast()->isv()->size() == 1 && + vdi->e()->ti()->domain()->cast()->isv()->min() == + vdi->e()->ti()->domain()->cast()->isv()->max())) { + // Variable is assigned an integer, or has a singleton domain + pushVarDecl(envi, vdi, i, vardeclQueue); + pushDependentConstraints(envi, vdi->e()->id(), constraintQueue); + } + } + } + } + + // Phase 2: handle boolean constraints + // - check if any boolean constraint is subsumed (e.g. a fixed false in a forall, or a fixed + // true in a disjunction) + // - check if any boolean constraint has a single non-fixed literal left, then fix that literal + for (unsigned int i = static_cast(boolConstraints.size()); i--;) { + Item* bi = m[boolConstraints[i]]; + if (bi->removed()) continue; + Call* c; + + if (bi->isa()) { + c = bi->cast()->e()->dyn_cast(); + } else { + c = bi->cast()->e()->e()->dyn_cast(); + } + if (c == NULL) continue; + bool isConjunction = (c->id() == constants().ids.forall); + bool subsumed = false; + Id* finalId = NULL; + bool finalIdNeg = false; + int idCount = 0; + std::vector pos; + std::vector neg; + for (unsigned int j = 0; j < c->n_args(); j++) { + bool unit = (j == 0 ? isConjunction : !isConjunction); + ArrayLit* al = follow_id(c->arg(j))->cast(); + for (unsigned int k = 0; k < al->size(); k++) { + if (Id* ident = (*al)[k]->dyn_cast()) { + if (ident->decl()->ti()->domain() || + (ident->decl()->e() && ident->decl()->e()->type().ispar())) { + bool identValue = ident->decl()->ti()->domain() + ? eval_bool(envi, ident->decl()->ti()->domain()) + : eval_bool(envi, ident->decl()->e()); + if (identValue != unit) { + subsumed = true; + goto subsumed_check_done; + } + } else { + idCount++; + finalId = ident; + finalIdNeg = (j == 1); + if (j == 0) + pos.push_back(ident->decl()); + else + neg.push_back(ident->decl()); + } + } else { + if ((*al)[k]->cast()->v() != unit) { + subsumed = true; + goto subsumed_check_done; + } + } + } + } + if (pos.size() > 0 && neg.size() > 0) { + std::sort(pos.begin(), pos.end()); + std::sort(neg.begin(), neg.end()); + unsigned int ix = 0; + unsigned int iy = 0; + for (;;) { + if (pos[ix] == neg[iy]) { + subsumed = true; + break; + } + if (pos[ix] < neg[iy]) { + ix++; + } else { + iy++; + } + if (ix == pos.size() || iy == neg.size()) break; + } + } + + subsumed_check_done: + if (subsumed) { + if (isConjunction) { + if (bi->isa()) { + env.envi().fail(); + } else { + if (bi->cast()->e()->ti()->domain()) { + if (eval_bool(envi, bi->cast()->e()->ti()->domain())) { + envi.fail(); + } + } else { + CollectDecls cd(envi.vo, deletedVarDecls, bi); + topDown(cd, bi->cast()->e()->e()); + bi->cast()->e()->ti()->domain(constants().lit_false); + bi->cast()->e()->ti()->setComputedDomain(true); + bi->cast()->e()->e(constants().lit_false); + pushVarDecl(envi, bi->cast(), boolConstraints[i], vardeclQueue); + pushDependentConstraints(envi, bi->cast()->e()->id(), constraintQueue); + } + } + } else { + if (bi->isa()) { + CollectDecls cd(envi.vo, deletedVarDecls, bi); + topDown(cd, bi->cast()->e()); + env.envi().flat_removeItem(bi); + } else { + if (bi->cast()->e()->ti()->domain()) { + if (!eval_bool(envi, bi->cast()->e()->ti()->domain())) { + envi.fail(); + } + } else { + CollectDecls cd(envi.vo, deletedVarDecls, bi); + topDown(cd, bi->cast()->e()->e()); + bi->cast()->e()->ti()->domain(constants().lit_true); + bi->cast()->e()->ti()->setComputedDomain(true); + bi->cast()->e()->e(constants().lit_true); + pushVarDecl(envi, bi->cast(), boolConstraints[i], vardeclQueue); + pushDependentConstraints(envi, bi->cast()->e()->id(), constraintQueue); + } + } + } + } else if (idCount == 1 && bi->isa()) { + assert(finalId->decl()->ti()->domain() == NULL); + finalId->decl()->ti()->domain(constants().boollit(!finalIdNeg)); + if (finalId->decl()->e() == NULL) finalId->decl()->e(constants().boollit(!finalIdNeg)); + CollectDecls cd(envi.vo, deletedVarDecls, bi); + topDown(cd, bi->cast()->e()); + env.envi().flat_removeItem(bi); + pushVarDecl(envi, envi.vo.idx.find(finalId->decl()->id())->second, vardeclQueue); + pushDependentConstraints(envi, finalId, constraintQueue); + } // todo: for var decls, we could unify the variable with the remaining finalId (the RHS) + } + + // Fix all bool vars in toAssignBoolVars to true and push their declarations and constraints + for (unsigned int i = static_cast(toAssignBoolVars.size()); i--;) { + if (m[toAssignBoolVars[i]]->removed()) continue; + VarDeclI* vdi = m[toAssignBoolVars[i]]->cast(); + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().lit_true); + pushVarDecl(envi, vdi, toAssignBoolVars[i], vardeclQueue); + pushDependentConstraints(envi, vdi->e()->id(), constraintQueue); + } + } + + // Phase 3: fixpoint of constraint and variable simplification + + std::unordered_map nonFixedLiteralCount; + while (!vardeclQueue.empty() || !constraintQueue.empty()) { + while (!vardeclQueue.empty()) { + int var_idx = vardeclQueue.front(); + vardeclQueue.pop_front(); + m[var_idx]->cast()->flag(false); + VarDecl* vd = m[var_idx]->cast()->e(); + + if (vd->type().isbool() && vd->ti()->domain()) { + bool isTrue = vd->ti()->domain() == constants().lit_true; + bool remove = false; + if (vd->e()) { + if (Id* id = vd->e()->dyn_cast()) { + // Variable assigned to id, so fix id + if (id->decl()->ti()->domain() == NULL) { + id->decl()->ti()->domain(vd->ti()->domain()); + pushVarDecl(envi, envi.vo.idx.find(id->decl()->id())->second, vardeclQueue); + } else if (id->decl()->ti()->domain() != vd->ti()->domain()) { + env.envi().fail(); + } + remove = true; + } else if (Call* c = vd->e()->dyn_cast()) { + if (isTrue && c->id() == constants().ids.forall) { + // Reified forall is now fixed to true, so make all elements of the conjunction true + remove = true; + ArrayLit* al = follow_id(c->arg(0))->cast(); + for (unsigned int i = 0; i < al->size(); i++) { + if (Id* id = (*al)[i]->dyn_cast()) { + if (id->decl()->ti()->domain() == NULL) { + id->decl()->ti()->domain(constants().lit_true); + pushVarDecl(envi, envi.vo.idx.find(id->decl()->id())->second, vardeclQueue); + } else if (id->decl()->ti()->domain() == constants().lit_false) { + env.envi().fail(); + remove = true; + } + } + } + } else if (!isTrue && + (c->id() == constants().ids.exists || c->id() == constants().ids.clause)) { + // Reified disjunction is now fixed to false, so make all elements of the + // disjunction false + remove = true; + for (unsigned int i = 0; i < c->n_args(); i++) { + bool ispos = i == 0; + ArrayLit* al = follow_id(c->arg(i))->cast(); + for (unsigned int j = 0; j < al->size(); j++) { + if (Id* id = (*al)[j]->dyn_cast()) { + if (id->decl()->ti()->domain() == NULL) { + id->decl()->ti()->domain(constants().boollit(!ispos)); + pushVarDecl(envi, envi.vo.idx.find(id->decl()->id())->second, vardeclQueue); + } else if (id->decl()->ti()->domain() == constants().boollit(ispos)) { + env.envi().fail(); + remove = true; + } + } + } + } + } + } + } else { + // If bool variable doesn't have a RHS, just remove it + remove = true; + } + pushDependentConstraints(envi, vd->id(), constraintQueue); + std::vector toRemove; + IdMap::iterator it = envi.vo._m.find(vd->id()->decl()->id()); + + // Handle all boolean constraints that involve this variable + if (it != envi.vo._m.end()) { + for (VarOccurrences::Items::iterator item = it->second.begin(); + item != it->second.end(); ++item) { + if ((*item)->removed()) continue; + if (VarDeclI* vdi = (*item)->dyn_cast()) { + // The variable occurs in the RHS of another variable, so + // if that is an array variable, simplify all constraints that + // mention the array variable + if (vdi->e()->e() && vdi->e()->e()->isa()) { + IdMap::iterator ait = + envi.vo._m.find(vdi->e()->id()->decl()->id()); + if (ait != envi.vo._m.end()) { + for (VarOccurrences::Items::iterator aitem = ait->second.begin(); + aitem != ait->second.end(); ++aitem) { + simplifyBoolConstraint(envi, *aitem, vd, remove, vardeclQueue, + constraintQueue, toRemove, nonFixedLiteralCount); + } + } + continue; + } + } + // Simplify the constraint *item (which depends on this variable) + simplifyBoolConstraint(envi, *item, vd, remove, vardeclQueue, constraintQueue, + toRemove, nonFixedLiteralCount); + } + } + // Actually remove all items that have become unnecessary in the step above + for (unsigned int i = static_cast(toRemove.size()); i--;) { + if (ConstraintI* ci = toRemove[i]->dyn_cast()) { + CollectDecls cd(envi.vo, deletedVarDecls, ci); + topDown(cd, ci->e()); + envi.flat_removeItem(ci); + } else { + VarDeclI* vdi = toRemove[i]->cast(); + CollectDecls cd(envi.vo, deletedVarDecls, vdi); + topDown(cd, vdi->e()->e()); + vdi->e()->e(NULL); + } + } + if (remove) { + deletedVarDecls.push_back(vd); + } else { + simplifyConstraint(envi, m[var_idx], deletedVarDecls, constraintQueue, vardeclQueue); + } + } else if (vd->type().isint() && vd->ti()->domain()) { + IntSetVal* isv = eval_intset(envi, vd->ti()->domain()); + if (isv->size() == 1 && isv->card() == 1) { + simplifyConstraint(envi, m[var_idx], deletedVarDecls, constraintQueue, vardeclQueue); + } + } + } // end of processing of variable queue + + // Now handle all non-boolean constraints (i.e. anything except forall, clause, exists) + bool handledConstraint = false; + while (!handledConstraint && !constraintQueue.empty()) { + Item* item = constraintQueue.front(); + constraintQueue.pop_front(); + Call* c; + ArrayLit* al = NULL; + if (ConstraintI* ci = item->dyn_cast()) { + ci->flag(false); + c = Expression::dyn_cast(ci->e()); + } else { + if (item->removed()) { + // This variable was removed because of unification, so we look up the + // variable it was unified to + item = m[envi.vo.find(item->cast()->e()->id()->decl())]->cast(); + } + item->cast()->flag(false); + c = Expression::dyn_cast(item->cast()->e()->e()); + al = Expression::dyn_cast(item->cast()->e()->e()); + } + if (!item->removed()) { + if (al) { + // Substitute all fixed variables by their values in array literals, then + // push all constraints that depend on the array + substituteFixedVars(envi, item, deletedVarDecls); + pushDependentConstraints(envi, item->cast()->e()->id(), constraintQueue); + } else if (!c || + !(c->id() == constants().ids.forall || c->id() == constants().ids.exists || + c->id() == constants().ids.clause)) { + // For any constraint that is not forall, exists or clause, + // substitute fixed arguments, then simplify it + substituteFixedVars(envi, item, deletedVarDecls); + handledConstraint = + simplifyConstraint(envi, item, deletedVarDecls, constraintQueue, vardeclQueue); + } + } + } + } + + // Clean up constraints that have been removed in the previous phase + for (unsigned int i = static_cast(toRemoveConstraints.size()); i--;) { + ConstraintI* ci = m[toRemoveConstraints[i]]->cast(); + CollectDecls cd(envi.vo, deletedVarDecls, ci); + topDown(cd, ci->e()); + envi.flat_removeItem(toRemoveConstraints[i]); + } + + // Phase 4: Chain Breaking + if (chain_compression) { + ImpCompressor imp(envi, m, deletedVarDecls, boolConstraints); + LECompressor le(envi, m, deletedVarDecls); + for (auto& item : m) { + imp.trackItem(item); + le.trackItem(item); + } + imp.compress(); + le.compress(); + } + + // Phase 5: handle boolean constraints again (todo: check if we can + // refactor this into a separate function) + // + // Difference to phase 2: constraint argument arrays are actually shortened here if possible + for (unsigned int i = static_cast(boolConstraints.size()); i--;) { + Item* bi = m[boolConstraints[i]]; + if (bi->removed()) continue; + Call* c; + std::vector removedVarDecls; + + if (bi->isa()) { + c = bi->cast()->e()->dyn_cast(); + } else { + c = Expression::dyn_cast(bi->cast()->e()->e()); + } + if (c == NULL) continue; + bool isConjunction = (c->id() == constants().ids.forall); + bool subsumed = false; + for (unsigned int j = 0; j < c->n_args(); j++) { + bool unit = (j == 0 ? isConjunction : !isConjunction); + ArrayLit* al = follow_id(c->arg(j))->cast(); + std::vector compactedAl; + for (unsigned int k = 0; k < al->size(); k++) { + if (Id* ident = (*al)[k]->dyn_cast()) { + if (ident->decl()->ti()->domain()) { + if (!(ident->decl()->ti()->domain() == constants().boollit(unit))) { + subsumed = true; + } + removedVarDecls.push_back(ident->decl()); + } else { + compactedAl.push_back(ident); + } + } else { + if ((*al)[k]->cast()->v() != unit) { + subsumed = true; + } + } + } + if (compactedAl.size() < al->size()) { + c->arg(j, new ArrayLit(al->loc(), compactedAl)); + c->arg(j)->type(Type::varbool(1)); + } + } + if (subsumed) { + if (isConjunction) { + if (bi->isa()) { + env.envi().fail(); + } else { + ArrayLit* al = follow_id(c->arg(0))->cast(); + for (unsigned int j = 0; j < al->size(); j++) { + removedVarDecls.push_back((*al)[j]->cast()->decl()); + } + bi->cast()->e()->ti()->domain(constants().lit_false); + bi->cast()->e()->ti()->setComputedDomain(true); + bi->cast()->e()->e(constants().lit_false); + } + } else { + if (bi->isa()) { + CollectDecls cd(envi.vo, deletedVarDecls, bi); + topDown(cd, bi->cast()->e()); + env.envi().flat_removeItem(bi); + } else { + CollectDecls cd(envi.vo, deletedVarDecls, bi); + topDown(cd, bi->cast()->e()->e()); + bi->cast()->e()->ti()->domain(constants().lit_true); + bi->cast()->e()->ti()->setComputedDomain(true); + bi->cast()->e()->e(constants().lit_true); + } + } + } + for (unsigned int j = 0; j < removedVarDecls.size(); j++) { + if (env.envi().vo.remove(removedVarDecls[j], bi) == 0) { + if ((removedVarDecls[j]->e() == NULL || removedVarDecls[j]->ti()->domain() == NULL || + removedVarDecls[j]->ti()->computedDomain()) && + !isOutput(removedVarDecls[j])) { + deletedVarDecls.push_back(removedVarDecls[j]); + } + } + } + if (VarDeclI* vdi = bi->dyn_cast()) { + if (envi.vo.occurrences(vdi->e()) == 0) { + if ((vdi->e()->e() == NULL || vdi->e()->ti()->domain() == NULL || + vdi->e()->ti()->computedDomain()) && + !isOutput(vdi->e())) { + deletedVarDecls.push_back(vdi->e()); + } + } + } + } + + // Phase 6: remove deleted variables if possible + while (!deletedVarDecls.empty()) { + VarDecl* cur = deletedVarDecls.back(); + deletedVarDecls.pop_back(); + if (envi.vo.occurrences(cur) == 0) { + IdMap::iterator cur_idx = envi.vo.idx.find(cur->id()); + if (cur_idx != envi.vo.idx.end() && !m[cur_idx->second]->removed()) { + if (isOutput(cur)) { + // We have to change the output model if we remove this variable + Expression* val = NULL; + if (cur->type().isbool() && cur->ti()->domain()) { + val = cur->ti()->domain(); + } else if (cur->type().isint()) { + if (cur->e() && cur->e()->isa()) { + val = cur->e(); + } else if (cur->ti()->domain() && cur->ti()->domain()->isa() && + cur->ti()->domain()->cast()->isv()->size() == 1 && + cur->ti()->domain()->cast()->isv()->min() == + cur->ti()->domain()->cast()->isv()->max()) { + val = IntLit::a(cur->ti()->domain()->cast()->isv()->min()); + } + } + if (val) { + // Find corresponding variable in output model and fix it + VarDecl* vd_out = + (*envi.output)[envi.output_vo_flat.find(cur)]->cast()->e(); + vd_out->e(val); + CollectDecls cd(envi.vo, deletedVarDecls, m[cur_idx->second]->cast()); + topDown(cd, cur->e()); + envi.flat_removeItem(cur_idx->second); + } + } else { + CollectDecls cd(envi.vo, deletedVarDecls, m[cur_idx->second]->cast()); + topDown(cd, cur->e()); + envi.flat_removeItem(cur_idx->second); + } + } + } + } + } catch (ModelInconsistent&) { + } +} + +class SubstitutionVisitor : public EVisitor { +protected: + std::vector removed; + Expression* subst(Expression* e) { + if (VarDecl* vd = follow_id_to_decl(e)->dyn_cast()) { + if (vd->type().isbool() && vd->ti()->domain()) { + removed.push_back(vd); + return vd->ti()->domain(); + } + if (vd->type().isint()) { + if (vd->e() && vd->e()->isa()) { + removed.push_back(vd); + return vd->e(); + } + if (vd->ti()->domain() && vd->ti()->domain()->isa() && + vd->ti()->domain()->cast()->isv()->size() == 1 && + vd->ti()->domain()->cast()->isv()->min() == + vd->ti()->domain()->cast()->isv()->max()) { + removed.push_back(vd); + return IntLit::a(vd->ti()->domain()->cast()->isv()->min()); + } + } + } + return e; + } + +public: + /// Visit array literal + void vArrayLit(ArrayLit& al) { + for (unsigned int i = 0; i < al.size(); i++) { + al.set(i, subst(al[i])); + } + } + /// Visit call + void vCall(Call& c) { + for (unsigned int i = 0; i < c.n_args(); i++) { + c.arg(i, subst(c.arg(i))); + } + } + /// Determine whether to enter node + bool enter(Expression* e) { return !e->isa(); } + void remove(EnvI& env, Item* item, std::vector& deletedVarDecls) { + for (unsigned int i = 0; i < removed.size(); i++) { + removed[i]->ann().remove(constants().ann.is_defined_var); + if (env.vo.remove(removed[i], item) == 0) { + if ((removed[i]->e() == NULL || removed[i]->ti()->domain() == NULL || + removed[i]->ti()->computedDomain()) && + !isOutput(removed[i])) { + deletedVarDecls.push_back(removed[i]); + } + } + } + } +}; + +void substituteFixedVars(EnvI& env, Item* ii, std::vector& deletedVarDecls) { + SubstitutionVisitor sv; + if (ConstraintI* ci = ii->dyn_cast()) { + topDown(sv, ci->e()); + for (ExpressionSetIter it = ci->e()->ann().begin(); it != ci->e()->ann().end(); ++it) { + topDown(sv, *it); + } + } else if (VarDeclI* vdi = ii->dyn_cast()) { + topDown(sv, vdi->e()); + for (ExpressionSetIter it = vdi->e()->ann().begin(); it != vdi->e()->ann().end(); ++it) { + topDown(sv, *it); + } + } else { + SolveI* si = ii->cast(); + topDown(sv, si->e()); + for (ExpressionSetIter it = si->ann().begin(); it != si->ann().end(); ++it) { + topDown(sv, *it); + } + } + sv.remove(env, ii, deletedVarDecls); +} + +bool simplifyConstraint(EnvI& env, Item* ii, std::vector& deletedVarDecls, + std::deque& constraintQueue, std::deque& vardeclQueue) { + Expression* con_e; + bool is_true; + bool is_false; + if (ConstraintI* ci = ii->dyn_cast()) { + con_e = ci->e(); + is_true = true; + is_false = false; + } else { + VarDeclI* vdi = ii->cast(); + con_e = vdi->e()->e(); + is_true = (vdi->e()->type().isbool() && vdi->e()->ti()->domain() == constants().lit_true); + is_false = (vdi->e()->type().isbool() && vdi->e()->ti()->domain() == constants().lit_false); + assert(is_true || is_false || !vdi->e()->type().isbool() || vdi->e()->ti()->domain() == NULL); + } + if (Call* c = Expression::dyn_cast(con_e)) { + if (c->id() == constants().ids.int_.eq || c->id() == constants().ids.bool_eq || + c->id() == constants().ids.float_.eq) { + if (is_true && c->arg(0)->isa() && c->arg(1)->isa() && + (c->arg(0)->cast()->decl()->e() == NULL || + c->arg(1)->cast()->decl()->e() == NULL)) { + unify(env, deletedVarDecls, c->arg(0)->cast(), c->arg(1)->cast()); + pushDependentConstraints(env, c->arg(0)->cast(), constraintQueue); + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + env.flat_removeItem(ii); + } else if (c->arg(0)->type().ispar() && c->arg(1)->type().ispar()) { + Expression* e0 = eval_par(env, c->arg(0)); + Expression* e1 = eval_par(env, c->arg(1)); + bool is_equal = Expression::equal(e0, e1); + if ((is_true && is_equal) || (is_false && !is_equal)) { + // do nothing + } else if ((is_true && !is_equal) || (is_false && is_equal)) { + env.fail(); + } else { + VarDeclI* vdi = ii->cast(); + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + vdi->e()->e(constants().boollit(is_equal)); + vdi->e()->ti()->domain(constants().boollit(is_equal)); + vdi->e()->ti()->setComputedDomain(true); + pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); + pushDependentConstraints(env, vdi->e()->id(), constraintQueue); + } + if (ii->isa()) { + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + env.flat_removeItem(ii); + } + } else if (is_true && ((c->arg(0)->isa() && c->arg(1)->type().ispar()) || + (c->arg(1)->isa() && c->arg(0)->type().ispar()))) { + Id* ident = c->arg(0)->isa() ? c->arg(0)->cast() : c->arg(1)->cast(); + Expression* arg = c->arg(0)->isa() ? c->arg(1) : c->arg(0); + bool canRemove = false; + TypeInst* ti = ident->decl()->ti(); + switch (ident->type().bt()) { + case Type::BT_BOOL: + if (ti->domain() == NULL) { + ti->domain(constants().boollit(eval_bool(env, arg))); + ti->setComputedDomain(false); + canRemove = true; + } else { + if (eval_bool(env, ti->domain()) == eval_bool(env, arg)) { + canRemove = true; + } else { + env.fail(); + canRemove = true; + } + } + break; + case Type::BT_INT: { + IntVal d = eval_int(env, arg); + if (ti->domain() == NULL) { + ti->domain(new SetLit(Location().introduce(), IntSetVal::a(d, d))); + ti->setComputedDomain(false); + canRemove = true; + } else { + IntSetVal* isv = eval_intset(env, ti->domain()); + if (isv->contains(d)) { + ident->decl()->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(d, d))); + ident->decl()->ti()->setComputedDomain(false); + canRemove = true; + } else { + env.fail(); + canRemove = true; + } + } + } break; + case Type::BT_FLOAT: { + if (ti->domain() == NULL) { + ti->domain(new BinOp(Location().introduce(), arg, BOT_DOTDOT, arg)); + ti->setComputedDomain(false); + canRemove = true; + } else { + FloatVal value = eval_float(env, arg); + if (LinearTraits::domain_contains(eval_floatset(env, ti->domain()), + value)) { + ti->domain(new BinOp(Location().introduce(), arg, BOT_DOTDOT, arg)); + ti->setComputedDomain(false); + canRemove = true; + } else { + env.fail(); + canRemove = true; + } + } + } break; + default: + break; + } + if (ident->decl()->e() == NULL) { + ident->decl()->e(c->arg(0)->isa() ? c->arg(1) : c->arg(0)); + ti->setComputedDomain(true); + canRemove = true; + } + + if (ident->decl()->e()->isa()) + constraintQueue.push_back((*env.flat())[env.vo.find(ident->decl())]); + pushDependentConstraints(env, ident, constraintQueue); + if (canRemove) { + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + env.flat_removeItem(ii); + } + } + } else if ((is_true || is_false) && c->id() == constants().ids.int_.le && + ((c->arg(0)->isa() && c->arg(1)->type().ispar()) || + (c->arg(1)->isa() && c->arg(0)->type().ispar()))) { + // todo: does this every happen? int_le shouldn't be generated any more + Id* ident = c->arg(0)->isa() ? c->arg(0)->cast() : c->arg(1)->cast(); + Expression* arg = c->arg(0)->isa() ? c->arg(1) : c->arg(0); + IntSetVal* domain = + ident->decl()->ti()->domain() ? eval_intset(env, ident->decl()->ti()->domain()) : NULL; + if (domain) { + BinOpType bot = + c->arg(0)->isa() ? (is_true ? BOT_LQ : BOT_GR) : (is_true ? BOT_GQ : BOT_LE); + IntSetVal* newDomain = LinearTraits::limit_domain(bot, domain, eval_int(env, arg)); + ident->decl()->ti()->domain(new SetLit(Location().introduce(), newDomain)); + ident->decl()->ti()->setComputedDomain(false); + + if (newDomain->min() == newDomain->max()) { + pushDependentConstraints(env, ident, constraintQueue); + } + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + + if (VarDeclI* vdi = ii->dyn_cast()) { + vdi->e()->e(constants().boollit(is_true)); + pushDependentConstraints(env, vdi->e()->id(), constraintQueue); + if (env.vo.occurrences(vdi->e()) == 0) { + if (isOutput(vdi->e())) { + VarDecl* vd_out = + (*env.output)[env.output_vo_flat.find(vdi->e())]->cast()->e(); + vd_out->e(constants().boollit(is_true)); + } + env.flat_removeItem(vdi); + } + } else { + env.flat_removeItem(ii); + } + } + } else if (c->id() == constants().ids.bool2int) { + VarDeclI* vdi = ii->dyn_cast(); + VarDecl* vd; + bool fixed = false; + bool b_val = false; + if (vdi) { + vd = vdi->e(); + } else if (Id* ident = c->arg(1)->dyn_cast()) { + vd = ident->decl(); + } else { + vd = NULL; + } + IntSetVal* vd_dom = NULL; + if (vd) { + if (vd->ti()->domain()) { + vd_dom = eval_intset(env, vd->ti()->domain()); + if (vd_dom->max() < 0 || vd_dom->min() > 1) { + env.fail(); + return true; + } + fixed = vd_dom->min() == vd_dom->max(); + b_val = (vd_dom->min() == 1); + } + } else { + fixed = true; + b_val = (eval_int(env, c->arg(1)) == 1); + } + if (fixed) { + if (c->arg(0)->type().ispar()) { + bool b2i_val = eval_bool(env, c->arg(0)); + if (b2i_val != b_val) { + env.fail(); + } else { + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + env.flat_removeItem(ii); + } + } else { + Id* ident = c->arg(0)->cast(); + TypeInst* ti = ident->decl()->ti(); + if (ti->domain() == NULL) { + ti->domain(constants().boollit(b_val)); + ti->setComputedDomain(false); + } else if (eval_bool(env, ti->domain()) != b_val) { + env.fail(); + } + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + if (vd) { + vd->e(IntLit::a(b_val)); + vd->ti()->setComputedDomain(true); + } + pushDependentConstraints(env, ident, constraintQueue); + if (vdi) { + if (env.vo.occurrences(vd) == 0) { + env.flat_removeItem(vdi); + } + } else { + env.flat_removeItem(ii); + } + } + } else { + IntVal v = -1; + if (BoolLit* bl = c->arg(0)->dyn_cast()) { + v = bl->v() ? 1 : 0; + } else if (Id* ident = c->arg(0)->dyn_cast()) { + if (ident->decl()->ti()->domain()) { + v = eval_bool(env, ident->decl()->ti()->domain()) ? 1 : 0; + } + } + if (v != -1) { + if (vd_dom && !vd_dom->contains(v)) { + env.fail(); + } else { + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + vd->e(IntLit::a(v)); + vd->ti()->domain(new SetLit(Location().introduce(), IntSetVal::a(v, v))); + vd->ti()->setComputedDomain(true); + pushVarDecl(env, env.vo.find(vd), vardeclQueue); + pushDependentConstraints(env, vd->id(), constraintQueue); + } + } + } + + } else { + // General propagation: call a propagator registered for this constraint type + Expression* rewrite = NULL; + GCLock lock; + switch (OptimizeRegistry::registry().process(env, ii, c, rewrite)) { + case OptimizeRegistry::CS_NONE: + return false; + case OptimizeRegistry::CS_OK: + return true; + case OptimizeRegistry::CS_FAILED: + if (is_true) { + env.fail(); + return true; + } else if (is_false) { + if (ii->isa()) { + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + env.flat_removeItem(ii); + } else { + deletedVarDecls.push_back(ii->cast()->e()); + } + return true; + } else { + VarDeclI* vdi = ii->cast(); + vdi->e()->ti()->domain(constants().lit_false); + pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); + return true; + } + case OptimizeRegistry::CS_ENTAILED: + if (is_true) { + if (ii->isa()) { + CollectDecls cd(env.vo, deletedVarDecls, ii); + topDown(cd, c); + env.flat_removeItem(ii); + } else { + deletedVarDecls.push_back(ii->cast()->e()); + } + return true; + } else if (is_false) { + env.fail(); + return true; + } else { + VarDeclI* vdi = ii->cast(); + vdi->e()->ti()->domain(constants().lit_true); + pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); + return true; + } + case OptimizeRegistry::CS_REWRITE: { + std::vector tdv; + CollectDecls cd(env.vo, tdv, ii); + topDown(cd, c); + + CollectOccurrencesE ce(env.vo, ii); + topDown(ce, rewrite); + + for (unsigned int i = 0; i < tdv.size(); i++) { + if (env.vo.occurrences(tdv[i]) == 0) deletedVarDecls.push_back(tdv[i]); + } + + assert(rewrite != NULL); + if (ConstraintI* ci = ii->dyn_cast()) { + ci->e(rewrite); + constraintQueue.push_back(ii); + } else { + VarDeclI* vdi = ii->cast(); + vdi->e()->e(rewrite); + if (vdi->e()->e() && vdi->e()->e()->isa() && vdi->e()->type().dim() == 0) { + Id* id1 = vdi->e()->e()->cast(); + vdi->e()->e(NULL); + unify(env, deletedVarDecls, vdi->e()->id(), id1); + pushDependentConstraints(env, id1, constraintQueue); + } + if (vdi->e()->e() && vdi->e()->e()->type().ispar() && vdi->e()->ti()->domain()) { + if (vdi->e()->e()->type().isint()) { + IntVal iv = eval_int(env, vdi->e()->e()); + IntSetVal* dom = eval_intset(env, vdi->e()->ti()->domain()); + if (!dom->contains(iv)) env.fail(); + } else if (vdi->e()->e()->type().isintset()) { + IntSetVal* isv = eval_intset(env, vdi->e()->e()); + IntSetVal* dom = eval_intset(env, vdi->e()->ti()->domain()); + IntSetRanges isv_r(isv); + IntSetRanges dom_r(dom); + if (!Ranges::subset(isv_r, dom_r)) env.fail(); + } else if (vdi->e()->e()->type().isfloat()) { + FloatVal fv = eval_float(env, vdi->e()->e()); + FloatSetVal* dom = eval_floatset(env, vdi->e()->ti()->domain()); + if (!dom->contains(fv)) env.fail(); + } + } + if (vdi->e()->ti()->type() != Type::varbool() || vdi->e()->ti()->domain() == NULL) + pushVarDecl(env, vdi, env.vo.find(vdi->e()), vardeclQueue); + + if (is_true) { + constraintQueue.push_back(ii); + } + } + return true; + } + } + } + } + return false; +} + +int boolState(EnvI& env, Expression* e) { + if (e->type().ispar()) { + return eval_bool(env, e); + } else { + Id* id = e->cast(); + if (id->decl()->ti()->domain() == NULL) return 2; + return id->decl()->ti()->domain() == constants().lit_true; + } +} + +int decrementNonFixedVars(std::unordered_map& nonFixedLiteralCount, Call* c) { + std::unordered_map::iterator it = nonFixedLiteralCount.find(c); + if (it == nonFixedLiteralCount.end()) { + int nonFixedVars = 0; + for (unsigned int i = 0; i < c->n_args(); i++) { + ArrayLit* al = follow_id(c->arg(i))->cast(); + nonFixedVars += al->size(); + for (unsigned int j = al->size(); j--;) { + if ((*al)[j]->type().ispar()) nonFixedVars--; + } + } + nonFixedVars--; // for the identifier we're currently processing + nonFixedLiteralCount.insert(std::make_pair(c, nonFixedVars)); + return nonFixedVars; + } else { + it->second--; + return it->second; + } +} + +void simplifyBoolConstraint(EnvI& env, Item* ii, VarDecl* vd, bool& remove, + std::deque& vardeclQueue, std::deque& constraintQueue, + std::vector& toRemove, + std::unordered_map& nonFixedLiteralCount) { + if (ii->isa()) { + remove = false; + return; + } + bool isTrue = vd->ti()->domain() == constants().lit_true; + Expression* e = NULL; + ConstraintI* ci = ii->dyn_cast(); + VarDeclI* vdi = ii->dyn_cast(); + if (ci) { + e = ci->e(); + } else if (vdi) { + e = vdi->e()->e(); + if (e == NULL) return; + if (Id* id = e->dyn_cast()) { + assert(id->decl() == vd); + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().boollit(isTrue)); + vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); + } else if (id->decl()->ti()->domain() == constants().boollit(!isTrue)) { + env.fail(); + remove = false; + } + return; + } + } + if (Id* ident = e->dyn_cast()) { + assert(ident->decl() == vd); + return; + } + if (e->isa()) { + if (e == constants().lit_true && ci) { + toRemove.push_back(ci); + } + return; + } + Call* c = e->cast(); + if (c->id() == constants().ids.bool_eq) { + Expression* b0 = c->arg(0); + Expression* b1 = c->arg(1); + int b0s = boolState(env, b0); + int b1s = boolState(env, b1); + if (b0s == 2) { + std::swap(b0, b1); + std::swap(b0s, b1s); + } + assert(b0s != 2); + if (ci || vdi->e()->ti()->domain() == constants().lit_true) { + if (b0s != b1s) { + if (b1s == 2) { + b1->cast()->decl()->ti()->domain(constants().boollit(isTrue)); + vardeclQueue.push_back(env.vo.idx.find(b1->cast()->decl()->id())->second); + if (ci) toRemove.push_back(ci); + } else { + env.fail(); + remove = false; + } + } else { + if (ci) toRemove.push_back(ci); + } + } else if (vdi && vdi->e()->ti()->domain() == constants().lit_false) { + if (b0s != b1s) { + if (b1s == 2) { + b1->cast()->decl()->ti()->domain(constants().boollit(isTrue)); + vardeclQueue.push_back(env.vo.idx.find(b1->cast()->decl()->id())->second); + } + } else { + env.fail(); + remove = false; + } + } else { + remove = false; + } + } else if (c->id() == constants().ids.forall || c->id() == constants().ids.exists || + c->id() == constants().ids.clause) { + if (isTrue && c->id() == constants().ids.exists) { + if (ci) { + toRemove.push_back(ci); + } else { + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().lit_true); + vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); + } else if (vdi->e()->ti()->domain() != constants().lit_true) { + env.fail(); + vdi->e()->e(constants().lit_true); + } + } + } else if (!isTrue && c->id() == constants().ids.forall) { + if (ci) { + env.fail(); + toRemove.push_back(ci); + } else { + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().lit_false); + vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); + } else if (vdi->e()->ti()->domain() != constants().lit_false) { + env.fail(); + vdi->e()->e(constants().lit_false); + } + } + } else { + int nonfixed = decrementNonFixedVars(nonFixedLiteralCount, c); + bool isConjunction = (c->id() == constants().ids.forall); + assert(nonfixed >= 0); + if (nonfixed <= 1) { + bool subsumed = false; + int nonfixed_i = -1; + int nonfixed_j = -1; + int realNonFixed = 0; + for (unsigned int i = 0; i < c->n_args(); i++) { + bool unit = (i == 0 ? isConjunction : !isConjunction); + ArrayLit* al = follow_id(c->arg(i))->cast(); + realNonFixed += al->size(); + for (unsigned int j = al->size(); j--;) { + if ((*al)[j]->type().ispar() || (*al)[j]->cast()->decl()->ti()->domain()) + realNonFixed--; + if ((*al)[j]->type().ispar() && eval_bool(env, (*al)[j]) != unit) { + subsumed = true; + i = 2; // break out of outer loop + break; + } else if (Id* id = (*al)[j]->dyn_cast()) { + if (id->decl()->ti()->domain()) { + bool idv = (id->decl()->ti()->domain() == constants().lit_true); + if (unit != idv) { + subsumed = true; + i = 2; // break out of outer loop + break; + } + } else { + nonfixed_i = i; + nonfixed_j = j; + } + } + } + } + + if (subsumed) { + if (ci) { + if (isConjunction) { + env.fail(); + ci->e(constants().lit_false); + } else { + toRemove.push_back(ci); + } + } else { + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().boollit(!isConjunction)); + vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); + } else if (vdi->e()->ti()->domain() != constants().boollit(!isConjunction)) { + env.fail(); + vdi->e()->e(constants().boollit(!isConjunction)); + } + } + } else if (realNonFixed == 0) { + if (ci) { + if (isConjunction) { + toRemove.push_back(ci); + } else { + env.fail(); + ci->e(constants().lit_false); + } + } else { + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().boollit(isConjunction)); + vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); + } else if (vdi->e()->ti()->domain() != constants().boollit(isConjunction)) { + env.fail(); + vdi->e()->e(constants().boollit(isConjunction)); + } + toRemove.push_back(vdi); + } + } else { + // not subsumed, nonfixed==1 + assert(nonfixed_i != -1); + ArrayLit* al = follow_id(c->arg(nonfixed_i))->cast(); + Id* id = (*al)[nonfixed_j]->cast(); + if (ci || vdi->e()->ti()->domain()) { + bool result = nonfixed_i == 0; + if (vdi && vdi->e()->ti()->domain() == constants().lit_false) result = !result; + VarDecl* vd = id->decl(); + if (vd->ti()->domain() == NULL) { + vd->ti()->domain(constants().boollit(result)); + vardeclQueue.push_back(env.vo.idx.find(vd->id())->second); + } else if (vd->ti()->domain() != constants().boollit(result)) { + env.fail(); + vd->e(constants().lit_true); + } + } else { + remove = false; + } + } + + } else if (c->id() == constants().ids.clause) { + int posOrNeg = isTrue ? 0 : 1; + ArrayLit* al = follow_id(c->arg(posOrNeg))->cast(); + ArrayLit* al_other = follow_id(c->arg(1 - posOrNeg))->cast(); + + if (ci && al->size() == 1 && (*al)[0] != vd->id() && al_other->size() == 1) { + // simple implication + assert((*al_other)[0] == vd->id()); + if (ci) { + if ((*al)[0]->type().ispar()) { + if (eval_bool(env, (*al)[0]) == isTrue) { + toRemove.push_back(ci); + } else { + env.fail(); + remove = false; + } + } else { + Id* id = (*al)[0]->cast(); + if (id->decl()->ti()->domain() == NULL) { + id->decl()->ti()->domain(constants().boollit(isTrue)); + vardeclQueue.push_back(env.vo.idx.find(id->decl()->id())->second); + } else { + if (id->decl()->ti()->domain() == constants().boollit(isTrue)) { + toRemove.push_back(ci); + } else { + env.fail(); + remove = false; + } + } + } + } + } else { + // proper clause + for (unsigned int i = 0; i < al->size(); i++) { + if ((*al)[i] == vd->id()) { + if (ci) { + toRemove.push_back(ci); + } else { + if (vdi->e()->ti()->domain() == NULL) { + vdi->e()->ti()->domain(constants().lit_true); + vardeclQueue.push_back(env.vo.idx.find(vdi->e()->id())->second); + } else if (vdi->e()->ti()->domain() != constants().lit_true) { + env.fail(); + vdi->e()->e(constants().lit_true); + } + } + break; + } + } + } + } + } + } else { + remove = false; + } +} + +} // namespace MiniZinc diff --git a/lib/optimize_constraints.cpp b/lib/optimize_constraints.cpp new file mode 100644 index 0000000..8f3c86d --- /dev/null +++ b/lib/optimize_constraints.cpp @@ -0,0 +1,376 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +namespace MiniZinc { + +void OptimizeRegistry::reg(const MiniZinc::ASTString& call, optimizer opt) { + _m.insert(std::make_pair(call, opt)); +} + +OptimizeRegistry::ConstraintStatus OptimizeRegistry::process(EnvI& env, MiniZinc::Item* i, + MiniZinc::Call* c, + Expression*& rewrite) { + ASTStringMap::t::iterator it = _m.find(c->id()); + if (it != _m.end()) { + return it->second(env, i, c, rewrite); + } + return CS_NONE; +} + +OptimizeRegistry& OptimizeRegistry::registry(void) { + static OptimizeRegistry reg; + return reg; +} + +namespace Optimizers { + +OptimizeRegistry::ConstraintStatus o_linear(EnvI& env, Item* ii, Call* c, Expression*& rewrite) { + ArrayLit* al_c = eval_array_lit(env, c->arg(0)); + std::vector coeffs(al_c->size()); + for (unsigned int i = 0; i < al_c->size(); i++) { + coeffs[i] = eval_int(env, (*al_c)[i]); + } + ArrayLit* al_x = eval_array_lit(env, c->arg(1)); + std::vector x(al_x->size()); + for (unsigned int i = 0; i < al_x->size(); i++) { + x[i] = (*al_x)[i]; + } + IntVal d = 0; + simplify_lin(coeffs, x, d); + if (coeffs.size() == 0) { + bool failed; + if (c->id() == constants().ids.int_.lin_le) { + failed = (d > eval_int(env, c->arg(2))); + } else if (c->id() == constants().ids.int_.lin_eq) { + failed = (d != eval_int(env, c->arg(2))); + } else { + failed = (d == eval_int(env, c->arg(2))); + } + if (failed) { + return OptimizeRegistry::CS_FAILED; + } else { + return OptimizeRegistry::CS_ENTAILED; + } + } else if (coeffs.size() == 1 && + (ii->isa() || + ii->cast()->e()->ti()->domain() == constants().lit_true)) { + VarDecl* vd = x[0]()->cast()->decl(); + IntSetVal* domain = vd->ti()->domain() ? eval_intset(env, vd->ti()->domain()) : NULL; + if (c->id() == constants().ids.int_.lin_eq) { + IntVal rd = eval_int(env, c->arg(2)) - d; + if (rd % coeffs[0] == 0) { + IntVal nd = rd / coeffs[0]; + if (domain && !domain->contains(nd)) return OptimizeRegistry::CS_FAILED; + std::vector args(2); + args[0] = x[0](); + args[1] = IntLit::a(nd); + Call* c = new Call(Location(), constants().ids.int_.eq, args); + c->type(Type::varbool()); + rewrite = c; + return OptimizeRegistry::CS_REWRITE; + } else { + return OptimizeRegistry::CS_FAILED; + } + } else if (c->id() == constants().ids.int_.lin_le) { + IntVal ac = std::abs(coeffs[0]); + IntVal rd = eval_int(env, c->arg(2)) - d; + IntVal ad = std::abs(rd); + IntVal nd; + if (ad % ac == 0) { + nd = rd / coeffs[0]; + } else { + double nd_d = static_cast(ad.toInt()) / static_cast(ac.toInt()); + if (coeffs[0] >= 0 && rd >= 0) { + nd = static_cast(std::floor(nd_d)); + } else if (rd >= 0) { + nd = -static_cast(std::floor(nd_d)); + } else if (coeffs[0] >= 0) { + nd = -static_cast(std::ceil(nd_d)); + } else { + nd = static_cast(std::ceil(nd_d)); + } + } + bool swapSign = coeffs[0] < 0; + if (domain) { + if (swapSign) { + if (domain->max() < nd) { + return OptimizeRegistry::CS_FAILED; + } else if (domain->min() >= nd) + return OptimizeRegistry::CS_ENTAILED; + } else { + if (domain->min() > nd) { + return OptimizeRegistry::CS_FAILED; + } else if (domain->max() <= nd) + return OptimizeRegistry::CS_ENTAILED; + } + std::vector args(2); + args[0] = x[0](); + args[1] = IntLit::a(nd); + if (swapSign) std::swap(args[0], args[1]); + Call* nc = new Call(Location(), constants().ids.int_.le, args); + nc->type(Type::varbool()); + rewrite = nc; + return OptimizeRegistry::CS_REWRITE; + } + } + } else if (c->id() == constants().ids.int_.lin_eq && coeffs.size() == 2 && + ((coeffs[0] == 1 && coeffs[1] == -1) || (coeffs[1] == 1 && coeffs[0] == -1)) && + eval_int(env, c->arg(2)) - d == 0) { + std::vector args(2); + args[0] = x[0](); + args[1] = x[1](); + Call* c = new Call(Location(), constants().ids.int_.eq, args); + rewrite = c; + return OptimizeRegistry::CS_REWRITE; + } + if (coeffs.size() < al_c->size()) { + std::vector coeffs_e(coeffs.size()); + std::vector x_e(coeffs.size()); + for (unsigned int i = 0; i < coeffs.size(); i++) { + coeffs_e[i] = IntLit::a(coeffs[i]); + x_e[i] = x[i](); + } + ArrayLit* al_c_new = new ArrayLit(al_c->loc(), coeffs_e); + al_c_new->type(Type::parint(1)); + ArrayLit* al_x_new = new ArrayLit(al_x->loc(), x_e); + al_x_new->type(al_x->type()); + + std::vector args(3); + args[0] = al_c_new; + args[1] = al_x_new; + args[2] = IntLit::a(eval_int(env, c->arg(2)) - d); + Call* nc = new Call(Location(), c->id(), args); + nc->type(Type::varbool()); + for (ExpressionSetIter it = c->ann().begin(); it != c->ann().end(); ++it) { + nc->addAnnotation(*it); + } + + rewrite = nc; + return OptimizeRegistry::CS_REWRITE; + } + return OptimizeRegistry::CS_OK; +} + +OptimizeRegistry::ConstraintStatus o_lin_exp(EnvI& env, Item* i, Call* c, Expression*& rewrite) { + if (c->type().isint()) { + ArrayLit* al_c = eval_array_lit(env, c->arg(0)); + std::vector coeffs(al_c->size()); + for (unsigned int i = 0; i < al_c->size(); i++) { + coeffs[i] = eval_int(env, (*al_c)[i]); + } + ArrayLit* al_x = eval_array_lit(env, c->arg(1)); + std::vector x(al_x->size()); + for (unsigned int i = 0; i < al_x->size(); i++) { + x[i] = (*al_x)[i]; + } + IntVal d = eval_int(env, c->arg(2)); + simplify_lin(coeffs, x, d); + if (coeffs.size() == 0) { + rewrite = IntLit::a(d); + return OptimizeRegistry::CS_REWRITE; + } else if (coeffs.size() < al_c->size()) { + if (coeffs.size() == 1 && coeffs[0] == 1 && d == 0) { + rewrite = x[0](); + return OptimizeRegistry::CS_REWRITE; + } + + std::vector coeffs_e(coeffs.size()); + std::vector x_e(coeffs.size()); + for (unsigned int i = 0; i < coeffs.size(); i++) { + coeffs_e[i] = IntLit::a(coeffs[i]); + x_e[i] = x[i](); + } + ArrayLit* al_c_new = new ArrayLit(al_c->loc(), coeffs_e); + al_c_new->type(Type::parint(1)); + ArrayLit* al_x_new = new ArrayLit(al_x->loc(), x_e); + al_x_new->type(al_x->type()); + + std::vector args(3); + args[0] = al_c_new; + args[1] = al_x_new; + args[2] = IntLit::a(d); + Call* nc = new Call(Location(), c->id(), args); + nc->type(c->type()); + for (ExpressionSetIter it = c->ann().begin(); it != c->ann().end(); ++it) { + nc->addAnnotation(*it); + } + rewrite = nc; + return OptimizeRegistry::CS_REWRITE; + } + } + return OptimizeRegistry::CS_OK; +} + +OptimizeRegistry::ConstraintStatus o_element(EnvI& env, Item* i, Call* c, Expression*& rewrite) { + if (c->arg(0)->isa()) { + IntVal idx = eval_int(env, c->arg(0)); + ArrayLit* al = eval_array_lit(env, c->arg(1)); + if (idx < 1 || idx > al->size()) { + return OptimizeRegistry::CS_FAILED; + } + Expression* result = (*al)[static_cast(idx.toInt()) - 1]; + std::vector args(2); + args[0] = result; + args[1] = c->arg(2); + Call* eq = new Call(Location(), constants().ids.int_.eq, args); + rewrite = eq; + return OptimizeRegistry::CS_REWRITE; + } + return OptimizeRegistry::CS_OK; +} + +OptimizeRegistry::ConstraintStatus o_clause(EnvI& env, Item* i, Call* c, Expression*& rewrite) { + std::vector pos; + std::vector neg; + ArrayLit* al_pos = eval_array_lit(env, c->arg(0)); + for (unsigned int i = 0; i < al_pos->size(); i++) { + if (Id* ident = (*al_pos)[i]->dyn_cast()) { + if (ident->decl()->ti()->domain() == NULL) pos.push_back(ident->decl()); + } + } + ArrayLit* al_neg = eval_array_lit(env, c->arg(1)); + for (unsigned int i = 0; i < al_neg->size(); i++) { + if (Id* ident = (*al_neg)[i]->dyn_cast()) { + if (ident->decl()->ti()->domain() == NULL) neg.push_back(ident->decl()); + } + } + bool subsumed = false; + if (pos.size() > 0 && neg.size() > 0) { + std::sort(pos.begin(), pos.end()); + std::sort(neg.begin(), neg.end()); + unsigned int ix = 0; + unsigned int iy = 0; + for (;;) { + if (pos[ix] == neg[iy]) { + subsumed = true; + break; + } + if (pos[ix] < neg[iy]) { + ix++; + } else { + iy++; + } + if (ix == pos.size() || iy == neg.size()) break; + } + } + if (subsumed) { + return OptimizeRegistry::CS_ENTAILED; + } else { + return OptimizeRegistry::CS_OK; + } +} + +OptimizeRegistry::ConstraintStatus o_div(EnvI& env, Item* i, Call* c, Expression*& rewrite) { + if (c->arg(1)->type().ispar()) { + IntVal c1v = eval_int(env, c->arg(1)); + if (c->arg(0)->type().ispar() && c->n_args() == 3 && c->arg(2)->type().ispar()) { + IntVal c0v = eval_int(env, c->arg(0)); + IntVal c2v = eval_int(env, c->arg(2)); + return (c0v / c1v == c2v) ? OptimizeRegistry::CS_ENTAILED : OptimizeRegistry::CS_FAILED; + } + } + return OptimizeRegistry::CS_OK; +} + +OptimizeRegistry::ConstraintStatus o_set_in(EnvI& env, Item* i, Call* c, Expression*& rewrite) { + if (c->arg(1)->type().ispar()) { + if (c->arg(0)->type().ispar()) { + IntSetVal* isv = eval_intset(env, c->arg(1)); + return isv->contains(eval_int(env, c->arg(0))) ? OptimizeRegistry::CS_ENTAILED + : OptimizeRegistry::CS_FAILED; + } else if (Id* ident = c->arg(0)->dyn_cast()) { + VarDecl* vd = ident->decl(); + IntSetVal* isv = eval_intset(env, c->arg(1)); + if (vd->ti()->domain()) { + IntSetVal* dom = eval_intset(env, vd->ti()->domain()); + { + IntSetRanges isv_r(isv); + IntSetRanges dom_r(dom); + if (Ranges::subset(dom_r, isv_r)) return OptimizeRegistry::CS_ENTAILED; + } + { + IntSetRanges isv_r(isv); + IntSetRanges dom_r(dom); + if (Ranges::disjoint(dom_r, isv_r)) return OptimizeRegistry::CS_FAILED; + } + } else if (isv->min() == isv->max()) { + std::vector args(2); + args[0] = vd->id(); + args[1] = IntLit::a(isv->min()); + Call* eq = new Call(Location(), constants().ids.int_.eq, args); + rewrite = eq; + return OptimizeRegistry::CS_REWRITE; + } + } + } + return OptimizeRegistry::CS_OK; +} + +OptimizeRegistry::ConstraintStatus o_int_ne(EnvI& env, Item* i, Call* c, Expression*& rewrite) { + Expression* e0 = c->arg(0); + Expression* e1 = c->arg(1); + if (e0->type().ispar() && e1->type().ispar()) { + return eval_int(env, e0) != eval_int(env, e1) ? OptimizeRegistry::CS_ENTAILED + : OptimizeRegistry::CS_FAILED; + } + if (e1->isa()) { + std::swap(e0, e1); + } + if (Id* ident = e0->dyn_cast()) { + if (e1->type().ispar()) { + if (ident->decl()->ti()->domain()) { + IntVal e1v = eval_int(env, e1); + IntSetVal* isv = eval_intset(env, ident->decl()->ti()->domain()); + if (!isv->contains(e1v)) return OptimizeRegistry::CS_ENTAILED; + if (e1v == isv->min() && e1v == isv->max()) return OptimizeRegistry::CS_FAILED; + } + } + } + + return OptimizeRegistry::CS_OK; +} + +class Register { +private: + Model* _keepAliveModel; + +public: + Register(void) { + GCLock lock; + _keepAliveModel = new Model; + ASTString id_element("array_int_element"); + ASTString id_var_element("array_var_int_element"); + std::vector e; + e.push_back(new StringLit(Location(), id_element)); + e.push_back(new StringLit(Location(), id_var_element)); + _keepAliveModel->addItem(new ConstraintI(Location(), new ArrayLit(Location(), e))); + OptimizeRegistry::registry().reg(constants().ids.int_.lin_eq, o_linear); + OptimizeRegistry::registry().reg(constants().ids.int_.lin_le, o_linear); + OptimizeRegistry::registry().reg(constants().ids.int_.lin_ne, o_linear); + OptimizeRegistry::registry().reg(constants().ids.int_.div, o_div); + OptimizeRegistry::registry().reg(id_element, o_element); + OptimizeRegistry::registry().reg(constants().ids.lin_exp, o_lin_exp); + OptimizeRegistry::registry().reg(id_var_element, o_element); + OptimizeRegistry::registry().reg(constants().ids.clause, o_clause); + OptimizeRegistry::registry().reg(constants().ids.bool_clause, o_clause); + OptimizeRegistry::registry().reg(constants().ids.set_in, o_set_in); + OptimizeRegistry::registry().reg(constants().ids.int_.ne, o_int_ne); + } + ~Register(void) { delete _keepAliveModel; } +} _r; + +} // namespace Optimizers + +} // namespace MiniZinc diff --git a/lib/options.cpp b/lib/options.cpp new file mode 100644 index 0000000..a5a706d --- /dev/null +++ b/lib/options.cpp @@ -0,0 +1,174 @@ +/* -*- mode: C++; c-basic-offOptions::set: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include + +namespace MiniZinc { + +Expression* Options::getParam(const std::string& name) const { + std::unordered_map::const_iterator it = _options.find(name); + if (it == _options.end()) { + std::stringstream ss; + ss << "Could not find option: \"" << name << "\"." << std::endl; + throw InternalError(ss.str()); + } + return (it->second)(); +} + +void Options::setIntParam(const std::string& name, KeepAlive ka) { + Expression* e = ka(); + if (e && e->type().ispar() && e->type().isint()) { + _options[name] = ka; + } else { + std::stringstream ss; + ss << "For option: " << name << " expected Par Int, received " << e->type().nonEnumToString() + << std::endl; + throw InternalError(ss.str()); + } +} +void Options::setFloatParam(const std::string& name, KeepAlive ka) { + Expression* e = ka(); + if (e && e->type().ispar() && e->type().isfloat()) { + _options[name] = ka; + } else { + std::stringstream ss; + ss << "For option: " << name << " expected Par Float, received " << e->type().nonEnumToString() + << std::endl; + throw InternalError(ss.str()); + } +} +void Options::setBoolParam(const std::string& name, KeepAlive ka) { + Expression* e = ka(); + if (e && e->type().ispar() && e->type().isbool()) { + _options[name] = ka; + } else { + std::stringstream ss; + ss << "For option: " << name << " expected Par Bool, received " << e->type().nonEnumToString() + << std::endl; + throw InternalError(ss.str()); + } +} +void Options::setStringParam(const std::string& name, KeepAlive ka) { + Expression* e = ka(); + if (e && e->type().ispar() && e->type().isstring()) { + _options[name] = ka; + } else { + std::stringstream ss; + ss << "For option: " << name << " expected Par String, received " << e->type().nonEnumToString() + << std::endl; + throw InternalError(ss.str()); + } +} + +void Options::setIntParam(const std::string& name, long long int e) { + GCLock lock; + IntLit* il = IntLit::a(e); + KeepAlive ka(il); + + setIntParam(name, ka); +}; +void Options::setFloatParam(const std::string& name, double e) { + GCLock lock; + FloatLit* fl = FloatLit::a(e); + KeepAlive ka(fl); + + setFloatParam(name, ka); +} +void Options::setBoolParam(const std::string& name, bool e) { + KeepAlive ka(constants().boollit(e)); + setBoolParam(name, ka); +} +void Options::setStringParam(const std::string& name, std::string e) { + GCLock lock; + StringLit* sl = new StringLit(Location(), e); + KeepAlive ka(sl); + + setStringParam(name, ka); +} + +long long int Options::getIntParam(const std::string& name) const { + if (IntLit* il = getParam(name)->dyn_cast()) { + return il->v().toInt(); + } else { + std::stringstream ss; + ss << "Option: \"" << name << "\" is not Par Int" << std::endl; + throw InternalError(ss.str()); + } +} +long long int Options::getIntParam(const std::string& name, long long int def) const { + if (hasParam(name)) { + if (IntLit* il = getParam(name)->dyn_cast()) { + return il->v().toInt(); + } + } + return def; +} +double Options::getFloatParam(const std::string& name) const { + if (FloatLit* fl = getParam(name)->dyn_cast()) { + return fl->v().toDouble(); + } else { + std::stringstream ss; + ss << "Option: \"" << name << "\" is not Par Float" << std::endl; + throw InternalError(ss.str()); + } +} +double Options::getFloatParam(const std::string& name, double def) const { + if (hasParam(name)) { + if (FloatLit* fl = getParam(name)->dyn_cast()) { + return fl->v().toDouble(); + } + } + return def; +} +bool Options::getBoolParam(const std::string& name) const { + if (BoolLit* bl = getParam(name)->dyn_cast()) { + return bl->v(); + } else { + std::stringstream ss; + ss << "Option: \"" << name << "\" is not Par Bool" << std::endl; + throw InternalError(ss.str()); + } +} +bool Options::getBoolParam(const std::string& name, bool def) const { + if (hasParam(name)) { + if (BoolLit* bl = getParam(name)->dyn_cast()) { + return bl->v(); + } + } + return def; +} +std::string Options::getStringParam(const std::string& name) const { + if (StringLit* sl = getParam(name)->dyn_cast()) { + return sl->v().str(); + } else { + std::stringstream ss; + ss << "Option: \"" << name << "\" is not Par String" << std::endl; + throw InternalError(ss.str()); + } +} +std::string Options::getStringParam(const std::string& name, std::string def) const { + if (hasParam(name)) { + if (StringLit* sl = getParam(name)->dyn_cast()) { + return sl->v().str(); + } + } + return def; +} +bool Options::hasParam(const std::string& name) const { + return _options.find(name) != _options.end(); +} +std::ostream& Options::dump(std::ostream& os) { + for (auto& it : _options) os << it.first << ':' << it.second() << ' '; + return os; +} +} // namespace MiniZinc diff --git a/lib/output.cpp b/lib/output.cpp new file mode 100644 index 0000000..bc344bf --- /dev/null +++ b/lib/output.cpp @@ -0,0 +1,1133 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +void outputVarDecls(EnvI& env, Item* ci, Expression* e); + +bool cannotUseRHSForOutput(EnvI& env, Expression* e, + std::unordered_set& seen_functions) { + if (e == NULL) return true; + + class V : public EVisitor { + public: + EnvI& env; + std::unordered_set& seen_functions; + bool success; + V(EnvI& env0, std::unordered_set& seen_functions0) + : env(env0), seen_functions(seen_functions0), success(true) {} + /// Visit anonymous variable + void vAnonVar(const AnonVar&) { success = false; } + /// Visit array literal + void vArrayLit(const ArrayLit&) {} + /// Visit array access + void vArrayAccess(const ArrayAccess&) {} + /// Visit array comprehension + void vComprehension(const Comprehension&) {} + /// Visit if-then-else + void vITE(const ITE&) {} + /// Visit binary operator + void vBinOp(const BinOp&) {} + /// Visit unary operator + void vUnOp(const UnOp&) {} + /// Visit call + void vCall(Call& c) { + std::vector tv(c.n_args()); + for (unsigned int i = c.n_args(); i--;) { + tv[i] = c.arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = env.output->matchFn(env, c.id(), tv, false); + Type t; + if (decl == NULL) { + FunctionI* origdecl = env.model->matchFn(env, c.id(), tv, false); + if (origdecl == NULL) { + throw FlatteningError( + env, c.loc(), "function " + c.id().str() + " is used in output, par version needed"); + } + bool seen = (seen_functions.find(origdecl) != seen_functions.end()); + if (seen) { + success = false; + } else { + seen_functions.insert(origdecl); + if (origdecl->e() && cannotUseRHSForOutput(env, origdecl->e(), seen_functions)) { + success = false; + } else { + if (!origdecl->from_stdlib()) { + decl = copy(env, env.cmap, origdecl)->cast(); + CollectOccurrencesE ce(env.output_vo, decl); + topDown(ce, decl->e()); + topDown(ce, decl->ti()); + for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]); + env.output->registerFn(env, decl); + env.output->addItem(decl); + outputVarDecls(env, origdecl, decl->e()); + outputVarDecls(env, origdecl, decl->ti()); + } else { + decl = origdecl; + } + c.decl(decl); + } + } + } + if (success) { + t = decl->rtype(env, tv, false); + if (!t.ispar()) success = false; + } + } + void vId(const Id& id) {} + /// Visit let + void vLet(const Let&) { success = false; } + /// Visit variable declaration + void vVarDecl(const VarDecl& vd) {} + /// Visit type inst + void vTypeInst(const TypeInst&) {} + /// Visit TIId + void vTIId(const TIId&) {} + /// Determine whether to enter node + bool enter(Expression* e) { return success; } + } _v(env, seen_functions); + topDown(_v, e); + + return !_v.success; +} + +bool cannotUseRHSForOutput(EnvI& env, Expression* e) { + std::unordered_set seen_functions; + return cannotUseRHSForOutput(env, e, seen_functions); +} + +void removeIsOutput(VarDecl* vd) { + if (vd == NULL) return; + vd->ann().remove(constants().ann.output_var); + vd->ann().removeCall(constants().ann.output_array); +} + +void copyOutput(EnvI& e) { + struct CopyOutput : public EVisitor { + EnvI& env; + CopyOutput(EnvI& env0) : env(env0) {} + void vId(Id& _id) { _id.decl(_id.decl()->flat()); } + void vCall(Call& c) { + std::vector tv(c.n_args()); + for (unsigned int i = c.n_args(); i--;) { + tv[i] = c.arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = c.decl(); + if (!decl->from_stdlib()) { + env.flat_addItem(decl); + } + } + }; + + if (OutputI* oi = e.model->outputItem()) { + GCLock lock; + OutputI* noi = copy(e, oi)->cast(); + CopyOutput co(e); + topDown(co, noi->e()); + e.flat_addItem(noi); + } +} + +void cleanupOutput(EnvI& env) { + for (unsigned int i = 0; i < env.output->size(); i++) { + if (VarDeclI* vdi = (*env.output)[i]->dyn_cast()) { + vdi->e()->flat(NULL); + } + } +} + +void makePar(EnvI& env, Expression* e) { + class OutputJSON : public EVisitor { + public: + EnvI& env; + OutputJSON(EnvI& env0) : env(env0) {} + void vCall(Call& c) { + if (c.id() == "outputJSON") { + bool outputObjective = (c.n_args() == 1 && eval_bool(env, c.arg(0))); + c.id(ASTString("array1d")); + Expression* json = copy(env, env.cmap, createJSONOutput(env, outputObjective, false)); + std::vector new_args({json}); + new_args[0]->type(Type::parstring(1)); + c.args(new_args); + } + } + } _outputJSON(env); + topDown(_outputJSON, e); + class Par : public EVisitor { + public: + /// Visit variable declaration + void vVarDecl(VarDecl& vd) { vd.ti()->type(vd.type()); } + /// Determine whether to enter node + bool enter(Expression* e) { + Type t = e->type(); + t.ti(Type::TI_PAR); + e->type(t); + return true; + } + } _par; + topDown(_par, e); + class Decls : public EVisitor { + protected: + static std::string createEnumToStringName(Id* ident, std::string prefix) { + std::string name = ident->str().str(); + if (name[0] == '\'') { + name = "'" + prefix + name.substr(1); + } else { + name = prefix + name; + } + return name; + } + + public: + EnvI& env; + Decls(EnvI& env0) : env(env0) {} + void vCall(Call& c) { + if (c.id() == "format" || c.id() == "show" || c.id() == "showDzn" || c.id() == "showJSON") { + int enumId = c.arg(c.n_args() - 1)->type().enumId(); + if (enumId != 0 && c.arg(c.n_args() - 1)->type().dim() != 0) { + const std::vector& enumIds = env.getArrayEnum(enumId); + enumId = enumIds[enumIds.size() - 1]; + } + if (enumId > 0) { + Id* ti_id = env.getEnum(enumId)->e()->id(); + GCLock lock; + std::vector args(3); + args[0] = c.arg(c.n_args() - 1); + if (args[0]->type().dim() > 1) { + std::vector a1dargs(1); + a1dargs[0] = args[0]; + Call* array1d = new Call(Location().introduce(), ASTString("array1d"), a1dargs); + Type array1dt = args[0]->type(); + array1dt.dim(1); + array1d->type(array1dt); + args[0] = array1d; + } + args[1] = constants().boollit(c.id() == "showDzn"); + args[2] = constants().boollit(c.id() == "showJSON"); + std::string enumName = createEnumToStringName(ti_id, "_toString_"); + c.id(ASTString(enumName)); + c.args(args); + } + if (c.id() == "showDzn" || (c.id() == "showJSON" && enumId > 0)) { + c.id(constants().ids.show); + } + } + c.decl(env.model->matchFn(env, &c, false)); + } + } _decls(env); + topDown(_decls, e); +} + +void checkRenameVar(EnvI& e, VarDecl* vd) { + if (vd->id()->idn() != vd->flat()->id()->idn()) { + TypeInst* vd_rename_ti = copy(e, e.cmap, vd->ti())->cast(); + VarDecl* vd_rename = + new VarDecl(Location().introduce(), vd_rename_ti, vd->flat()->id()->idn(), NULL); + vd_rename->flat(vd->flat()); + makePar(e, vd_rename); + vd->e(vd_rename->id()); + e.output->addItem(new VarDeclI(Location().introduce(), vd_rename)); + } +} + +class ClearAnnotations { +public: + /// Push all elements of \a v onto \a stack + template + static void pushVec(std::vector& stack, ASTExprVec v) { + for (unsigned int i = 0; i < v.size(); i++) stack.push_back(v[i]); + } + + static void run(Expression* root) { + std::vector stack; + stack.push_back(root); + while (!stack.empty()) { + Expression* e = stack.back(); + stack.pop_back(); + if (e == NULL) { + continue; + } + e->ann().clear(); + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ID: + case Expression::E_ANON: + case Expression::E_TIID: + break; + case Expression::E_SETLIT: + pushVec(stack, e->template cast()->v()); + break; + case Expression::E_ARRAYLIT: + for (unsigned int i = 0; i < e->cast()->size(); i++) { + stack.push_back((*e->cast())[i]); + } + break; + case Expression::E_ARRAYACCESS: + pushVec(stack, e->template cast()->idx()); + stack.push_back(e->template cast()->v()); + break; + case Expression::E_COMP: { + Comprehension* comp = e->template cast(); + for (unsigned int i = comp->n_generators(); i--;) { + stack.push_back(comp->where(i)); + stack.push_back(comp->in(i)); + for (unsigned int j = comp->n_decls(i); j--;) { + stack.push_back(comp->decl(i, j)); + } + } + stack.push_back(comp->e()); + } break; + case Expression::E_ITE: { + ITE* ite = e->template cast(); + stack.push_back(ite->e_else()); + for (int i = 0; i < ite->size(); i++) { + stack.push_back(ite->e_if(i)); + stack.push_back(ite->e_then(i)); + } + } break; + case Expression::E_BINOP: + stack.push_back(e->template cast()->rhs()); + stack.push_back(e->template cast()->lhs()); + break; + case Expression::E_UNOP: + stack.push_back(e->template cast()->e()); + break; + case Expression::E_CALL: + for (unsigned int i = 0; i < e->template cast()->n_args(); i++) + stack.push_back(e->template cast()->arg(i)); + break; + case Expression::E_VARDECL: + stack.push_back(e->template cast()->e()); + stack.push_back(e->template cast()->ti()); + break; + case Expression::E_LET: + stack.push_back(e->template cast()->in()); + pushVec(stack, e->template cast()->let()); + break; + case Expression::E_TI: + stack.push_back(e->template cast()->domain()); + pushVec(stack, e->template cast()->ranges()); + break; + } + } + } +}; + +void outputVarDecls(EnvI& env, Item* ci, Expression* e) { + class O : public EVisitor { + public: + EnvI& env; + Item* ci; + O(EnvI& env0, Item* ci0) : env(env0), ci(ci0) {} + void vId(Id& id) { + if (&id == constants().absent) return; + if (!id.decl()->toplevel()) return; + VarDecl* vd = id.decl(); + VarDecl* reallyFlat = vd->flat(); + while (reallyFlat != NULL && reallyFlat != reallyFlat->flat()) + reallyFlat = reallyFlat->flat(); + IdMap::iterator idx = + reallyFlat ? env.output_vo_flat.idx.find(reallyFlat->id()) : env.output_vo_flat.idx.end(); + IdMap::iterator idx2 = env.output_vo.idx.find(vd->id()); + if (idx == env.output_vo_flat.idx.end() && idx2 == env.output_vo.idx.end()) { + VarDeclI* nvi = + new VarDeclI(Location().introduce(), copy(env, env.cmap, vd)->cast()); + Type t = nvi->e()->ti()->type(); + if (t.ti() != Type::TI_PAR) { + t.ti(Type::TI_PAR); + } + makePar(env, nvi->e()); + nvi->e()->ti()->domain(NULL); + nvi->e()->flat(vd->flat()); + ClearAnnotations::run(nvi->e()); + nvi->e()->introduced(false); + if (reallyFlat) env.output_vo_flat.add_idx(reallyFlat, env.output->size()); + env.output_vo.add_idx(nvi, env.output->size()); + env.output_vo.add(nvi->e(), ci); + env.output->addItem(nvi); + + IdMap::iterator it; + if ((it = env.reverseMappers.find(nvi->e()->id())) != env.reverseMappers.end()) { + Call* rhs = copy(env, env.cmap, it->second())->cast(); + { + std::vector tv(rhs->n_args()); + for (unsigned int i = rhs->n_args(); i--;) { + tv[i] = rhs->arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = env.output->matchFn(env, rhs->id(), tv, false); + Type t; + if (decl == NULL) { + FunctionI* origdecl = env.model->matchFn(env, rhs->id(), tv, false); + if (origdecl == NULL) { + throw FlatteningError( + env, rhs->loc(), + "function " + rhs->id().str() + " is used in output, par version needed"); + } + if (!origdecl->from_stdlib()) { + decl = copy(env, env.cmap, origdecl)->cast(); + CollectOccurrencesE ce(env.output_vo, decl); + topDown(ce, decl->e()); + topDown(ce, decl->ti()); + for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]); + env.output->registerFn(env, decl); + env.output->addItem(decl); + } else { + decl = origdecl; + } + } + rhs->decl(decl); + } + outputVarDecls(env, nvi, it->second()); + nvi->e()->e(rhs); + } else if (reallyFlat && cannotUseRHSForOutput(env, reallyFlat->e())) { + assert(nvi->e()->flat()); + nvi->e()->e(NULL); + if (nvi->e()->type().dim() == 0) { + reallyFlat->addAnnotation(constants().ann.output_var); + } else { + std::vector args(reallyFlat->e()->type().dim()); + for (unsigned int i = 0; i < args.size(); i++) { + if (nvi->e()->ti()->ranges()[i]->domain() == NULL) { + args[i] = new SetLit(Location().introduce(), + eval_intset(env, reallyFlat->ti()->ranges()[i]->domain())); + } else { + args[i] = new SetLit(Location().introduce(), + eval_intset(env, nvi->e()->ti()->ranges()[i]->domain())); + } + } + ArrayLit* al = new ArrayLit(Location().introduce(), args); + args.resize(1); + args[0] = al; + reallyFlat->addAnnotation( + new Call(Location().introduce(), constants().ann.output_array, args)); + } + checkRenameVar(env, nvi->e()); + } else { + outputVarDecls(env, nvi, nvi->e()->ti()); + outputVarDecls(env, nvi, nvi->e()->e()); + } + CollectOccurrencesE ce(env.output_vo, nvi); + topDown(ce, nvi->e()); + } + } + } _o(env, ci); + topDown(_o, e); +} + +void processDeletions(EnvI& e, std::vector& deletedFlatVarDecls) { + std::vector deletedVarDecls; + for (unsigned int i = 0; i < e.output->size(); i++) { + if (VarDeclI* vdi = (*e.output)[i]->dyn_cast()) { + if (!vdi->removed() && e.output_vo.occurrences(vdi->e()) == 0 && + !vdi->e()->ann().contains(constants().ann.mzn_check_var) && + !(vdi->e()->id()->idn() == -1 && vdi->e()->id()->v() == "_mzn_solution_checker")) { + CollectDecls cd(e.output_vo, deletedVarDecls, vdi); + topDown(cd, vdi->e()->e()); + removeIsOutput(vdi->e()->flat()); + if (e.output_vo.find(vdi->e()) != -1) e.output_vo.remove(vdi->e()); + vdi->remove(); + } + } + } + while (!deletedVarDecls.empty()) { + VarDecl* cur = deletedVarDecls.back(); + deletedVarDecls.pop_back(); + if (e.output_vo.occurrences(cur) == 0) { + IdMap::iterator cur_idx = e.output_vo.idx.find(cur->id()); + if (cur_idx != e.output_vo.idx.end()) { + VarDeclI* vdi = (*e.output)[cur_idx->second]->cast(); + if (!vdi->removed()) { + CollectDecls cd(e.output_vo, deletedVarDecls, vdi); + topDown(cd, cur->e()); + removeIsOutput(vdi->e()->flat()); + if (e.output_vo.find(vdi->e()) != -1) e.output_vo.remove(vdi->e()); + vdi->remove(); + } + } + } + } + + for (IdMap::iterator it = e.output_vo._m.begin(); + it != e.output_vo._m.end(); ++it) { + std::vector toRemove; + for (VarOccurrences::Items::iterator iit = it->second.begin(); iit != it->second.end(); ++iit) { + if ((*iit)->removed()) { + toRemove.push_back(*iit); + } + } + for (unsigned int i = 0; i < toRemove.size(); i++) { + it->second.erase(toRemove[i]); + } + } +} + +void createDznOutputItem(EnvI& e, bool outputObjective, bool includeOutputItem) { + std::vector outputVars; + + class DZNOVisitor : public ItemVisitor { + protected: + EnvI& e; + bool outputObjective; + bool includeOutputItem; + std::vector& outputVars; + bool had_add_to_output; + + public: + DZNOVisitor(EnvI& e0, bool outputObjective0, bool includeOutputItem0, + std::vector& outputVars0) + : e(e0), + outputObjective(outputObjective0), + outputVars(outputVars0), + includeOutputItem(includeOutputItem0), + had_add_to_output(false) {} + void vVarDeclI(VarDeclI* vdi) { + VarDecl* vd = vdi->e(); + bool process_var = false; + if (outputObjective && vd->id()->idn() == -1 && vd->id()->v() == "_objective") { + process_var = true; + } else { + if (vd->ann().contains(constants().ann.add_to_output)) { + if (!had_add_to_output) { + outputVars.clear(); + } + had_add_to_output = true; + process_var = true; + } else { + if (!had_add_to_output) { + process_var = false; + if (vd->type().isvar()) { + if (vd->e()) { + if (ArrayLit* al = vd->e()->dyn_cast()) { + for (unsigned int i = 0; i < al->size(); i++) { + if ((*al)[i]->isa()) { + process_var = true; + break; + } + } + } else if (vd->ann().contains(constants().ann.rhs_from_assignment)) { + process_var = true; + } + } else { + process_var = true; + } + } + } + } + } + if (process_var) { + std::ostringstream s; + s << vd->id()->str().str() << " = "; + if (vd->type().dim() > 0) { + ArrayLit* al = NULL; + if (vd->flat() && vd->flat()->e()) { + al = eval_array_lit(e, vd->flat()->e()); + } else if (vd->e()) { + al = eval_array_lit(e, vd->e()); + } + s << "array" << vd->type().dim() << "d("; + for (int i = 0; i < vd->type().dim(); i++) { + unsigned int enumId = + (vd->type().enumId() != 0 ? e.getArrayEnum(vd->type().enumId())[i] : 0); + if (enumId != 0) { + s << e.getEnum(enumId)->e()->id()->str() << ", "; + } else if (al != NULL) { + s << al->min(i) << ".." << al->max(i) << ", "; + } else { + IntSetVal* idxset = eval_intset(e, vd->ti()->ranges()[i]->domain()); + s << *idxset << ", "; + } + } + } + StringLit* sl = new StringLit(Location().introduce(), s.str()); + outputVars.push_back(sl); + + std::vector showArgs(1); + showArgs[0] = vd->id(); + Call* show = new Call(Location().introduce(), ASTString("showDzn"), showArgs); + show->type(Type::parstring()); + FunctionI* fi = e.model->matchFn(e, show, false); + assert(fi); + show->decl(fi); + outputVars.push_back(show); + std::string ends = vd->type().dim() > 0 ? ")" : ""; + ends += ";\n"; + StringLit* eol = new StringLit(Location().introduce(), ends); + outputVars.push_back(eol); + } + } + void vOutputI(OutputI* oi) { + if (includeOutputItem) { + outputVars.push_back(new StringLit(Location().introduce(), "_output = ")); + Call* concat = new Call(Location().introduce(), ASTString("concat"), {oi->e()}); + concat->type(Type::parstring()); + FunctionI* fi = e.model->matchFn(e, concat, false); + assert(fi); + concat->decl(fi); + Call* show = new Call(Location().introduce(), ASTString("showDzn"), {concat}); + show->type(Type::parstring()); + fi = e.model->matchFn(e, show, false); + assert(fi); + show->decl(fi); + outputVars.push_back(show); + outputVars.push_back(new StringLit(Location().introduce(), ";\n")); + } + + oi->remove(); + } + } dznov(e, outputObjective, includeOutputItem, outputVars); + + iterItems(dznov, e.model); + + OutputI* newOutputItem = + new OutputI(Location().introduce(), new ArrayLit(Location().introduce(), outputVars)); + e.model->addItem(newOutputItem); +} + +ArrayLit* createJSONOutput(EnvI& e, bool outputObjective, bool includeOutputItem) { + std::vector outputVars; + outputVars.push_back(new StringLit(Location().introduce(), "{\n")); + + class JSONOVisitor : public ItemVisitor { + protected: + EnvI& e; + bool outputObjective; + bool includeOutputItem; + std::vector& outputVars; + bool had_add_to_output; + bool first_var; + + public: + JSONOVisitor(EnvI& e0, bool outputObjective0, bool includeOutputItem0, + std::vector& outputVars0) + : e(e0), + outputObjective(outputObjective0), + outputVars(outputVars0), + includeOutputItem(includeOutputItem0), + had_add_to_output(false), + first_var(true) {} + void vVarDeclI(VarDeclI* vdi) { + VarDecl* vd = vdi->e(); + bool process_var = false; + if (outputObjective && vd->id()->idn() == -1 && vd->id()->v() == "_objective") { + process_var = true; + } else { + if (vd->ann().contains(constants().ann.add_to_output)) { + if (!had_add_to_output) { + outputVars.clear(); + outputVars.push_back(new StringLit(Location().introduce(), "{\n")); + first_var = true; + } + had_add_to_output = true; + process_var = true; + } else { + if (!had_add_to_output) { + process_var = + vd->type().isvar() && + (vd->e() == NULL || vd->ann().contains(constants().ann.rhs_from_assignment)); + } + } + } + if (process_var) { + std::ostringstream s; + if (first_var) { + first_var = false; + } else { + s << ",\n"; + } + s << " \"" << vd->id()->str().str() << "\"" + << " : "; + StringLit* sl = new StringLit(Location().introduce(), s.str()); + outputVars.push_back(sl); + + std::vector showArgs(1); + showArgs[0] = vd->id(); + Call* show = new Call(Location().introduce(), "showJSON", showArgs); + show->type(Type::parstring()); + FunctionI* fi = e.model->matchFn(e, show, false); + assert(fi); + show->decl(fi); + outputVars.push_back(show); + } + } + void vOutputI(OutputI* oi) { + if (includeOutputItem) { + std::ostringstream s; + if (first_var) { + first_var = false; + } else { + s << ",\n"; + } + s << " \"_output\"" + << " : "; + StringLit* sl = new StringLit(Location().introduce(), s.str()); + outputVars.push_back(sl); + Call* concat = new Call(Location().introduce(), ASTString("concat"), {oi->e()}); + concat->type(Type::parstring()); + FunctionI* fi = e.model->matchFn(e, concat, false); + assert(fi); + concat->decl(fi); + Call* show = new Call(Location().introduce(), ASTString("showJSON"), {concat}); + show->type(Type::parstring()); + fi = e.model->matchFn(e, show, false); + assert(fi); + show->decl(fi); + outputVars.push_back(show); + } + + oi->remove(); + } + } jsonov(e, outputObjective, includeOutputItem, outputVars); + + iterItems(jsonov, e.model); + + outputVars.push_back(new StringLit(Location().introduce(), "\n}\n")); + return new ArrayLit(Location().introduce(), outputVars); +} +void createJSONOutputItem(EnvI& e, bool outputObjective, bool includeOutputItem) { + OutputI* newOutputItem = + new OutputI(Location().introduce(), createJSONOutput(e, outputObjective, includeOutputItem)); + e.model->addItem(newOutputItem); +} + +void createOutput(EnvI& e, std::vector& deletedFlatVarDecls, + FlatteningOptions::OutputMode outputMode, bool outputObjective, + bool includeOutputItem) { + // Create new output model + OutputI* outputItem = NULL; + GCLock lock; + + switch (outputMode) { + case FlatteningOptions::OUTPUT_DZN: + createDznOutputItem(e, outputObjective, includeOutputItem); + break; + case FlatteningOptions::OUTPUT_JSON: + createJSONOutputItem(e, outputObjective, includeOutputItem); + default: + if (e.model->outputItem() == NULL) { + createDznOutputItem(e, outputObjective, false); + } + break; + } + + // Copy output item from model into output model + outputItem = copy(e, e.cmap, e.model->outputItem())->cast(); + makePar(e, outputItem->e()); + e.output->addItem(outputItem); + + // Copy all function definitions that are required for output into the output model + class CollectFunctions : public EVisitor { + public: + EnvI& env; + CollectFunctions(EnvI& env0) : env(env0) {} + bool enter(Expression* e) { + if (e->type().isvar()) { + Type t = e->type(); + t.ti(Type::TI_PAR); + e->type(t); + } + return true; + } + void vCall(Call& c) { + std::vector tv(c.n_args()); + for (unsigned int i = c.n_args(); i--;) { + tv[i] = c.arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = env.output->matchFn(env, c.id(), tv, false); + FunctionI* origdecl = env.model->matchFn(env, c.id(), tv, false); + bool canReuseDecl = (decl != nullptr); + if (canReuseDecl && origdecl) { + // Check if this is the exact same overloaded declaration as in the model + for (unsigned int i = 0; i < decl->params().size(); i++) { + if (decl->params()[i]->type() != origdecl->params()[i]->type()) { + // no, the types don't match, so we have to copy the original decl + canReuseDecl = false; + break; + } + } + } + Type t; + if (!canReuseDecl) { + if (origdecl == NULL || !origdecl->rtype(env, tv, false).ispar()) { + throw FlatteningError( + env, c.loc(), "function " + c.id().str() + " is used in output, par version needed"); + } + if (!origdecl->from_stdlib()) { + decl = copy(env, env.cmap, origdecl)->cast(); + env.output->registerFn(env, decl); + env.output->addItem(decl); + if (decl->e()) { + makePar(env, decl->e()); + topDown(*this, decl->e()); + } + CollectOccurrencesE ce(env.output_vo, decl); + topDown(ce, decl->e()); + topDown(ce, decl->ti()); + for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]); + } else { + decl = origdecl; + } + } + c.decl(decl); + } + } _cf(e); + topDown(_cf, outputItem->e()); + + // If we are checking solutions using a checker model, all parameters of the checker model + // have to be made available in the output model + class OV1 : public ItemVisitor { + public: + EnvI& env; + CollectFunctions& _cf; + OV1(EnvI& env0, CollectFunctions& cf) : env(env0), _cf(cf) {} + void vVarDeclI(VarDeclI* vdi) { + if (vdi->e()->ann().contains(constants().ann.mzn_check_var)) { + VarDecl* output_vd = copy(env, env.cmap, vdi->e())->cast(); + topDown(_cf, output_vd); + } + } + } _ov1(e, _cf); + iterItems(_ov1, e.model); + + // Copying the output item and the functions it depends on has created copies + // of all dependent VarDecls. However the output model does not contain VarDeclIs for + // these VarDecls yet. This iterator processes all variable declarations of the + // original model, and if they were copied (i.e., if the output model depends on them), + // the corresponding VarDeclI is created in the output model. + class OV2 : public ItemVisitor { + public: + EnvI& env; + OV2(EnvI& env0) : env(env0) {} + void vVarDeclI(VarDeclI* vdi) { + IdMap::iterator idx = env.output_vo.idx.find(vdi->e()->id()); + if (idx != env.output_vo.idx.end()) return; + if (Expression* vd_e = env.cmap.find(vdi->e())) { + // We found a copied VarDecl, now need to create a VarDeclI + VarDecl* vd = vd_e->cast(); + VarDeclI* vdi_copy = copy(env, env.cmap, vdi)->cast(); + + Type t = vdi_copy->e()->ti()->type(); + t.ti(Type::TI_PAR); + vdi_copy->e()->ti()->domain(NULL); + vdi_copy->e()->flat(vdi->e()->flat()); + bool isCheckVar = vdi_copy->e()->ann().contains(constants().ann.mzn_check_var); + Call* checkVarEnum = vdi_copy->e()->ann().getCall(constants().ann.mzn_check_enum_var); + vdi_copy->e()->ann().clear(); + if (isCheckVar) { + vdi_copy->e()->ann().add(constants().ann.mzn_check_var); + } + if (checkVarEnum) { + vdi_copy->e()->ann().add(checkVarEnum); + } + vdi_copy->e()->introduced(false); + IdMap::iterator it; + if (!vdi->e()->type().ispar()) { + if (vd->flat() == NULL && vdi->e()->e() != NULL && vdi->e()->e()->type().ispar()) { + // Don't have a flat version of this variable, but the original has a right hand + // side that is par, so we can use that. + Expression* flate = eval_par(env, vdi->e()->e()); + outputVarDecls(env, vdi_copy, flate); + vd->e(flate); + } else { + vd = follow_id_to_decl(vd->id())->cast(); + VarDecl* reallyFlat = vd->flat(); + while (reallyFlat && reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat(); + if (reallyFlat == NULL) { + // The variable doesn't have a flat version. This can only happen if + // the original variable had type-inst var, but a right-hand-side that + // was par, so follow_id_to_decl lead to a par variable. + assert(vd->e() && vd->e()->type().ispar()); + Expression* flate = eval_par(env, vd->e()); + outputVarDecls(env, vdi_copy, flate); + vd->e(flate); + } else if (vd->flat()->e() && vd->flat()->e()->type().ispar()) { + // We can use the right hand side of the flat version of this variable + Expression* flate = copy(env, env.cmap, follow_id(reallyFlat->id())); + outputVarDecls(env, vdi_copy, flate); + vd->e(flate); + } else if ((it = env.reverseMappers.find(vd->id())) != env.reverseMappers.end()) { + // Found a reverse mapper, so we need to add the mapping function to the + // output model to map the FlatZinc value back to the model variable. + Call* rhs = copy(env, env.cmap, it->second())->cast(); + { + std::vector tv(rhs->n_args()); + for (unsigned int i = rhs->n_args(); i--;) { + tv[i] = rhs->arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = env.output->matchFn(env, rhs->id(), tv, false); + if (decl == NULL) { + FunctionI* origdecl = env.model->matchFn(env, rhs->id(), tv, false); + if (origdecl == NULL) { + throw FlatteningError( + env, rhs->loc(), + "function " + rhs->id().str() + " is used in output, par version needed"); + } + if (!origdecl->from_stdlib()) { + decl = copy(env, env.cmap, origdecl)->cast(); + CollectOccurrencesE ce(env.output_vo, decl); + topDown(ce, decl->e()); + topDown(ce, decl->ti()); + for (unsigned int i = decl->params().size(); i--;) + topDown(ce, decl->params()[i]); + env.output->registerFn(env, decl); + env.output->addItem(decl); + } else { + decl = origdecl; + } + } + rhs->decl(decl); + } + outputVarDecls(env, vdi_copy, rhs); + vd->e(rhs); + } else if (cannotUseRHSForOutput(env, vd->e())) { + // If the VarDecl does not have a usable right hand side, it needs to be + // marked as output in the FlatZinc + vd->e(NULL); + assert(vd->flat()); + if (vd->type().dim() == 0) { + vd->flat()->addAnnotation(constants().ann.output_var); + checkRenameVar(env, vd); + } else { + bool needOutputAnn = true; + if (reallyFlat->e() && reallyFlat->e()->isa()) { + ArrayLit* al = reallyFlat->e()->cast(); + for (unsigned int i = 0; i < al->size(); i++) { + if (Id* id = (*al)[i]->dyn_cast()) { + if (env.reverseMappers.find(id) != env.reverseMappers.end()) { + needOutputAnn = false; + break; + } + } + } + if (!needOutputAnn) { + outputVarDecls(env, vdi_copy, al); + vd->e(copy(env, env.cmap, al)); + } + } + if (needOutputAnn) { + std::vector args(vdi->e()->type().dim()); + for (unsigned int i = 0; i < args.size(); i++) { + if (vdi->e()->ti()->ranges()[i]->domain() == NULL) { + args[i] = + new SetLit(Location().introduce(), + eval_intset(env, vd->flat()->ti()->ranges()[i]->domain())); + } else { + args[i] = new SetLit(Location().introduce(), + eval_intset(env, vd->ti()->ranges()[i]->domain())); + } + } + ArrayLit* al = new ArrayLit(Location().introduce(), args); + args.resize(1); + args[0] = al; + vd->flat()->addAnnotation( + new Call(Location().introduce(), constants().ann.output_array, args)); + checkRenameVar(env, vd); + } + } + } + if (reallyFlat && env.output_vo_flat.find(reallyFlat) == -1) + env.output_vo_flat.add_idx(reallyFlat, env.output->size()); + } + } + makePar(env, vdi_copy->e()); + env.output_vo.add_idx(vdi_copy, env.output->size()); + CollectOccurrencesE ce(env.output_vo, vdi_copy); + topDown(ce, vdi_copy->e()); + env.output->addItem(vdi_copy); + } + } + } _ov2(e); + iterItems(_ov2, e.model); + + CollectOccurrencesE ce(e.output_vo, outputItem); + topDown(ce, outputItem->e()); + + e.model->mergeStdLib(e, e.output); + processDeletions(e, deletedFlatVarDecls); +} + +Expression* isFixedDomain(EnvI& env, VarDecl* vd) { + if (vd->type() != Type::varbool() && vd->type() != Type::varint() && + vd->type() != Type::varfloat()) + return NULL; + Expression* e = vd->ti()->domain(); + if (e == constants().lit_true || e == constants().lit_false) return e; + if (SetLit* sl = Expression::dyn_cast(e)) { + if (sl->type().bt() == Type::BT_INT) { + IntSetVal* isv = eval_intset(env, sl); + return isv->min() == isv->max() ? IntLit::a(isv->min()) : NULL; + } else if (sl->type().bt() == Type::BT_FLOAT) { + FloatSetVal* fsv = eval_floatset(env, sl); + return fsv->min() == fsv->max() ? FloatLit::a(fsv->min()) : NULL; + } + } + return NULL; +} + +void finaliseOutput(EnvI& e, std::vector& deletedFlatVarDecls) { + if (e.output->size() > 0) { + // Adapt existing output model + // (generated by repeated flattening) + e.output_vo.clear(); + for (unsigned int i = 0; i < e.output->size(); i++) { + Item* item = (*e.output)[i]; + if (item->removed()) continue; + switch (item->iid()) { + case Item::II_VD: { + VarDecl* vd = item->cast()->e(); + IdMap::iterator it; + GCLock lock; + VarDecl* reallyFlat = vd->flat(); + while (reallyFlat && reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat(); + if (vd->e() == NULL) { + if ((vd->flat()->e() && vd->flat()->e()->type().ispar()) || + isFixedDomain(e, vd->flat())) { + VarDecl* reallyFlat = vd->flat(); + while (reallyFlat != reallyFlat->flat()) reallyFlat = reallyFlat->flat(); + removeIsOutput(reallyFlat); + Expression* flate; + if (Expression* fd = isFixedDomain(e, vd->flat())) { + flate = fd; + } else { + flate = copy(e, e.cmap, follow_id(reallyFlat->id())); + } + outputVarDecls(e, item, flate); + vd->e(flate); + } else if ((it = e.reverseMappers.find(vd->id())) != e.reverseMappers.end()) { + Call* rhs = copy(e, e.cmap, it->second())->cast(); + std::vector tv(rhs->n_args()); + for (unsigned int i = rhs->n_args(); i--;) { + tv[i] = rhs->arg(i)->type(); + tv[i].ti(Type::TI_PAR); + } + FunctionI* decl = e.output->matchFn(e, rhs->id(), tv, false); + if (decl == NULL) { + FunctionI* origdecl = e.model->matchFn(e, rhs->id(), tv, false); + if (origdecl == NULL) { + throw FlatteningError( + e, rhs->loc(), + "function " + rhs->id().str() + " is used in output, par version needed"); + } + if (!origdecl->from_stdlib()) { + decl = copy(e, e.cmap, origdecl)->cast(); + CollectOccurrencesE ce(e.output_vo, decl); + topDown(ce, decl->e()); + topDown(ce, decl->ti()); + for (unsigned int i = decl->params().size(); i--;) topDown(ce, decl->params()[i]); + e.output->registerFn(e, decl); + e.output->addItem(decl); + } else { + decl = origdecl; + } + } + rhs->decl(decl); + removeIsOutput(reallyFlat); + + if (e.vo.occurrences(reallyFlat) == 0 && reallyFlat->e() == NULL) { + deletedFlatVarDecls.push_back(reallyFlat); + } + + outputVarDecls(e, item, it->second()->cast()); + vd->e(rhs); + } else { + // If the VarDecl does not have a usable right hand side, it needs to be + // marked as output in the FlatZinc + assert(vd->flat()); + + bool needOutputAnn = true; + if (reallyFlat->e() && reallyFlat->e()->isa()) { + ArrayLit* al = reallyFlat->e()->cast(); + for (unsigned int i = 0; i < al->size(); i++) { + if (Id* id = (*al)[i]->dyn_cast()) { + if (e.reverseMappers.find(id) != e.reverseMappers.end()) { + needOutputAnn = false; + break; + } + } + } + if (!needOutputAnn) { + removeIsOutput(vd); + removeIsOutput(reallyFlat); + if (e.vo.occurrences(reallyFlat) == 0) { + deletedFlatVarDecls.push_back(reallyFlat); + } + + outputVarDecls(e, item, al); + vd->e(copy(e, e.cmap, al)); + } + } + if (needOutputAnn) { + if (!isOutput(vd->flat())) { + GCLock lock; + if (vd->type().dim() == 0) { + vd->flat()->addAnnotation(constants().ann.output_var); + } else { + std::vector args(vd->type().dim()); + for (unsigned int i = 0; i < args.size(); i++) { + if (vd->ti()->ranges()[i]->domain() == NULL) { + args[i] = + new SetLit(Location().introduce(), + eval_intset(e, vd->flat()->ti()->ranges()[i]->domain())); + } else { + args[i] = new SetLit(Location().introduce(), + eval_intset(e, vd->ti()->ranges()[i]->domain())); + } + } + ArrayLit* al = new ArrayLit(Location().introduce(), args); + args.resize(1); + args[0] = al; + vd->flat()->addAnnotation( + new Call(Location().introduce(), constants().ann.output_array, args)); + } + checkRenameVar(e, vd); + } + } + } + vd->flat(NULL); + // Remove enum type + Type vdt = vd->type(); + vdt.enumId(0); + vd->type(vdt); + vd->ti()->type(vdt); + } + e.output_vo.add_idx(item->cast(), i); + CollectOccurrencesE ce(e.output_vo, item); + topDown(ce, vd); + } break; + case Item::II_OUT: { + CollectOccurrencesE ce(e.output_vo, item); + topDown(ce, item->cast()->e()); + } break; + case Item::II_FUN: { + CollectOccurrencesE ce(e.output_vo, item); + topDown(ce, item->cast()->e()); + topDown(ce, item->cast()->ti()); + for (unsigned int i = item->cast()->params().size(); i--;) + topDown(ce, item->cast()->params()[i]); + } break; + default: + throw FlatteningError(e, item->loc(), "invalid item in output model"); + } + } + } + processDeletions(e, deletedFlatVarDecls); +} +} // namespace MiniZinc diff --git a/lib/parser.cpp b/lib/parser.cpp new file mode 100644 index 0000000..f5bec72 --- /dev/null +++ b/lib/parser.cpp @@ -0,0 +1,295 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This (main) file coordinates flattening and solving. + * The corresponding modules are flexibly plugged in + * as derived classes, prospectively from DLLs. + * A flattening module should provide MinZinc::GetFlattener() + * A solving module should provide an object of a class derived from SolverFactory. + * Need to get more flexible for multi-pass & multi-solving stuff TODO + */ + +#include +#include +#include +#include + +#include + +using namespace std; + +int mzn_yylex_init(void** scanner); +void mzn_yyset_extra(void* user_defined, void* yyscanner); +int mzn_yyparse(void*); +int mzn_yylex_destroy(void* scanner); + +namespace { +// fastest way to read a file into a string (especially big files) +// see: http://insanecoding.blogspot.be/2011/11/how-to-read-in-file-in-c.html +std::string get_file_contents(std::ifstream& in) { + if (in) { + std::string contents; + in.seekg(0, std::ios::end); + contents.resize(static_cast(in.tellg())); + in.seekg(0, std::ios::beg); + in.read(&contents[0], contents.size()); + in.close(); + if (contents.size() > 0 && contents[0] == '@') { + contents = MiniZinc::FileUtils::decodeBase64(contents); + MiniZinc::FileUtils::inflateString(contents); + } + return (contents); + } + throw(errno); +} +} // namespace + +namespace MiniZinc { + +void parse(Env& env, Model*& model, const vector& filenames, + const vector& datafiles, const std::string& modelString, + const std::string& modelStringName, const vector& ip, bool ignoreStdlib, + bool parseDocComments, bool verbose, ostream& err, + std::vector& syntaxErrors) { + vector includePaths; + for (unsigned int i = 0; i < ip.size(); i++) includePaths.push_back(ip[i]); + + vector files; + map seenModels; + + string workingDir = FileUtils::working_directory(); + + if (filenames.size() > 0) { + GCLock lock; + model->setFilename(FileUtils::base_name(filenames[0])); + if (FileUtils::is_absolute(filenames[0])) { + files.push_back(ParseWorkItem(model, NULL, "", filenames[0])); + } else { + files.push_back(ParseWorkItem(model, NULL, "", workingDir + "/" + filenames[0])); + } + + for (unsigned int i = 1; i < filenames.size(); i++) { + GCLock lock; + string fullName = filenames[i]; + string baseName = FileUtils::base_name(filenames[i]); + if (!FileUtils::is_absolute(fullName)) fullName = workingDir + "/" + fullName; + bool isFzn = (baseName.compare(baseName.length() - 4, 4, ".fzn") == 0); + if (isFzn) { + files.push_back(ParseWorkItem(model, NULL, "", fullName)); + } else { + Model* includedModel = new Model; + includedModel->setFilename(baseName); + files.push_back(ParseWorkItem(includedModel, NULL, "", fullName)); + seenModels.insert(pair(baseName, includedModel)); + Location loc(ASTString(filenames[i]), 0, 0, 0, 0); + IncludeI* inc = new IncludeI(loc, includedModel->filename()); + inc->m(includedModel, true); + model->addItem(inc); + } + } + if (!modelString.empty()) { + Model* includedModel = new Model; + includedModel->setFilename(modelStringName); + files.push_back(ParseWorkItem(includedModel, NULL, modelString, modelStringName, true)); + seenModels.insert(pair(modelStringName, includedModel)); + Location loc(ASTString(modelStringName), 0, 0, 0, 0); + IncludeI* inc = new IncludeI(loc, includedModel->filename()); + inc->m(includedModel, true); + model->addItem(inc); + } + } else if (!modelString.empty()) { + GCLock lock; + model->setFilename(modelStringName); + files.push_back(ParseWorkItem(model, NULL, modelString, modelStringName, true)); + } + + if (!ignoreStdlib) { + GCLock lock; + Model* stdlib = new Model; + stdlib->setFilename("stdlib.mzn"); + files.push_back(ParseWorkItem(stdlib, NULL, "./", "stdlib.mzn")); + seenModels.insert(pair("stdlib.mzn", stdlib)); + Location stdlibloc(ASTString(model->filename()), 0, 0, 0, 0); + IncludeI* stdlibinc = new IncludeI(stdlibloc, stdlib->filename()); + stdlibinc->m(stdlib, true); + model->addItem(stdlibinc); + } + + while (!files.empty()) { + GCLock lock; + ParseWorkItem& np = files.back(); + string parentPath = np.dirName; + Model* m = np.m; + bool isModelString = np.isModelString; + IncludeI* np_ii = np.ii; + string f(np.fileName); + files.pop_back(); + + std::string s; + std::string fullname; + bool isFzn; + if (!isModelString) { + for (Model* p = m->parent(); p; p = p->parent()) { + if (f == p->filename().c_str()) { + err << "Error: cyclic includes: " << std::endl; + for (Model* pe = m; pe; pe = pe->parent()) { + err << " " << pe->filename() << std::endl; + } + goto error; + } + } + ifstream file; + if (FileUtils::is_absolute(f) || parentPath == "") { + if (filenames.size() == 0) { + err << "Internal error." << endl; + goto error; + } + fullname = f; + if (FileUtils::file_exists(fullname)) { + file.open(fullname.c_str(), std::ios::binary); + } + } else { + includePaths.push_back(parentPath); + unsigned int i = 0; + for (; i < includePaths.size(); i++) { + fullname = includePaths[i] + "/" + f; + if (FileUtils::file_exists(fullname)) { + file.open(fullname.c_str(), std::ios::binary); + if (file.is_open()) break; + } + } + if (file.is_open() && i < includePaths.size() - 1 && parentPath == workingDir && + FileUtils::file_path(includePaths[i], workingDir) != FileUtils::file_path(workingDir) && + FileUtils::file_exists(workingDir + "/" + f)) { + err << "Warning: file " << f + << " included from library, but also exists in current working directory" << endl; + } + includePaths.pop_back(); + } + if (!file.is_open()) { + if (np_ii) { + err << np_ii->loc().toString() << ":\n"; + err << "MiniZinc: error in include item, cannot open file '" << f << "'." << endl; + } else { + err << "Error: cannot open file '" << f << "'." << endl; + } + goto error; + } + if (verbose) std::cerr << "processing file '" << fullname << "'" << endl; + s = get_file_contents(file); + + if (m->filepath().size() == 0) m->setFilepath(fullname); + isFzn = (fullname.compare(fullname.length() - 4, 4, ".fzn") == 0); + isFzn |= (fullname.compare(fullname.length() - 4, 4, ".ozn") == 0); + isFzn |= (fullname.compare(fullname.length() - 4, 4, ".szn") == 0); + isFzn |= (fullname.compare(fullname.length() - 4, 4, ".mzc") == 0); + } else { + isFzn = false; + fullname = f; + s = parentPath; + } + ParserState pp(fullname, s, err, files, seenModels, m, false, isFzn, parseDocComments); + mzn_yylex_init(&pp.yyscanner); + mzn_yyset_extra(&pp, pp.yyscanner); + mzn_yyparse(&pp); + if (pp.yyscanner) mzn_yylex_destroy(pp.yyscanner); + if (pp.hadError) { + for (unsigned int i = 0; i < pp.syntaxErrors.size(); i++) + syntaxErrors.push_back(pp.syntaxErrors[i]); + goto error; + } + } + + for (unsigned int i = 0; i < datafiles.size(); i++) { + GCLock lock; + string f = datafiles[i]; + if (f.size() >= 6 && f.substr(f.size() - 5, string::npos) == ".json") { + JSONParser jp(env.envi()); + jp.parse(model, f); + } else { + string s; + if (f.size() > 5 && f.substr(0, 5) == "cmd:/") { + s = f.substr(5); + } else { + std::ifstream file; + file.open(f.c_str(), std::ios::binary); + if (!FileUtils::file_exists(f) || !file.is_open()) { + err << "Error: cannot open data file '" << f << "'." << endl; + goto error; + } + if (verbose) std::cerr << "processing data file '" << f << "'" << endl; + s = get_file_contents(file); + } + + ParserState pp(f, s, err, files, seenModels, model, true, false, parseDocComments); + mzn_yylex_init(&pp.yyscanner); + mzn_yyset_extra(&pp, pp.yyscanner); + mzn_yyparse(&pp); + if (pp.yyscanner) mzn_yylex_destroy(pp.yyscanner); + if (pp.hadError) { + for (unsigned int i = 0; i < pp.syntaxErrors.size(); i++) + syntaxErrors.push_back(pp.syntaxErrors[i]); + goto error; + } + } + } + + return; +error: + delete model; + model = NULL; +} + +Model* parse(Env& env, const vector& filenames, const vector& datafiles, + const string& textModel, const string& textModelName, const vector& ip, + bool ignoreStdlib, bool parseDocComments, bool verbose, ostream& err) { + if (filenames.empty() && textModel.empty()) { + err << "Error: no model given" << std::endl; + return NULL; + } + + Model* model; + { + GCLock lock; + model = new Model(); + } + std::vector se; + parse(env, model, filenames, datafiles, textModel, textModelName, ip, ignoreStdlib, + parseDocComments, verbose, err, se); + return model; +} + +Model* parseData(Env& env, Model* model, const vector& datafiles, + const vector& includePaths, bool ignoreStdlib, bool parseDocComments, + bool verbose, ostream& err) { + vector filenames; + std::vector se; + parse(env, model, filenames, datafiles, "", "", includePaths, ignoreStdlib, parseDocComments, + verbose, err, se); + return model; +} + +Model* parseFromString(Env& env, const string& text, const string& filename, + const vector& ip, bool ignoreStdlib, bool parseDocComments, + bool verbose, ostream& err, std::vector& syntaxErrors) { + vector filenames; + vector datafiles; + Model* model; + { + GCLock lock; + model = new Model(); + } + parse(env, model, filenames, datafiles, text, filename, ip, ignoreStdlib, parseDocComments, + verbose, err, syntaxErrors); + return model; +} + +} // namespace MiniZinc diff --git a/lib/parser.yxx b/lib/parser.yxx new file mode 100644 index 0000000..d10929f --- /dev/null +++ b/lib/parser.yxx @@ -0,0 +1,1599 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%define api.pure + +%parse-param {void *parm} + +%define api.header.include {} + +%lex-param {void* SCANNER} +%{ +#define SCANNER static_cast(parm)->yyscanner +#include +#include +#include +#include + +namespace MiniZinc{ class ParserLocation; } +#define YYLTYPE MiniZinc::ParserLocation +#define YYLTYPE_IS_DECLARED 1 +#define YYLTYPE_IS_TRIVIAL 0 + +#define YYMAXDEPTH 10000 +#define YYINITDEPTH 10000 + +#include +#include + +using namespace std; +using namespace MiniZinc; + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + (Current).filename(Rhs[1].filename()); \ + (Current).first_line(Rhs[1].first_line()); \ + (Current).first_column(Rhs[1].first_column()); \ + (Current).last_line(Rhs[N].last_line()); \ + (Current).last_column(Rhs[N].last_column()); + +int mzn_yyparse(void*); +int mzn_yylex(YYSTYPE*, YYLTYPE*, void* scanner); +int mzn_yylex_init (void** scanner); +int mzn_yylex_destroy (void* scanner); +int mzn_yyget_lineno (void* scanner); +void mzn_yyset_extra (void* user_defined ,void* yyscanner ); + +extern int yydebug; + +void yyerror(YYLTYPE* location, void* parm, const string& str) { + ParserState* pp = static_cast(parm); + Model* m = pp->model; + while (m->parent() != NULL) { + m = m->parent(); + pp->err << "(included from file '" << m->filename() << "')" << endl; + } + pp->err << location->toString() << ":" << endl; + pp->printCurrentLine(location->first_column(),location->last_column()); + pp->err << "Error: " << str << std::endl; + pp->hadError = true; + pp->syntaxErrors.push_back(SyntaxError(Location(*location), str)); +} + +bool notInDatafile(YYLTYPE* location, void* parm, const string& item) { + ParserState* pp = static_cast(parm); + if (pp->isDatafile) { + yyerror(location,parm,item+" item not allowed in data file"); + return false; + } + return true; +} + +Expression* createDocComment(const ParserLocation& loc, const std::string& s) { + std::vector args(1); + args[0] = new StringLit(loc, s); + Call* c = new Call(Location(loc), constants().ann.doc_comment, args); + c->type(Type::ann()); + return c; +} + +Expression* createArrayAccess(const ParserLocation& loc, Expression* e, std::vector >& idx) { + Expression* ret = e; + for (unsigned int i=0; i* vardeclexpr_v; + MiniZinc::TypeInst* tiexpr; + std::vector* tiexpr_v; + MiniZinc::Expression* expression; + std::vector* expression_v; + std::vector >* expression_vv; + std::vector > >* expression_vvv; + MiniZinc::Generator* generator; + std::vector* generator_v; + std::vector* string_v; + std::vector >* expression_p; + MiniZinc::Generators* generators; + } + +%locations +%define parse.error verbose + +%initial-action +{ + GCLock lock; + @$.filename(ASTString(static_cast(parm)->filename)); +} + +%token MZN_INTEGER_LITERAL "integer literal" MZN_BOOL_LITERAL "bool literal" +%token MZN_FLOAT_LITERAL "float literal" +%token MZN_IDENTIFIER "identifier" MZN_QUOTED_IDENTIFIER "quoted identifier" MZN_STRING_LITERAL "string literal" +%token MZN_STRING_QUOTE_START "interpolated string start" MZN_STRING_QUOTE_MID "interpolated string middle" MZN_STRING_QUOTE_END "interpolated string end" +%token MZN_TI_IDENTIFIER "type-inst identifier" MZN_TI_ENUM_IDENTIFIER "type-inst enum identifier" MZN_DOC_COMMENT "documentation comment" MZN_DOC_FILE_COMMENT "file-level documentation comment" + +%token MZN_VAR "var" MZN_PAR "par" + +%token MZN_ABSENT "<>" +%token MZN_ANN "ann" +%token MZN_ANNOTATION "annotation" +%token MZN_ANY "any" +%token MZN_ARRAY "array" +%token MZN_BOOL "bool" +%token MZN_CASE "case" +%token MZN_CONSTRAINT "constraint" +%token MZN_DEFAULT "default" +%token MZN_ELSE "else" +%token MZN_ELSEIF "elseif" +%token MZN_ENDIF "endif" +%token MZN_ENUM "enum" +%token MZN_FLOAT "float" +%token MZN_FUNCTION "function" +%token MZN_IF "if" +%token MZN_INCLUDE "include" +%token MZN_INFINITY "infinity" +%token MZN_INT "int" +%token MZN_LET "let" +%token MZN_LIST "list" +%token MZN_MAXIMIZE "maximize" +%token MZN_MINIMIZE "minimize" +%token MZN_OF "of" +%token MZN_OPT "opt" +%token MZN_SATISFY "satisfy" +%token MZN_OUTPUT "output" +%token MZN_PREDICATE "predicate" +%token MZN_RECORD "record" +%token MZN_SET "set" +%token MZN_SOLVE "solve" +%token MZN_STRING "string" +%token MZN_TEST "test" +%token MZN_THEN "then" +%token MZN_TUPLE "tuple" +%token MZN_TYPE "type" +%token MZN_UNDERSCORE "_" +%token MZN_VARIANT_RECORD "variant_record" +%token MZN_WHERE "where" + +%token MZN_LEFT_BRACKET "[" +%token MZN_LEFT_2D_BRACKET "[|" +%token MZN_RIGHT_BRACKET "]" +%token MZN_RIGHT_2D_BRACKET "|]" + +// Used to signal an error when parsing a MiniZinc file +// that contains identifiers starting with _ +%token FLATZINC_IDENTIFIER + +%token MZN_INVALID_INTEGER_LITERAL "invalid integer literal" +%token MZN_INVALID_FLOAT_LITERAL "invalid float literal" +%token MZN_UNTERMINATED_STRING "unterminated string" +%token MZN_END_OF_LINE_IN_STRING "end of line inside string literal" +%token MZN_INVALID_NULL "null character" + +%token END 0 "end of file" + +%token MZN_EQUIV "<->" +%token MZN_IMPL "->" MZN_RIMPL "<-" +%token MZN_OR "\\/" MZN_XOR "xor" +%token MZN_AND "/\\" +%token MZN_LE "<" MZN_GR ">" MZN_LQ "<=" MZN_GQ ">=" MZN_EQ "=" MZN_NQ "!=" MZN_WEAK_EQ "~=" +%token MZN_IN "in" MZN_SUBSET "subset" MZN_SUPERSET "superset" +%token MZN_UNION "union" MZN_DIFF "diff" MZN_SYMDIFF "symdiff" +%token MZN_DOTDOT ".." +%token MZN_PLUS "+" MZN_MINUS "-" MZN_WEAK_PLUS "~+" MZN_WEAK_MINUS "~-" +%token MZN_MULT "*" MZN_DIV "/" MZN_IDIV "div" MZN_MOD "mod" MZN_INTERSECT "intersect" MZN_WEAK_MULT "~*" +%token MZN_POW "^" +%token MZN_NOT "not" +%token MZN_PLUSPLUS "++" +%token MZN_COLONCOLON "::" + +%right PREC_ANNO +%left MZN_EQUIV +%left MZN_IMPL MZN_RIMPL +%left MZN_OR MZN_XOR +%left MZN_AND +%nonassoc MZN_LE MZN_GR MZN_LQ MZN_GQ MZN_EQ MZN_NQ MZN_WEAK_EQ +%nonassoc MZN_IN MZN_SUBSET MZN_SUPERSET +%left MZN_UNION MZN_DIFF MZN_SYMDIFF MZN_INTERSECT +%nonassoc MZN_DOTDOT +%left MZN_PLUS MZN_MINUS MZN_WEAK_PLUS MZN_WEAK_MINUS +%left MZN_MULT MZN_DIV MZN_IDIV MZN_MOD MZN_WEAK_MULT +%left MZN_POW +%nonassoc MZN_NOT +%left MZN_PLUSPLUS +%left MZN_QUOTED_IDENTIFIER +%left MZN_COLONCOLON + +%token MZN_EQUIV_QUOTED "'<->'" +%token MZN_IMPL_QUOTED "'->'" MZN_RIMPL_QUOTED "'<-'" +%token MZN_OR_QUOTED "'\\/'" MZN_XOR_QUOTED "'xor'" +%token MZN_AND_QUOTED "'/\\'" +%token MZN_LE_QUOTED "'<'" MZN_GR_QUOTED "'>'" MZN_LQ_QUOTED "'<='" MZN_GQ_QUOTED "'>='" MZN_EQ_QUOTED "'='" MZN_NQ_QUOTED "'!='" +%token MZN_IN_QUOTED "'in'" MZN_SUBSET_QUOTED "'subset'" MZN_SUPERSET_QUOTED "'superset'" +%token MZN_UNION_QUOTED "'union'" MZN_DIFF_QUOTED "'diff'" MZN_SYMDIFF_QUOTED "'symdiff'" +%token MZN_DOTDOT_QUOTED "'..'" +%token MZN_PLUS_QUOTED "'+'" MZN_MINUS_QUOTED "'-'" +%token MZN_MULT_QUOTED "'*'" MZN_DIV_QUOTED "'/'" MZN_IDIV_QUOTED "'div'" MZN_MOD_QUOTED "'mod'" MZN_INTERSECT_QUOTED "'intersect'" +%token MZN_POW_QUOTED "'^'" +%token MZN_NOT_QUOTED "'not'" +%token MZN_COLONCOLON_QUOTED "'::'" +%token MZN_PLUSPLUS_QUOTED "'++'" + +%type item item_tail include_item vardecl_item assign_item constraint_item solve_item output_item predicate_item annotation_item function_item + +%type ti_expr_and_id ti_expr_and_id_or_anon let_vardecl_item +%type params params_list params_list_head +%type ti_expr base_ti_expr base_ti_expr_tail +%type ti_expr_list ti_expr_list_head + +%type expr expr_atom_head expr_atom_head_nonstring array_access_expr +%type set_expr string_expr string_quote_rest annotation_expr +%type simple_array_literal simple_array_literal_2d simple_array_comp if_then_else_expr call_expr quoted_op_call let_expr operation_item_tail set_literal set_comp + +%type expr_list expr_list_head array_access_expr_list array_access_expr_list_head elseif_list let_vardecl_item_list enum_id_list string_lit_list +%type simple_array_literal_2d_list array_access_tail +%type simple_array_literal_3d_list + +%type comp_tail +%type generator generator_eq +%type generator_list generator_list_head +%type id_list id_list_head + +%type comp_or_expr comp_or_expr_head + +%type annotations ne_annotations + +%type quoted_op + +%type id_or_quoted_op + +%type opt_opt + +%% + +/********************************/ +/* main goal and item lists */ +/********************************/ + +model : item_list + +item_list : + /* empty */ + | item_list_head semi_or_none + +item_list_head: + item + { + ParserState* pp = static_cast(parm); + if ($1) { + pp->model->addItem($1); + GC::unlock(); + GC::lock(); + } + } + | doc_file_comments item + { + ParserState* pp = static_cast(parm); + if ($2) { + pp->model->addItem($2); + GC::unlock(); + GC::lock(); + } + } + | item_list_head ';' item + { + ParserState* pp = static_cast(parm); + if ($3) { + pp->model->addItem($3); + GC::unlock(); + GC::lock(); + } + } + | item_list_head ';' doc_file_comments item + { + ParserState* pp = static_cast(parm); + if ($4) { + pp->model->addItem($4); + GC::unlock(); + GC::lock(); + } + } + | item error_item_start +{ yyerror(&@2, parm, "unexpected item, expecting ';' or end of file"); YYERROR; } + | error ';' item + +doc_file_comments: + MZN_DOC_FILE_COMMENT + { + ParserState* pp = static_cast(parm); + if (pp->parseDocComments && $1) { + pp->model->addDocComment($1); + } + free($1); + } + | doc_file_comments MZN_DOC_FILE_COMMENT + { + ParserState* pp = static_cast(parm); + if (pp->parseDocComments && $2) { + pp->model->addDocComment($2); + } + free($2); + } + +semi_or_none : | ';' + +item : MZN_DOC_COMMENT item_tail + { $$ = $2; + ParserState* pp = static_cast(parm); + if (FunctionI* fi = Item::dyn_cast($$)) { + if (pp->parseDocComments) { + fi->ann().add(createDocComment(@1,$1)); + } + } else if (VarDeclI* vdi = Item::dyn_cast($$)) { + if (pp->parseDocComments) { + vdi->e()->addAnnotation(createDocComment(@1,$1)); + } + } else { + yyerror(&@2, parm, "documentation comments are only supported for function, predicate and variable declarations"); + } + free($1); + } + | item_tail + { $$ = $1; } + +item_tail : + include_item + { $$=notInDatafile(&@$,parm,"include") ? $1 : NULL; } + | vardecl_item + { $$=notInDatafile(&@$,parm,"variable declaration") ? $1 : NULL; } + | assign_item + | constraint_item + { $$=notInDatafile(&@$,parm,"constraint") ? $1 : NULL; } + | solve_item + { $$=notInDatafile(&@$,parm,"solve") ? $1 : NULL; } + | output_item + { $$=notInDatafile(&@$,parm,"output") ? $1 : NULL; } + | predicate_item + { $$=notInDatafile(&@$,parm,"predicate") ? $1 : NULL; } + | function_item + { $$=notInDatafile(&@$,parm,"predicate") ? $1 : NULL; } + | annotation_item + { $$=notInDatafile(&@$,parm,"annotation") ? $1 : NULL; } + +error_item_start : MZN_INCLUDE | MZN_ENUM | MZN_OUTPUT + | MZN_CONSTRAINT | MZN_SOLVE | MZN_PREDICATE | MZN_FUNCTION | MZN_TEST + | MZN_ANNOTATION + +include_item : + MZN_INCLUDE MZN_STRING_LITERAL + { ParserState* pp = static_cast(parm); + map::iterator ret = pp->seenModels.find($2); + IncludeI* ii = new IncludeI(@$,ASTString($2)); + $$ = ii; + if (ret == pp->seenModels.end()) { + Model* im = new Model; + im->setParent(pp->model); + im->setFilename($2); + string fpath = FileUtils::dir_name(pp->filename); + string fbase = FileUtils::base_name(pp->filename); + if (fpath=="") + fpath="./"; + ParseWorkItem pm(im, ii, fpath, $2); + pp->files.push_back(pm); + ii->m(im); + pp->seenModels.insert(pair($2,im)); + } else { + ii->m(ret->second, false); + } + free($2); + } + +vardecl_item : + ti_expr_and_id annotations + { if ($1 && $2) $1->addAnnotations(*$2); + if ($1) + $$ = new VarDeclI(@$,$1); + delete $2; + } + | ti_expr_and_id annotations MZN_EQ expr + { if ($1) $1->e($4); + if ($1 && $2) $1->addAnnotations(*$2); + if ($1) + $$ = new VarDeclI(@$,$1); + delete $2; + } + | MZN_ENUM MZN_IDENTIFIER + { + TypeInst* ti = new TypeInst(@$,Type::parsetint()); + ti->setIsEnum(true); + VarDecl* vd = new VarDecl(@$,ti,$2); + free($2); + $$ = new VarDeclI(@$,vd); + } + | MZN_ENUM MZN_IDENTIFIER MZN_EQ '{' enum_id_list '}' + { + TypeInst* ti = new TypeInst(@$,Type::parsetint()); + ti->setIsEnum(true); + SetLit* sl = new SetLit(@$, *$5); + VarDecl* vd = new VarDecl(@$,ti,$2,sl); + free($2); + delete $5; + $$ = new VarDeclI(@$,vd); + } + | MZN_ENUM MZN_IDENTIFIER MZN_EQ MZN_LEFT_BRACKET string_lit_list MZN_RIGHT_BRACKET + { + TypeInst* ti = new TypeInst(@$,Type::parsetint()); + ti->setIsEnum(true); + vector args; + args.push_back(new ArrayLit(@$,*$5)); + Call* sl = new Call(@$, constants().ids.anonEnumFromStrings, args); + VarDecl* vd = new VarDecl(@$,ti,$2,sl); + free($2); + delete $5; + $$ = new VarDeclI(@$,vd); + } + | MZN_ENUM MZN_IDENTIFIER MZN_EQ MZN_IDENTIFIER '(' expr ')' + { + TypeInst* ti = new TypeInst(@$,Type::parsetint()); + ti->setIsEnum(true); + vector args; + args.push_back($6); + Call* sl = new Call(@$, ASTString($4), args); + VarDecl* vd = new VarDecl(@$,ti,$2,sl); + free($2); + free($4); + $$ = new VarDeclI(@$,vd); + } + +string_lit_list : + // empty + { $$ = new std::vector(); } + | MZN_STRING_LITERAL + { $$ = new std::vector(); + $$->push_back(new StringLit(@$, $1)); free($1); + } + | string_lit_list ',' MZN_STRING_LITERAL + { $$ = $1; + if ($$) $$->push_back(new StringLit(@$, $3)); + free($3); + } + +enum_id_list : + // empty + { $$ = new std::vector(); } + | MZN_IDENTIFIER + { $$ = new std::vector(); + $$->push_back(new Id(@$,$1,NULL)); free($1); + } + | enum_id_list ',' MZN_IDENTIFIER + { $$ = $1; if ($$) $$->push_back(new Id(@$,$3,NULL)); free($3); } + +assign_item : + MZN_IDENTIFIER MZN_EQ expr + { $$ = new AssignI(@$,$1,$3); + free($1); + } + +constraint_item : + MZN_CONSTRAINT expr + { $$ = new ConstraintI(@$,$2);} + | MZN_CONSTRAINT MZN_COLONCOLON string_expr expr + { $$ = new ConstraintI(@$,$4); + if ($4 && $3) + $$->cast()->e()->ann().add(new Call(@2, ASTString("mzn_constraint_name"), {$3})); + } + +solve_item : + MZN_SOLVE annotations MZN_SATISFY + { $$ = SolveI::sat(@$); + if ($$ && $2) $$->cast()->ann().add(*$2); + delete $2; + } + | MZN_SOLVE annotations MZN_MINIMIZE expr + { $$ = SolveI::min(@$,$4); + if ($$ && $2) $$->cast()->ann().add(*$2); + delete $2; + } + | MZN_SOLVE annotations MZN_MAXIMIZE expr + { $$ = SolveI::max(@$,$4); + if ($$ && $2) $$->cast()->ann().add(*$2); + delete $2; + } + +output_item : + MZN_OUTPUT expr + { $$ = new OutputI(@$,$2);} + +predicate_item : + MZN_PREDICATE MZN_IDENTIFIER params annotations operation_item_tail + { if ($3) $$ = new FunctionI(@$,$2,new TypeInst(@$, + Type::varbool()),*$3,$5); + if ($$ && $4) $$->cast()->ann().add(*$4); + free($2); + delete $3; + delete $4; + } + | MZN_TEST MZN_IDENTIFIER params annotations operation_item_tail + { if ($3) $$ = new FunctionI(@$,$2,new TypeInst(@$, + Type::parbool()),*$3,$5); + if ($$ && $4) $$->cast()->ann().add(*$4); + free($2); + delete $3; + delete $4; + } + +function_item : + MZN_FUNCTION ti_expr ':' id_or_quoted_op params annotations operation_item_tail + { if ($5) $$ = new FunctionI(@$,$4,$2,*$5,$7); + if ($$ && $6) $$->cast()->ann().add(*$6); + free($4); + delete $5; + delete $6; + } + | ti_expr ':' MZN_IDENTIFIER '(' params_list ')' annotations operation_item_tail + { if ($5) $$ = new FunctionI(@$,$3,$1,*$5,$8); + if ($$ && $7) $$->cast()->ann().add(*$7); + free($3); + delete $5; + delete $7; + } + +annotation_item : + MZN_ANNOTATION MZN_IDENTIFIER params + { + TypeInst* ti=new TypeInst(@1,Type::ann()); + if ($3==NULL || $3->empty()) { + VarDecl* vd = new VarDecl(@$,ti,$2); + $$ = new VarDeclI(@$,vd); + } else { + $$ = new FunctionI(@$,$2,ti,*$3,NULL); + } + free($2); + delete $3; + } + | MZN_ANNOTATION MZN_IDENTIFIER params MZN_EQ expr + { TypeInst* ti=new TypeInst(@1,Type::ann()); + if ($3) $$ = new FunctionI(@$,$2,ti,*$3,$5); + delete $3; + } + +operation_item_tail : + /*empty*/ + { $$=NULL; } + | MZN_EQ expr + { $$=$2; } + +params : + /* empty */ + { $$=new vector(); } + | '(' params_list ')' + { $$=$2; } + | '(' error ')' + { $$=new vector(); } + +params_list : + /* empty */ + { $$=new vector(); } + | params_list_head comma_or_none + { $$=$1; } + +params_list_head : + ti_expr_and_id_or_anon + { $$=new vector(); + if ($1) $1->toplevel(false); + if ($1) $$->push_back($1); } + | params_list_head ',' ti_expr_and_id_or_anon + { $$=$1; + if ($3) $3->toplevel(false); + if ($1 && $3) $1->push_back($3); } + +comma_or_none : | ',' + +ti_expr_and_id_or_anon : + ti_expr_and_id + { $$=$1; } + | ti_expr + { if ($1) $$=new VarDecl(@$, $1, ""); } + +ti_expr_and_id : + ti_expr ':' MZN_IDENTIFIER + { if ($1 && $3) $$ = new VarDecl(@$, $1, $3); + free($3); + } + +ti_expr_list : ti_expr_list_head comma_or_none + { $$=$1; } + +ti_expr_list_head : + ti_expr + { $$=new vector(); $$->push_back($1); } + | ti_expr_list_head ',' ti_expr + { $$=$1; if ($1 && $3) $1->push_back($3); } + +ti_expr : + base_ti_expr + | MZN_ARRAY MZN_LEFT_BRACKET ti_expr_list MZN_RIGHT_BRACKET MZN_OF base_ti_expr + { + $$ = $6; + if ($$ && $3) $$->setRanges(*$3); + delete $3; + } + | MZN_LIST MZN_OF base_ti_expr + { + $$ = $3; + std::vector ti(1); + ti[0] = new TypeInst(@$,Type::parint()); + if ($$) $$->setRanges(ti); + } + +base_ti_expr : + base_ti_expr_tail + { $$ = $1; + } + | MZN_OPT base_ti_expr_tail + { $$ = $2; + if ($$) { + Type tt = $$->type(); + tt.ot(Type::OT_OPTIONAL); + $$->type(tt); + } + } + | MZN_PAR opt_opt base_ti_expr_tail + { $$ = $3; + if ($$ && $2) { + Type tt = $$->type(); + tt.ot(Type::OT_OPTIONAL); + $$->type(tt); + } + } + | MZN_VAR opt_opt base_ti_expr_tail + { $$ = $3; + if ($$) { + Type tt = $$->type(); + tt.ti(Type::TI_VAR); + if ($2) tt.ot(Type::OT_OPTIONAL); + $$->type(tt); + } + } + | opt_opt MZN_SET MZN_OF base_ti_expr_tail + { $$ = $4; + if ($$) { + Type tt = $$->type(); + tt.st(Type::ST_SET); + if ($1) tt.ot(Type::OT_OPTIONAL); + $$->type(tt); + } + } + | MZN_PAR opt_opt MZN_SET MZN_OF base_ti_expr_tail + { $$ = $5; + if ($$) { + Type tt = $$->type(); + tt.st(Type::ST_SET); + if ($2) tt.ot(Type::OT_OPTIONAL); + $$->type(tt); + } + } + | MZN_VAR opt_opt MZN_SET MZN_OF base_ti_expr_tail + { $$ = $5; + if ($$) { + Type tt = $$->type(); + tt.ti(Type::TI_VAR); + tt.st(Type::ST_SET); + if ($2) tt.ot(Type::OT_OPTIONAL); + $$->type(tt); + } + } + +opt_opt: + /* nothing */ + { $$ = false; } + | MZN_OPT + { $$ = true; } + +base_ti_expr_tail : + MZN_INT + { $$ = new TypeInst(@$,Type::parint()); } + | MZN_BOOL + { $$ = new TypeInst(@$,Type::parbool()); } + | MZN_FLOAT + { $$ = new TypeInst(@$,Type::parfloat()); } + | MZN_STRING + { $$ = new TypeInst(@$,Type::parstring()); } + | MZN_ANN + { $$ = new TypeInst(@$,Type::ann()); } + | set_expr + { if ($1) $$ = new TypeInst(@$,Type(),$1); } + | MZN_TI_IDENTIFIER + { $$ = new TypeInst(@$,Type::top(), + new TIId(@$, $1)); + free($1); + } + | MZN_TI_ENUM_IDENTIFIER + { $$ = new TypeInst(@$,Type::parint(), + new TIId(@$, $1)); + free($1); + } + +array_access_expr_list : array_access_expr_list_head comma_or_none + +array_access_expr_list_head : + array_access_expr + { $$=new std::vector; $$->push_back($1); } + | array_access_expr_list_head ',' array_access_expr + { $$=$1; if ($$ && $3) $$->push_back($3); } + +array_access_expr : + expr + { $$ = $1; } + | MZN_DOTDOT + { $$=new SetLit(@$, IntSetVal::a(-IntVal::infinity(),IntVal::infinity())); } + | MZN_DOTDOT expr + { if ($2==NULL) { + $$ = NULL; + } else if ($2->isa()) { + $$=new SetLit(@$, IntSetVal::a(-IntVal::infinity(),$2->cast()->v())); + } else { + $$=new BinOp(@$, IntLit::a(-IntVal::infinity()), BOT_DOTDOT, $2); + } + } + | expr MZN_DOTDOT + { if ($1==NULL) { + $$ = NULL; + } else if ($1->isa()) { + $$=new SetLit(@$, IntSetVal::a($1->cast()->v(),IntVal::infinity())); + } else { + $$=new BinOp(@$, $1, BOT_DOTDOT, IntLit::a(IntVal::infinity())); + } + } + + +expr_list : expr_list_head comma_or_none + +expr_list_head : + expr + { $$=new std::vector; $$->push_back($1); } + | expr_list_head ',' expr + { $$=$1; if ($$ && $3) $$->push_back($3); } + +/// + +set_expr : + expr_atom_head + | set_expr MZN_COLONCOLON annotation_expr + { if ($1 && $3) $1->addAnnotation($3); $$=$1; } + | set_expr MZN_UNION set_expr + { $$=new BinOp(@$, $1, BOT_UNION, $3); } + | set_expr MZN_DIFF set_expr + { $$=new BinOp(@$, $1, BOT_DIFF, $3); } + | set_expr MZN_SYMDIFF set_expr + { $$=new BinOp(@$, $1, BOT_SYMDIFF, $3); } + | set_expr MZN_DOTDOT set_expr + { if ($1==NULL || $3==NULL) { + $$ = NULL; + } else if ($1->isa() && $3->isa()) { + $$=new SetLit(@$, IntSetVal::a($1->cast()->v(),$3->cast()->v())); + } else { + $$=new BinOp(@$, $1, BOT_DOTDOT, $3); + } + } + | MZN_DOTDOT_QUOTED '(' expr ',' expr ')' + { if ($3==NULL || $5==NULL) { + $$ = NULL; + } else if ($3->isa() && $5->isa()) { + $$=new SetLit(@$, IntSetVal::a($3->cast()->v(),$5->cast()->v())); + } else { + $$=new BinOp(@$, $3, BOT_DOTDOT, $5); + } + } + | set_expr MZN_INTERSECT set_expr + { $$=new BinOp(@$, $1, BOT_INTERSECT, $3); } + | set_expr MZN_PLUSPLUS set_expr + { $$=new BinOp(@$, $1, BOT_PLUSPLUS, $3); } + | set_expr MZN_PLUS set_expr + { $$=new BinOp(@$, $1, BOT_PLUS, $3); } + | set_expr MZN_MINUS set_expr + { $$=new BinOp(@$, $1, BOT_MINUS, $3); } + | set_expr MZN_MULT set_expr + { $$=new BinOp(@$, $1, BOT_MULT, $3); } + | set_expr MZN_DIV set_expr + { $$=new BinOp(@$, $1, BOT_DIV, $3); } + | set_expr MZN_IDIV set_expr + { $$=new BinOp(@$, $1, BOT_IDIV, $3); } + | set_expr MZN_MOD set_expr + { $$=new BinOp(@$, $1, BOT_MOD, $3); } + | set_expr MZN_POW set_expr + { $$=new BinOp(@$, $1, BOT_POW, $3); } + | set_expr MZN_WEAK_PLUS set_expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~+"), args); + } + | set_expr MZN_WEAK_MINUS set_expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~-"), args); + } + | set_expr MZN_WEAK_MULT set_expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~*"), args); + } + | set_expr MZN_WEAK_EQ set_expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~="), args); + } + | set_expr MZN_QUOTED_IDENTIFIER set_expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, $2, args); + free($2); + } + | MZN_PLUS set_expr %prec MZN_NOT + { $$=new UnOp(@$, UOT_PLUS, $2); } + | MZN_MINUS set_expr %prec MZN_NOT + { if ($2 && $2->isa()) { + $$ = IntLit::a(-$2->cast()->v()); + } else if ($2 && $2->isa()) { + $$ = FloatLit::a(-$2->cast()->v()); + } else { + $$=new UnOp(@$, UOT_MINUS, $2); + } + } + +/// + +expr : + expr_atom_head + | expr MZN_COLONCOLON annotation_expr + { if ($1 && $3) $1->addAnnotation($3); $$=$1; } + | expr MZN_EQUIV expr + { $$=new BinOp(@$, $1, BOT_EQUIV, $3); } + | expr MZN_IMPL expr + { $$=new BinOp(@$, $1, BOT_IMPL, $3); } + | expr MZN_RIMPL expr + { $$=new BinOp(@$, $1, BOT_RIMPL, $3); } + | expr MZN_OR expr + { $$=new BinOp(@$, $1, BOT_OR, $3); } + | expr MZN_XOR expr + { $$=new BinOp(@$, $1, BOT_XOR, $3); } + | expr MZN_AND expr + { $$=new BinOp(@$, $1, BOT_AND, $3); } + | expr MZN_LE expr + { $$=new BinOp(@$, $1, BOT_LE, $3); } + | expr MZN_GR expr + { $$=new BinOp(@$, $1, BOT_GR, $3); } + | expr MZN_LQ expr + { $$=new BinOp(@$, $1, BOT_LQ, $3); } + | expr MZN_GQ expr + { $$=new BinOp(@$, $1, BOT_GQ, $3); } + | expr MZN_EQ expr + { $$=new BinOp(@$, $1, BOT_EQ, $3); } + | expr MZN_NQ expr + { $$=new BinOp(@$, $1, BOT_NQ, $3); } + | expr MZN_IN expr + { $$=new BinOp(@$, $1, BOT_IN, $3); } + | expr MZN_SUBSET expr + { $$=new BinOp(@$, $1, BOT_SUBSET, $3); } + | expr MZN_SUPERSET expr + { $$=new BinOp(@$, $1, BOT_SUPERSET, $3); } + | expr MZN_UNION expr + { $$=new BinOp(@$, $1, BOT_UNION, $3); } + | expr MZN_DIFF expr + { $$=new BinOp(@$, $1, BOT_DIFF, $3); } + | expr MZN_SYMDIFF expr + { $$=new BinOp(@$, $1, BOT_SYMDIFF, $3); } + | expr MZN_DOTDOT expr + { if ($1==NULL || $3==NULL) { + $$ = NULL; + } else if ($1->isa() && $3->isa()) { + $$=new SetLit(@$, IntSetVal::a($1->cast()->v(),$3->cast()->v())); + } else { + $$=new BinOp(@$, $1, BOT_DOTDOT, $3); + } + } + | MZN_DOTDOT_QUOTED '(' expr ',' expr ')' + { if ($3==NULL || $5==NULL) { + $$ = NULL; + } else if ($3->isa() && $5->isa()) { + $$=new SetLit(@$, IntSetVal::a($3->cast()->v(),$5->cast()->v())); + } else { + $$=new BinOp(@$, $3, BOT_DOTDOT, $5); + } + } + | expr MZN_INTERSECT expr + { $$=new BinOp(@$, $1, BOT_INTERSECT, $3); } + | expr MZN_PLUSPLUS expr + { $$=new BinOp(@$, $1, BOT_PLUSPLUS, $3); } + | expr MZN_PLUS expr + { $$=new BinOp(@$, $1, BOT_PLUS, $3); } + | expr MZN_MINUS expr + { $$=new BinOp(@$, $1, BOT_MINUS, $3); } + | expr MZN_MULT expr + { $$=new BinOp(@$, $1, BOT_MULT, $3); } + | expr MZN_DIV expr + { $$=new BinOp(@$, $1, BOT_DIV, $3); } + | expr MZN_IDIV expr + { $$=new BinOp(@$, $1, BOT_IDIV, $3); } + | expr MZN_MOD expr + { $$=new BinOp(@$, $1, BOT_MOD, $3); } + | expr MZN_POW expr + { $$=new BinOp(@$, $1, BOT_POW, $3); } + | expr MZN_WEAK_PLUS expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~+"), args); + } + | expr MZN_WEAK_MINUS expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~-"), args); + } + | expr MZN_WEAK_MULT expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~*"), args); + } + | expr MZN_WEAK_EQ expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, ASTString("~="), args); + } + | expr MZN_QUOTED_IDENTIFIER expr + { vector args; + args.push_back($1); args.push_back($3); + $$=new Call(@$, $2, args); + free($2); + } + | MZN_NOT expr %prec MZN_NOT + { $$=new UnOp(@$, UOT_NOT, $2); } + | MZN_PLUS expr %prec MZN_NOT + { if (($2 && $2->isa()) || ($2 && $2->isa())) { + $$ = $2; + } else { + $$=new UnOp(@$, UOT_PLUS, $2); + } + } + | MZN_MINUS expr %prec MZN_NOT + { if ($2 && $2->isa()) { + $$ = IntLit::a(-$2->cast()->v()); + } else if ($2 && $2->isa()) { + $$ = FloatLit::a(-$2->cast()->v()); + } else { + $$=new UnOp(@$, UOT_MINUS, $2); + } + } + + +expr_atom_head : + expr_atom_head_nonstring + { $$=$1; } + | string_expr + { $$=$1; } + +expr_atom_head_nonstring : + '(' expr ')' + { $$=$2; } + | '(' expr ')' array_access_tail + { if ($4) $$=createArrayAccess(@$, $2, *$4); delete $4; } + | MZN_IDENTIFIER + { $$=new Id(@$, $1, NULL); free($1); } + | MZN_IDENTIFIER array_access_tail + { if ($2) $$=createArrayAccess(@$, new Id(@1,$1,NULL), *$2); + free($1); delete $2; } + | MZN_UNDERSCORE + { $$=new AnonVar(@$); } + | MZN_UNDERSCORE array_access_tail + { if ($2) $$=createArrayAccess(@$, new AnonVar(@$), *$2); + delete $2; } + | MZN_BOOL_LITERAL + { $$=constants().boollit(($1!=0)); } + | MZN_INTEGER_LITERAL + { $$=IntLit::a($1); } + | MZN_INFINITY + { $$=IntLit::a(IntVal::infinity()); } + | MZN_FLOAT_LITERAL + { $$=FloatLit::a($1); } + | MZN_ABSENT + { $$=constants().absent; } + | set_literal + | set_literal array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + | set_comp + | set_comp array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + | simple_array_literal + | simple_array_literal array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + | simple_array_literal_2d + | simple_array_literal_2d array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + | simple_array_comp + | simple_array_comp array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + | if_then_else_expr + | if_then_else_expr array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + | let_expr + | call_expr + | call_expr array_access_tail + { if ($2) $$=createArrayAccess(@$, $1, *$2); + delete $2; } + +string_expr: + MZN_STRING_LITERAL + { $$=new StringLit(@$, $1); free($1); } + | MZN_STRING_QUOTE_START string_quote_rest + { $$=new BinOp(@$, new StringLit(@$, $1), BOT_PLUSPLUS, $2); + free($1); + } + +string_quote_rest: + expr_list_head MZN_STRING_QUOTE_END + { if ($1) $$=new BinOp(@$, new Call(@$, ASTString("format"), *$1), BOT_PLUSPLUS, new StringLit(@$,$2)); + free($2); + delete $1; + } + | expr_list_head MZN_STRING_QUOTE_MID string_quote_rest + { if ($1) $$=new BinOp(@$, new Call(@$, ASTString("format"), *$1), BOT_PLUSPLUS, + new BinOp(@$, new StringLit(@$,$2), BOT_PLUSPLUS, $3)); + free($2); + delete $1; + } + +array_access_tail : + MZN_LEFT_BRACKET array_access_expr_list MZN_RIGHT_BRACKET + { $$=new std::vector >(); + if ($2) { + $$->push_back(*$2); + delete $2; + } + } + | array_access_tail MZN_LEFT_BRACKET array_access_expr_list MZN_RIGHT_BRACKET + { $$=$1; + if ($$ && $3) { + $$->push_back(*$3); + delete $3; + } + } + +set_literal : + '{' '}' + { $$ = new SetLit(@$, std::vector()); } + | '{' expr_list '}' + { if ($2) $$ = new SetLit(@$, *$2); + delete $2; } + +set_comp : + '{' expr '|' comp_tail '}' + { if ($4) $$ = new Comprehension(@$, $2, *$4, true); + delete $4; + } + +comp_tail : + generator_list + { if ($1) $$=new Generators; $$->_g = *$1; delete $1; } + +generator_list : generator_list_head comma_or_none + +generator_list_head : + generator + { $$=new std::vector; if ($1) $$->push_back(*$1); delete $1; } + | generator_eq + { $$=new std::vector; if ($1) $$->push_back(*$1); delete $1; } + | generator_eq MZN_WHERE expr + { $$=new std::vector; + if ($1) $$->push_back(*$1); + if ($1 && $3) $$->push_back(Generator($$->size(),$3)); + delete $1; + } + | generator_list_head ',' generator + { $$=$1; if ($$ && $3) $$->push_back(*$3); delete $3; } + | generator_list_head ',' generator_eq + { $$=$1; if ($$ && $3) $$->push_back(*$3); delete $3; } + | generator_list_head ',' generator_eq MZN_WHERE expr + { $$=$1; + if ($$ && $3) $$->push_back(*$3); + if ($$ && $3 && $5) $$->push_back(Generator($$->size(),$5)); + delete $3; + } + +generator : + id_list MZN_IN expr + { if ($1 && $3) $$=new Generator(*$1,$3,NULL); else $$=NULL; delete $1; } + | id_list MZN_IN expr MZN_WHERE expr + { if ($1 && $3) $$=new Generator(*$1,$3,$5); else $$=NULL; delete $1; } +generator_eq : + MZN_IDENTIFIER MZN_EQ expr + { if ($3) $$=new Generator({$1},NULL,$3); else $$=NULL; free($1); } + +id_list : id_list_head comma_or_none + +id_list_head : + MZN_IDENTIFIER + { $$=new std::vector; $$->push_back($1); free($1); } + | id_list_head ',' MZN_IDENTIFIER + { $$=$1; if ($$ && $3) $$->push_back($3); free($3); } + +simple_array_literal : + MZN_LEFT_BRACKET MZN_RIGHT_BRACKET + { $$=new ArrayLit(@$, std::vector()); } + | MZN_LEFT_BRACKET expr_list MZN_RIGHT_BRACKET + { if ($2) $$=new ArrayLit(@$, *$2); delete $2; } + +simple_array_literal_2d : + MZN_LEFT_2D_BRACKET MZN_RIGHT_2D_BRACKET + { $$=new ArrayLit(@$, std::vector >()); } + | MZN_LEFT_2D_BRACKET simple_array_literal_2d_list MZN_RIGHT_2D_BRACKET + { if ($2) { + $$=new ArrayLit(@$, *$2); + for (unsigned int i=1; i<$2->size(); i++) + if ((*$2)[i].size() != (*$2)[i-1].size()) + yyerror(&@2, parm, "syntax error, all sub-arrays of 2d array literal must have the same length"); + delete $2; + } else { + $$ = NULL; + } + } + | MZN_LEFT_2D_BRACKET simple_array_literal_2d_list '|' MZN_RIGHT_2D_BRACKET + { if ($2) { + $$=new ArrayLit(@$, *$2); + for (unsigned int i=1; i<$2->size(); i++) + if ((*$2)[i].size() != (*$2)[i-1].size()) + yyerror(&@2, parm, "syntax error, all sub-arrays of 2d array literal must have the same length"); + delete $2; + } else { + $$ = NULL; + } + } + | MZN_LEFT_2D_BRACKET simple_array_literal_3d_list MZN_RIGHT_2D_BRACKET + { + if ($2) { + std::vector > dims(3); + dims[0] = std::pair(1,static_cast($2->size())); + if ($2->size()==0) { + dims[1] = std::pair(1,0); + dims[2] = std::pair(1,0); + } else { + dims[1] = std::pair(1,static_cast((*$2)[0].size())); + if ((*$2)[0].size()==0) { + dims[2] = std::pair(1,0); + } else { + dims[2] = std::pair(1,static_cast((*$2)[0][0].size())); + } + } + std::vector a; + for (int i=0; i > >; + } + | '|' simple_array_literal_2d_list '|' + { $$=new std::vector > >; + if ($2) $$->push_back(*$2); + delete $2; + } + | simple_array_literal_3d_list ',' '|' simple_array_literal_2d_list '|' + { $$=$1; + if ($$ && $4) $$->push_back(*$4); + delete $4; + } + +simple_array_literal_2d_list : + expr_list + { $$=new std::vector >; + if ($1) $$->push_back(*$1); + delete $1; + } + | simple_array_literal_2d_list '|' expr_list + { $$=$1; if ($$ && $3) $$->push_back(*$3); delete $3; } + +simple_array_comp : + MZN_LEFT_BRACKET expr '|' comp_tail MZN_RIGHT_BRACKET + { if ($4) $$=new Comprehension(@$, $2, *$4, false); + delete $4; + } + +if_then_else_expr : + MZN_IF expr MZN_THEN expr MZN_ENDIF + { + std::vector iexps; + iexps.push_back($2); + iexps.push_back($4); + $$=new ITE(@$, iexps, NULL); + } + | MZN_IF expr MZN_THEN expr elseif_list MZN_ELSE expr MZN_ENDIF + { + std::vector iexps; + iexps.push_back($2); + iexps.push_back($4); + if ($5) { + for (unsigned int i=0; i<$5->size(); i+=2) { + iexps.push_back((*$5)[i]); + iexps.push_back((*$5)[i+1]); + } + } + $$=new ITE(@$, iexps,$7); + delete $5; + } + +elseif_list : + { $$=new std::vector; } + | elseif_list MZN_ELSEIF expr MZN_THEN expr + { $$=$1; if ($$ && $3 && $5) { $$->push_back($3); $$->push_back($5); } } + +quoted_op : + MZN_EQUIV_QUOTED + { $$=BOT_EQUIV; } + | MZN_IMPL_QUOTED + { $$=BOT_IMPL; } + | MZN_RIMPL_QUOTED + { $$=BOT_RIMPL; } + | MZN_OR_QUOTED + { $$=BOT_OR; } + | MZN_XOR_QUOTED + { $$=BOT_XOR; } + | MZN_AND_QUOTED + { $$=BOT_AND; } + | MZN_LE_QUOTED + { $$=BOT_LE; } + | MZN_GR_QUOTED + { $$=BOT_GR; } + | MZN_LQ_QUOTED + { $$=BOT_LQ; } + | MZN_GQ_QUOTED + { $$=BOT_GQ; } + | MZN_EQ_QUOTED + { $$=BOT_EQ; } + | MZN_NQ_QUOTED + { $$=BOT_NQ; } + | MZN_IN_QUOTED + { $$=BOT_IN; } + | MZN_SUBSET_QUOTED + { $$=BOT_SUBSET; } + | MZN_SUPERSET_QUOTED + { $$=BOT_SUPERSET; } + | MZN_UNION_QUOTED + { $$=BOT_UNION; } + | MZN_DIFF_QUOTED + { $$=BOT_DIFF; } + | MZN_SYMDIFF_QUOTED + { $$=BOT_SYMDIFF; } + | MZN_PLUS_QUOTED + { $$=BOT_PLUS; } + | MZN_MINUS_QUOTED + { $$=BOT_MINUS; } + | MZN_MULT_QUOTED + { $$=BOT_MULT; } + | MZN_POW_QUOTED + { $$=BOT_POW; } + | MZN_DIV_QUOTED + { $$=BOT_DIV; } + | MZN_IDIV_QUOTED + { $$=BOT_IDIV; } + | MZN_MOD_QUOTED + { $$=BOT_MOD; } + | MZN_INTERSECT_QUOTED + { $$=BOT_INTERSECT; } + | MZN_PLUSPLUS_QUOTED + { $$=BOT_PLUSPLUS; } + | MZN_NOT_QUOTED + { $$=-1; } + +quoted_op_call : + quoted_op '(' expr ',' expr ')' + { if ($1==-1) { + $$=NULL; + yyerror(&@3, parm, "syntax error, unary operator with two arguments"); + } else { + $$=new BinOp(@$, $3,static_cast($1),$5); + } + } + | quoted_op '(' expr ')' + { int uot=-1; + switch ($1) { + case -1: + uot = UOT_NOT; + break; + case BOT_MINUS: + uot = UOT_MINUS; + break; + case BOT_PLUS: + uot = UOT_PLUS; + break; + default: + yyerror(&@3, parm, "syntax error, binary operator with unary argument list"); + break; + } + if (uot==-1) + $$=NULL; + else { + if (uot==UOT_PLUS && $3 && ($3->isa() || $3->isa())) { + $$ = $3; + } else if (uot==UOT_MINUS && $3 && $3->isa()) { + $$ = IntLit::a(-$3->cast()->v()); + } else if (uot==UOT_MINUS && $3 && $3->isa()) { + $$ = FloatLit::a(-$3->cast()->v()); + } else { + $$=new UnOp(@$, static_cast(uot),$3); + } + } + } + +call_expr : + MZN_IDENTIFIER '(' ')' + { $$=new Call(@$, $1, std::vector()); free($1); } + | quoted_op_call + | MZN_IDENTIFIER '(' comp_or_expr ')' + { + if ($3!=NULL) { + bool hadWhere = false; + std::vector args; + for (unsigned int i=0; i<$3->size(); i++) { + if ((*$3)[i].second) { + yyerror(&@3, parm, "syntax error, 'where' expression outside generator call"); + hadWhere = true; + $$=NULL; + } + args.push_back((*$3)[i].first); + } + if (!hadWhere) { + $$=new Call(@$, $1, args); + } + } + free($1); + delete $3; + } + | MZN_IDENTIFIER '(' comp_or_expr ')' '(' expr ')' + { + vector gens; + vector ids; + if ($3) { + for (unsigned int i=0; i<$3->size(); i++) { + if (Id* id = Expression::dyn_cast((*$3)[i].first)) { + if ((*$3)[i].second) { + ParserLocation loc = (*$3)[i].second->loc().parserLocation(); + yyerror(&loc, parm, "illegal where expression in generator call"); + } + ids.push_back(id); + } else { + if (BinOp* boe = Expression::dyn_cast((*$3)[i].first)) { + if (boe->lhs() && boe->rhs()) { + Id* id = Expression::dyn_cast(boe->lhs()); + if (id && boe->op() == BOT_IN) { + ids.push_back(id); + gens.push_back(Generator(ids,boe->rhs(),(*$3)[i].second)); + ids = vector(); + } else if (id && boe->op() == BOT_EQ && ids.empty()) { + ids.push_back(id); + gens.push_back(Generator(ids,NULL,boe->rhs())); + if ((*$3)[i].second) { + gens.push_back(Generator(gens.size(),(*$3)[i].second)); + } + ids = vector(); + } else { + ParserLocation loc = (*$3)[i].first->loc().parserLocation(); + yyerror(&loc, parm, "illegal expression in generator call"); + } + } + } else { + ParserLocation loc = (*$3)[i].first->loc().parserLocation(); + yyerror(&loc, parm, "illegal expression in generator call"); + } + } + } + } + if (ids.size() != 0) { + yyerror(&@3, parm, "illegal expression in generator call"); + } + ParserState* pp = static_cast(parm); + if (pp->hadError) { + $$=NULL; + } else { + Generators g; g._g = gens; + Comprehension* ac = new Comprehension(@$, $6,g,false); + vector args; args.push_back(ac); + $$=new Call(@$, $1, args); + } + free($1); + delete $3; + } + +comp_or_expr : comp_or_expr_head comma_or_none + +comp_or_expr_head : + expr + { $$=new vector >; + if ($1) { + $$->push_back(pair($1,NULL)); + } + } + | expr MZN_WHERE expr + { $$=new vector >; + if ($1 && $3) { + $$->push_back(pair($1,$3)); + } + } + | comp_or_expr_head ',' expr + { $$=$1; if ($$ && $3) $$->push_back(pair($3,NULL)); } + | comp_or_expr_head ',' expr MZN_WHERE expr + { $$=$1; if ($$ && $3 && $5) $$->push_back(pair($3,$5)); } + +let_expr : + MZN_LET '{' let_vardecl_item_list '}' MZN_IN expr %prec PREC_ANNO + { if ($3 && $6) { + $$=new Let(@$, *$3, $6); delete $3; + } else { + $$=NULL; + } + } + | MZN_LET '{' let_vardecl_item_list comma_or_semi '}' MZN_IN expr %prec PREC_ANNO + { if ($3 && $7) { + $$=new Let(@$, *$3, $7); delete $3; + } else { + $$=NULL; + } + } + +let_vardecl_item_list : + let_vardecl_item + { $$=new vector; $$->push_back($1); } + | constraint_item + { $$=new vector; + if ($1) { + ConstraintI* ce = $1->cast(); + $$->push_back(ce->e()); + ce->e(NULL); + } + } + | let_vardecl_item_list comma_or_semi let_vardecl_item + { $$=$1; if ($$ && $3) $$->push_back($3); } + | let_vardecl_item_list comma_or_semi constraint_item + { $$=$1; + if ($$ && $3) { + ConstraintI* ce = $3->cast(); + $$->push_back(ce->e()); + ce->e(NULL); + } + } + +comma_or_semi : ',' | ';' + +let_vardecl_item : + ti_expr_and_id annotations + { $$ = $1; + if ($$) $$->toplevel(false); + if ($$ && $2) $$->addAnnotations(*$2); + delete $2; + } + | ti_expr_and_id annotations MZN_EQ expr + { if ($1) $1->e($4); + $$ = $1; + if ($$) $$->loc(@$); + if ($$) $$->toplevel(false); + if ($$ && $2) $$->addAnnotations(*$2); + delete $2; + } + +annotations : + /* empty */ + { $$=NULL; } + | ne_annotations + +annotation_expr : + expr_atom_head_nonstring + { $$ = $1; } + | string_expr + { $$ = new Call(@1, ASTString("mzn_expression_name"), {$1}); } + +ne_annotations : + MZN_COLONCOLON annotation_expr + { $$=new std::vector(1); + (*$$)[0] = $2; + } + | ne_annotations MZN_COLONCOLON annotation_expr + { $$=$1; if ($$) $$->push_back($3); } + +id_or_quoted_op : + MZN_IDENTIFIER + { $$=$1; } + | MZN_EQUIV_QUOTED + { $$=strdup("'<->'"); } + | MZN_IMPL_QUOTED + { $$=strdup("'->'"); } + | MZN_RIMPL_QUOTED + { $$=strdup("'<-'"); } + | MZN_OR_QUOTED + { $$=strdup("'\\/'"); } + | MZN_XOR_QUOTED + { $$=strdup("'xor'"); } + | MZN_AND_QUOTED + { $$=strdup("'/\\'"); } + | MZN_LE_QUOTED + { $$=strdup("'<'"); } + | MZN_GR_QUOTED + { $$=strdup("'>'"); } + | MZN_LQ_QUOTED + { $$=strdup("'<='"); } + | MZN_GQ_QUOTED + { $$=strdup("'>='"); } + | MZN_EQ_QUOTED + { $$=strdup("'='"); } + | MZN_NQ_QUOTED + { $$=strdup("'!='"); } + | MZN_IN_QUOTED + { $$=strdup("'in'"); } + | MZN_SUBSET_QUOTED + { $$=strdup("'subset'"); } + | MZN_SUPERSET_QUOTED + { $$=strdup("'superset'"); } + | MZN_UNION_QUOTED + { $$=strdup("'union'"); } + | MZN_DIFF_QUOTED + { $$=strdup("'diff'"); } + | MZN_SYMDIFF_QUOTED + { $$=strdup("'symdiff'"); } + | MZN_DOTDOT_QUOTED + { $$=strdup("'..'"); } + | MZN_PLUS_QUOTED + { $$=strdup("'+'"); } + | MZN_MINUS_QUOTED + { $$=strdup("'-'"); } + | MZN_MULT_QUOTED + { $$=strdup("'*'"); } + | MZN_POW_QUOTED + { $$=strdup("'^'"); } + | MZN_DIV_QUOTED + { $$=strdup("'/'"); } + | MZN_IDIV_QUOTED + { $$=strdup("'div'"); } + | MZN_MOD_QUOTED + { $$=strdup("'mod'"); } + | MZN_INTERSECT_QUOTED + { $$=strdup("'intersect'"); } + | MZN_NOT_QUOTED + { $$=strdup("'not'"); } + | MZN_PLUSPLUS_QUOTED + { $$=strdup("'++'"); } diff --git a/lib/passes/compile_pass.cpp b/lib/passes/compile_pass.cpp new file mode 100644 index 0000000..76079f8 --- /dev/null +++ b/lib/passes/compile_pass.cpp @@ -0,0 +1,171 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Kevin Leo + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace MiniZinc { + +using std::string; +using std::vector; + +Env* changeLibrary(Env& e, vector& includePaths, string globals_dir, + CompilePassFlags& compflags, bool verbose = false) { + GCLock lock; + CopyMap cm; + Model* m = e.envi().orig_model ? e.envi().orig_model : e.envi().model; + Model* new_mod = new Model(); + new_mod->setFilename(m->filename().str()); + new_mod->setFilepath(m->filepath().str()); + + vector new_includePaths; + + if (std::find(includePaths.begin(), includePaths.end(), globals_dir) == includePaths.end()) + new_includePaths.push_back(globals_dir); + new_includePaths.insert(new_includePaths.end(), includePaths.begin(), includePaths.end()); + + // Collect include items + vector include_names; + for (Item* item : *m) { + if (IncludeI* inc = item->dyn_cast()) { + include_names.push_back(inc->f().str()); + } else { + new_mod->addItem(copy(e.envi(), cm, item)); + } + } + + std::stringstream ss; + for (auto& name : include_names) ss << "include \"" << name << "\";"; + + vector syntax_errors; + Env* fenv = new Env(new_mod); + // Model* inc_mod = parse(*fenv, include_names, {}, new_includePaths, true, true, verbose, + // std::cerr); + Model* inc_mod = parseFromString(*fenv, ss.str(), m->filepath().str() + "_Dummy.mzn", + new_includePaths, true, true, verbose, std::cerr, syntax_errors); + if (inc_mod == nullptr) { + for (const SyntaxError& se : syntax_errors) { + std::cerr << std::endl; + std::cerr << se.what() << ": " << se.msg() << std::endl; + std::cerr << se.loc() << std::endl; + } + return nullptr; + } + IncludeI* new_inc = new IncludeI(Location().introduce(), string("MultiPassDummy.mzn")); + new_inc->m(inc_mod); + inc_mod->setParent(new_mod); + new_mod->addItem(new_inc); + + return fenv; +} + +CompilePass::CompilePass(Env* e, FlatteningOptions& opts, CompilePassFlags& cflags, + string globals_library, vector include_paths, bool change_lib, + bool ignore_unknown) + : env(e), + fopts(opts), + compflags(cflags), + library(globals_library), + includePaths(include_paths), + change_library(change_lib), + ignore_unknown_ids(ignore_unknown) {} + +Env* CompilePass::run(Env* store, std::ostream& log) { + Timer lasttime; + if (compflags.verbose) log << "\n\tCompilePass: Flatten with \'" << library << "\' library ...\n"; + + Env* new_env; + if (change_library) { + new_env = changeLibrary(*env, includePaths, library, compflags, compflags.verbose); + if (new_env == nullptr) return nullptr; + new_env->envi().copyPathMapsAndState(store->envi()); + } else { + new_env = env; + } + new_env->envi().ignoreUnknownIds = ignore_unknown_ids; + + vector typeErrors; + MiniZinc::typecheck(*new_env, new_env->model(), typeErrors, + compflags.model_check_only || compflags.model_interface_only, + compflags.allow_multi_assign); + if (typeErrors.size() > 0) { + std::ostringstream errstream; + for (unsigned int i = 0; i < typeErrors.size(); i++) { + errstream << typeErrors[i].what() << ": " << typeErrors[i].msg() << std::endl; + errstream << typeErrors[i].loc() << std::endl; + } + throw Error(errstream.str()); + } + + registerBuiltins(*new_env); + + try { + flatten(*new_env, fopts); + } catch (LocationException& e) { + if (compflags.verbose) log << std::endl; + std::ostringstream errstream; + errstream << e.what() << ": " << std::endl; + new_env->dumpErrorStack(errstream); + errstream << " " << e.msg() << std::endl; + throw Error(errstream.str()); + } + + if (!compflags.noMIPdomains) { + if (compflags.verbose) log << "MIP domains ..."; + MIPdomains(*new_env, compflags.statistics); + if (compflags.verbose) log << " done (" << lasttime.stoptime() << ")" << std::endl; + } + + if (compflags.optimize) { + if (compflags.verbose) log << "Optimizing ..."; + optimize(*new_env, compflags.chain_compression); + if (compflags.verbose) log << " done (" << lasttime.stoptime() << ")" << std::endl; + } + + for (unsigned int i = 0; i < new_env->warnings().size(); i++) { + log << (compflags.werror ? "\n ERROR: " : "\n WARNING: ") << new_env->warnings()[i]; + } + if (compflags.werror && new_env->warnings().size() > 0) { + throw Error("errors encountered"); + } + new_env->clearWarnings(); + + if (!compflags.newfzn) { + if (compflags.verbose) log << "Converting to old FlatZinc ..."; + oldflatzinc(*new_env); + if (compflags.verbose) log << " done (" << lasttime.stoptime() << ")" << std::endl; + } else { + new_env->flat()->compact(); + new_env->output()->compact(); + } + + if (compflags.verbose) log << " done (" << lasttime.stoptime() << ")" << std::endl; + + return new_env; +} + +CompilePass::~CompilePass(){}; + +} // namespace MiniZinc diff --git a/lib/passes/gecode_pass.cpp b/lib/passes/gecode_pass.cpp new file mode 100644 index 0000000..30ed383 --- /dev/null +++ b/lib/passes/gecode_pass.cpp @@ -0,0 +1,31 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Kevin Leo + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +GecodePass::GecodePass(GecodeOptions* g_opts) : gopts(g_opts) {} + +Env* GecodePass::run(Env* env, std::ostream& log) { + // try { + // GecodeSolverInstance gecode(*env,log,new GecodeOptions(*gopts)); + // gecode.processFlatZinc(); + // gecode.presolve(env->flat()); + // } catch(InternalError e) { + // std::cerr << "Warning during presolve: " << e.msg() << std::endl; + std::cerr << "NO LONGER IMPLEMENTED" << std::endl; + + // } + return env; +} +} // namespace MiniZinc diff --git a/lib/pathfileprinter.cpp b/lib/pathfileprinter.cpp new file mode 100644 index 0000000..679723b --- /dev/null +++ b/lib/pathfileprinter.cpp @@ -0,0 +1,177 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#include + +namespace MiniZinc { + +using std::string; +using std::vector; + +PathFilePrinter::PathFilePrinter(std::ostream& o, EnvI&) : os(o), constraint_index(0) {} + +void PathFilePrinter::addBetterName(Id* id, string name, string path, bool overwrite = false) { + string oname; + string opath; + + NameMap::iterator it = betternames.find(id); + if (it != betternames.end()) { + oname = it->second.first; + opath = it->second.second; + } + + if (!name.empty() && (overwrite || oname.empty())) oname = name; + if (!path.empty() && (overwrite || opath.empty())) opath = path; + + betternames[id] = NamePair(oname, opath); +} + +string path2name(string path) { + std::stringstream name; + + size_t idpos = path.rfind("id:"); + if (idpos != string::npos) { + idpos += 3; + size_t semi = path.find(";", idpos); + if (semi != string::npos) { + // Variable name + name << path.substr(idpos, semi - idpos); + + // Check for array + int dim = 0; + size_t ilpos = semi - idpos; + do { + ilpos = path.find("il:", ilpos); + if (ilpos != string::npos) { + ilpos += 3; + semi = path.find(";", ilpos); + if (semi != string::npos) { + if (dim == 0) + name << "["; + else + name << ","; + name << path.substr(ilpos, semi - ilpos); + dim++; + } + } + } while (ilpos != string::npos); + + if (dim > 0) name << "?]"; + + // Check for anon + if (path.find(":anon") != string::npos || path.find("=") != string::npos) { + name.str(""); + name.clear(); + } + } + } + + return name.str(); +} + +void PathFilePrinter::print(Model* m) { + // Build map + for (VarDeclIterator vdit = m->begin_vardecls(); vdit != m->end_vardecls(); ++vdit) { + VarDecl* e = vdit->e(); + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) { + if (Call* ca = (*it)->dyn_cast()) { + ASTString cid = ca->id(); + if (cid == constants().ann.output_array) { + if (ArrayLit* rhs = e->e()->dyn_cast()) { + for (unsigned int ind = 0; ind < rhs->size(); ind++) { + if (Id* id = (*rhs)[ind]->dyn_cast()) { + std::stringstream bettername; + bettername << *e->id() << "["; + + // Array of sets + ArrayLit& dimsets = *ca->arg(0)->cast(); + vector dims(dimsets.size(), 1); + for (unsigned int i = 0; i < dimsets.size(); i++) { + SetLit* sl = dimsets[i]->cast(); + dims[i] = sl->isv()->card(); + } + vector dimspan(dims.size(), 1); + for (unsigned int i = 0; i < dims.size(); i++) + for (unsigned int j = i + 1; j < dims.size(); j++) dimspan[i] *= dims[j]; + + IntVal curind = ind; + for (unsigned int i = 0; i < dims.size() - 1; i++) { + IntVal thisind = curind / dimspan[i]; + curind -= thisind * dimspan[i]; + bettername << dimsets[i]->cast()->isv()->min() + thisind << ","; + } + bettername << dimsets[dimsets.size() - 1]->cast()->isv()->min() + curind + << "]"; + + addBetterName(id, bettername.str(), "", true); + } + } + } + } else if (ca->id() == constants().ann.mzn_path) { + StringLit* sl = ca->arg(0)->cast(); + addBetterName(e->id(), path2name(sl->v().str()), sl->v().str()); + } + } + } + } + + // Print values + for (Item* item : *m) print(item); +} + +void PathFilePrinter::print(Item* item) { + if (VarDeclI* vdi = item->dyn_cast()) { + Id* id = vdi->e()->id(); + NamePair np = betternames[id]; + if (!np.first.empty() || !np.second.empty()) { + // FlatZinc name + os << *id << "\t"; + + // Nice name + if (np.first.empty()) { + os << *id << "\t"; + } else { + string name = np.first; + os << name; + if (name.find("?") != string::npos) os << "(" << *id << ")"; + os << "\t"; + } + + // Path + os << np.second << std::endl; + } + } else if (ConstraintI* ci = item->dyn_cast()) { + StringLit* sl = nullptr; + Expression* e = ci->e(); + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) { + if (Call* ca = (*it)->dyn_cast()) { + ASTString cid = ca->id(); + if (cid == constants().ann.mzn_path) { + sl = ca->arg(0)->cast(); + } + } + } + + os << constraint_index << "\t"; + os << constraint_index << "\t"; + if (sl) { + os << sl->v(); + } else { + os << ""; + } + os << std::endl; + constraint_index++; + } +} +} // namespace MiniZinc diff --git a/lib/prettyprinter.cpp b/lib/prettyprinter.cpp new file mode 100644 index 0000000..f70c13c --- /dev/null +++ b/lib/prettyprinter.cpp @@ -0,0 +1,1919 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Pierre Wilke + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +int precedence(const Expression* e) { + if (const BinOp* bo = e->dyn_cast()) { + switch (bo->op()) { + case BOT_EQUIV: + return 1200; + case BOT_IMPL: + return 1100; + case BOT_RIMPL: + return 1100; + case BOT_OR: + return 1000; + case BOT_XOR: + return 1000; + case BOT_AND: + return 900; + case BOT_LE: + return 800; + case BOT_LQ: + return 800; + case BOT_GR: + return 800; + case BOT_GQ: + return 800; + case BOT_EQ: + return 800; + case BOT_NQ: + return 800; + case BOT_IN: + return 700; + case BOT_SUBSET: + return 700; + case BOT_SUPERSET: + return 700; + case BOT_UNION: + return 600; + case BOT_DIFF: + return 600; + case BOT_SYMDIFF: + return 600; + case BOT_DOTDOT: + return 500; + case BOT_PLUS: + return 400; + case BOT_MINUS: + return 400; + case BOT_MULT: + return 300; + case BOT_IDIV: + return 300; + case BOT_MOD: + return 300; + case BOT_DIV: + return 300; + case BOT_INTERSECT: + return 300; + case BOT_POW: + return 200; + case BOT_PLUSPLUS: + return 200; + default: + assert(false); + return -1; + } + + } else if (e->isa()) { + return 1300; + + } else { + return 0; + } +} + +enum Assoc { AS_LEFT, AS_RIGHT, AS_NONE }; + +Assoc assoc(const BinOp* bo) { + switch (bo->op()) { + case BOT_LE: + case BOT_LQ: + case BOT_GR: + case BOT_GQ: + case BOT_NQ: + case BOT_EQ: + case BOT_IN: + case BOT_SUBSET: + case BOT_SUPERSET: + case BOT_DOTDOT: + return AS_NONE; + case BOT_PLUSPLUS: + return AS_RIGHT; + default: + return AS_LEFT; + } +} + +enum Parentheses { PN_LEFT = 1, PN_RIGHT = 2 }; + +Parentheses needParens(const BinOp* bo, const Expression* left, const Expression* right) { + int pbo = precedence(bo); + int pl = precedence(left); + int pr = precedence(right); + int ret = (pbo < pl) || (pbo == pl && assoc(bo) != AS_LEFT); + ret += 2 * ((pbo < pr) || (pbo == pr && assoc(bo) != AS_RIGHT)); + return static_cast(ret); +} + +std::string Printer::escapeStringLit(const ASTString& s) { + const char* sc = s.c_str(); + std::ostringstream ret; + for (unsigned int i = 0; i < s.size(); i++) { + switch (sc[i]) { + case '\n': + ret << "\\n"; + break; + case '\t': + ret << "\\t"; + break; + case '"': + ret << "\\\""; + break; + case '\\': + ret << "\\\\"; + break; + default: + ret << sc[i]; + } + } + return ret.str(); +} + +void ppFloatVal(std::ostream& os, const FloatVal& fv, bool hexFloat) { + std::ostringstream oss; + if (fv.isFinite()) { + if (hexFloat) { + throw InternalError("disabled due to hexfloat being not supported by g++ 4.9"); + // std::hexfloat(oss); + oss << fv.toDouble(); + os << oss.str(); + } else { + oss << std::setprecision(std::numeric_limits::digits10 + 1); + oss << fv; + if (oss.str().find("e") == std::string::npos && oss.str().find(".") == std::string::npos) + oss << ".0"; + os << oss.str(); + } + } else { + if (fv.isPlusInfinity()) + os << "infinity"; + else + os << "-infinity"; + } +} + +class PlainPrinter { +public: + EnvI* env; + std::ostream& os; + bool _flatZinc; + PlainPrinter(std::ostream& os0, bool flatZinc, EnvI* env0) + : env(env0), os(os0), _flatZinc(flatZinc) {} + + void p(const Type& type, const Expression* e) { + switch (type.ti()) { + case Type::TI_PAR: + break; + case Type::TI_VAR: + os << "var "; + break; + } + if (type.ot() == Type::OT_OPTIONAL) os << "opt "; + if (type.st() == Type::ST_SET) os << "set of "; + if (e == NULL) { + switch (type.bt()) { + case Type::BT_INT: + os << "int"; + break; + case Type::BT_BOOL: + os << "bool"; + break; + case Type::BT_FLOAT: + os << "float"; + break; + case Type::BT_STRING: + os << "string"; + break; + case Type::BT_ANN: + os << "ann"; + break; + case Type::BT_BOT: + os << "bot"; + break; + case Type::BT_TOP: + os << "top"; + break; + case Type::BT_UNKNOWN: + os << "???"; + break; + } + } else { + p(e); + } + } + + void p(const Annotation& ann) { + for (ExpressionSetIter it = ann.begin(); it != ann.end(); ++it) { + os << ":: "; + p(*it); + } + } + + void p(const Expression* e) { + if (e == NULL) return; + switch (e->eid()) { + case Expression::E_INTLIT: + os << e->cast()->v(); + break; + case Expression::E_FLOATLIT: { + ppFloatVal(os, e->cast()->v()); + } break; + case Expression::E_SETLIT: { + const SetLit& sl = *e->cast(); + if (sl.isv()) { + if (sl.isv()->size() == 0) { + os << (_flatZinc ? "1..0" : "{}"); + } else if (sl.isv()->size() == 1) { + os << sl.isv()->min(0) << ".." << sl.isv()->max(0); + } else { + if (!sl.isv()->min(0).isFinite()) + os << sl.isv()->min(0) << ".." << sl.isv()->max(0) << "++"; + os << "{"; + for (IntSetRanges isr(sl.isv()); isr();) { + if (isr.min().isFinite() && isr.max().isFinite()) { + for (IntVal i = isr.min(); i <= isr.max(); i++) { + os << i; + if (i < isr.max()) os << ","; + } + ++isr; + if (isr()) os << ","; + } + } + os << "}"; + if (!sl.isv()->max(sl.isv()->size() - 1).isFinite()) + os << "++" << sl.isv()->min(sl.isv()->size() - 1) << ".." + << sl.isv()->max(sl.isv()->size() - 1); + } + } else if (sl.fsv()) { + if (sl.fsv()->size() == 0) { + os << (_flatZinc ? "1.0..0.0" : "{}"); + } else if (sl.fsv()->size() == 1) { + ppFloatVal(os, sl.fsv()->min(0)); + os << ".."; + ppFloatVal(os, sl.fsv()->max(0)); + } else { + bool allSingleton = true; + for (FloatSetRanges isr(sl.fsv()); isr(); ++isr) { + if (isr.min() != isr.max()) { + allSingleton = false; + break; + } + } + if (allSingleton) { + os << "{"; + bool first = true; + for (FloatSetRanges isr(sl.fsv()); isr(); ++isr) { + if (!first) os << ","; + first = false; + ppFloatVal(os, isr.min()); + } + os << "}"; + } else { + bool first = true; + for (FloatSetRanges isr(sl.fsv()); isr(); ++isr) { + if (!first) os << "++"; + first = false; + ppFloatVal(os, isr.min()); + os << ".."; + ppFloatVal(os, isr.max()); + } + } + } + } else { + os << "{"; + for (unsigned int i = 0; i < sl.v().size(); i++) { + p(sl.v()[i]); + if (i < sl.v().size() - 1) os << ","; + } + os << "}"; + } + } break; + case Expression::E_BOOLLIT: + os << (e->cast()->v() ? "true" : "false"); + break; + case Expression::E_STRINGLIT: + os << "\"" << Printer::escapeStringLit(e->cast()->v()) << "\""; + break; + case Expression::E_ID: { + if (e == constants().absent) { + os << "<>"; + } else { + const Id* id = e->cast(); + if (id->decl()) id = id->decl()->id(); + if (id->idn() == -1) { + os << id->v(); + } else { + os << "X_INTRODUCED_" << id->idn() << "_"; + } + } + } break; + case Expression::E_TIID: + os << "$" << e->cast()->v(); + break; + case Expression::E_ANON: + os << "_"; + break; + case Expression::E_ARRAYLIT: { + const ArrayLit& al = *e->cast(); + int n = al.dims(); + if (n == 1 && al.min(0) == 1) { + os << "["; + for (unsigned int i = 0; i < al.size(); i++) { + p(al[i]); + if (i < al.size() - 1) os << ","; + } + os << "]"; + } else if (n == 2 && al.min(0) == 1 && al.min(1) == 1 && al.max(1) != 0) { + os << "[|"; + for (int i = 0; i < al.max(0); i++) { + for (int j = 0; j < al.max(1); j++) { + p(al[i * al.max(1) + j]); + if (j < al.max(1) - 1) os << ","; + } + if (i < al.max(0) - 1) os << "|"; + } + os << "|]"; + } else { + os << "array" << n << "d("; + for (int i = 0; i < al.dims(); i++) { + os << al.min(i) << ".." << al.max(i); + os << ","; + } + os << "["; + for (unsigned int i = 0; i < al.size(); i++) { + p(al[i]); + if (i < al.size() - 1) os << ","; + } + os << "])"; + } + } break; + case Expression::E_ARRAYACCESS: { + const ArrayAccess& aa = *e->cast(); + p(aa.v()); + os << "["; + for (unsigned int i = 0; i < aa.idx().size(); i++) { + p(aa.idx()[i]); + if (i < aa.idx().size() - 1) os << ","; + } + os << "]"; + } break; + case Expression::E_COMP: { + const Comprehension& c = *e->cast(); + os << (c.set() ? "{" : "["); + p(c.e()); + os << " | "; + for (int i = 0; i < c.n_generators(); i++) { + for (int j = 0; j < c.n_decls(i); j++) { + os << c.decl(i, j)->id()->v(); + if (j < c.n_decls(i) - 1) os << ","; + } + if (c.in(i) == NULL) { + os << " = "; + p(c.where(i)); + } else { + os << " in "; + p(c.in(i)); + if (c.where(i) != NULL) { + os << " where "; + p(c.where(i)); + } + } + if (i < c.n_generators()) os << ", "; + } + os << (c.set() ? "}" : "]"); + } break; + case Expression::E_ITE: { + const ITE& ite = *e->cast(); + for (int i = 0; i < ite.size(); i++) { + os << (i == 0 ? "if " : " elseif "); + p(ite.e_if(i)); + os << " then "; + p(ite.e_then(i)); + } + os << " else "; + p(ite.e_else()); + os << " endif"; + } break; + case Expression::E_BINOP: { + const BinOp& bo = *e->cast(); + Parentheses ps = needParens(&bo, bo.lhs(), bo.rhs()); + if (ps & PN_LEFT) os << "("; + p(bo.lhs()); + if (ps & PN_LEFT) os << ")"; + switch (bo.op()) { + case BOT_PLUS: + os << "+"; + break; + case BOT_MINUS: + os << "-"; + break; + case BOT_MULT: + os << "*"; + break; + case BOT_POW: + os << "^"; + break; + case BOT_DIV: + os << "/"; + break; + case BOT_IDIV: + os << " div "; + break; + case BOT_MOD: + os << " mod "; + break; + case BOT_LE: + os << " < "; + break; + case BOT_LQ: + os << "<="; + break; + case BOT_GR: + os << " > "; + break; + case BOT_GQ: + os << ">="; + break; + case BOT_EQ: + os << "=="; + break; + case BOT_NQ: + os << "!="; + break; + case BOT_IN: + os << " in "; + break; + case BOT_SUBSET: + os << " subset "; + break; + case BOT_SUPERSET: + os << " superset "; + break; + case BOT_UNION: + os << " union "; + break; + case BOT_DIFF: + os << " diff "; + break; + case BOT_SYMDIFF: + os << " symdiff "; + break; + case BOT_INTERSECT: + os << " intersect "; + break; + case BOT_PLUSPLUS: + os << "++"; + break; + case BOT_EQUIV: + os << " <-> "; + break; + case BOT_IMPL: + os << " -> "; + break; + case BOT_RIMPL: + os << " <- "; + break; + case BOT_OR: + os << " \\/ "; + break; + case BOT_AND: + os << " /\\ "; + break; + case BOT_XOR: + os << " xor "; + break; + case BOT_DOTDOT: + os << ".."; + break; + default: + assert(false); + break; + } + + if (ps & PN_RIGHT) os << "("; + p(bo.rhs()); + if (ps & PN_RIGHT) os << ")"; + } break; + case Expression::E_UNOP: { + const UnOp& uo = *e->cast(); + switch (uo.op()) { + case UOT_NOT: + os << "not "; + break; + case UOT_PLUS: + os << "+"; + break; + case UOT_MINUS: + os << "-"; + break; + default: + assert(false); + break; + } + bool needParen = (uo.e()->isa() || uo.e()->isa() || !uo.ann().isEmpty()); + if (needParen) os << "("; + p(uo.e()); + if (needParen) os << ")"; + } break; + case Expression::E_CALL: { + const Call& c = *e->cast(); + os << c.id() << "("; + for (unsigned int i = 0; i < c.n_args(); i++) { + p(c.arg(i)); + if (i < c.n_args() - 1) os << ","; + } + os << ")"; + } break; + case Expression::E_VARDECL: { + const VarDecl& vd = *e->cast(); + p(vd.ti()); + if (!vd.ti()->isEnum()) { + os << ":"; + } + if (vd.id()->idn() != -1) { + os << " X_INTRODUCED_" << vd.id()->idn() << "_"; + } else if (vd.id()->v().size() != 0) + os << " " << vd.id()->v(); + if (vd.introduced()) { + os << " ::var_is_introduced "; + } + p(vd.ann()); + if (vd.e()) { + os << " = "; + p(vd.e()); + } + } break; + case Expression::E_LET: { + const Let& l = *e->cast(); + os << "let {"; + + for (unsigned int i = 0; i < l.let().size(); i++) { + const Expression* li = l.let()[i]; + if (!li->isa()) os << "constraint "; + p(li); + if (i < l.let().size() - 1) os << ", "; + } + os << "} in ("; + p(l.in()); + os << ")"; + } break; + case Expression::E_TI: { + const TypeInst& ti = *e->cast(); + if (ti.isEnum()) { + os << "enum"; + } else if (env) { + os << ti.type().toString(*env); + } else { + if (ti.isarray()) { + os << "array ["; + for (unsigned int i = 0; i < ti.ranges().size(); i++) { + p(Type::parint(), ti.ranges()[i]); + if (i < ti.ranges().size() - 1) os << ","; + } + os << "] of "; + } + p(ti.type(), ti.domain()); + } + } + } + if (!e->isa()) { + p(e->ann()); + } + } + + void p(const Item* i) { + if (i == NULL) return; + if (i->removed()) os << "% "; + switch (i->iid()) { + case Item::II_INC: + os << "include \"" << i->cast()->f() << "\""; + break; + case Item::II_VD: + p(i->cast()->e()); + break; + case Item::II_ASN: + os << i->cast()->id() << " = "; + p(i->cast()->e()); + break; + case Item::II_CON: + os << "constraint "; + p(i->cast()->e()); + break; + case Item::II_SOL: { + const SolveI* si = i->cast(); + os << "solve "; + p(si->ann()); + switch (si->st()) { + case SolveI::ST_SAT: + os << " satisfy"; + break; + case SolveI::ST_MIN: + os << " minimize "; + p(si->e()); + break; + case SolveI::ST_MAX: + os << " maximize "; + p(si->e()); + break; + } + } break; + case Item::II_OUT: + os << "output "; + p(i->cast()->e()); + break; + case Item::II_FUN: { + const FunctionI& fi = *i->cast(); + if (fi.ti()->type().isann() && fi.e() == NULL) { + os << "annotation "; + } else if (fi.ti()->type() == Type::parbool()) { + os << "test "; + } else if (fi.ti()->type() == Type::varbool()) { + os << "predicate "; + } else { + os << "function "; + p(fi.ti()); + os << " : "; + } + os << fi.id(); + if (fi.params().size() > 0) { + os << "("; + for (unsigned int i = 0; i < fi.params().size(); i++) { + p(fi.params()[i]); + if (i < fi.params().size() - 1) os << ","; + } + os << ")"; + } + p(fi.ann()); + if (fi.e()) { + os << " = "; + p(fi.e()); + } + } break; + } + os << ";" << std::endl; + } +}; + +template +class ExpressionMapper { +protected: + T& _t; + +public: + ExpressionMapper(T& t) : _t(t) {} + typename T::ret map(const Expression* e) { + switch (e->eid()) { + case Expression::E_INTLIT: + return _t.mapIntLit(*e->cast()); + case Expression::E_FLOATLIT: + return _t.mapFloatLit(*e->cast()); + case Expression::E_SETLIT: + return _t.mapSetLit(*e->cast()); + case Expression::E_BOOLLIT: + return _t.mapBoolLit(*e->cast()); + case Expression::E_STRINGLIT: + return _t.mapStringLit(*e->cast()); + case Expression::E_ID: + return _t.mapId(*e->cast()); + case Expression::E_ANON: + return _t.mapAnonVar(*e->cast()); + case Expression::E_ARRAYLIT: + return _t.mapArrayLit(*e->cast()); + case Expression::E_ARRAYACCESS: + return _t.mapArrayAccess(*e->cast()); + case Expression::E_COMP: + return _t.mapComprehension(*e->cast()); + case Expression::E_ITE: + return _t.mapITE(*e->cast()); + case Expression::E_BINOP: + return _t.mapBinOp(*e->cast()); + case Expression::E_UNOP: + return _t.mapUnOp(*e->cast()); + case Expression::E_CALL: + return _t.mapCall(*e->cast()); + case Expression::E_VARDECL: + return _t.mapVarDecl(*e->cast()); + case Expression::E_LET: + return _t.mapLet(*e->cast()); + case Expression::E_TI: + return _t.mapTypeInst(*e->cast()); + case Expression::E_TIID: + return _t.mapTIId(*e->cast()); + default: + assert(false); + return typename T::ret(); + break; + } + } +}; + +class Document { +private: + int level; + +public: + Document() : level(0) {} + virtual ~Document() {} + int getLevel() { return level; } + // Make this object a child of "d". + virtual void setParent(Document* d) { level = d->level + 1; } +}; + +class BreakPoint : public Document { +private: + bool dontSimplify; + +public: + BreakPoint() { dontSimplify = false; } + BreakPoint(bool ds) { dontSimplify = ds; } + virtual ~BreakPoint() {} + void setDontSimplify(bool b) { dontSimplify = b; } + bool getDontSimplify() { return dontSimplify; } +}; + +class StringDocument : public Document { +private: + std::string stringDocument; + +public: + StringDocument() {} + virtual ~StringDocument() {} + + StringDocument(std::string s) : stringDocument(s) {} + + std::string getString() { return stringDocument; } + void setString(std::string s) { stringDocument = s; } +}; + +class DocumentList : public Document { +private: + std::vector docs; + std::string beginToken; + std::string separator; + std::string endToken; + bool unbreakable; + bool alignment; + +public: + virtual ~DocumentList() { + std::vector::iterator it; + for (it = docs.begin(); it != docs.end(); it++) { + delete *it; + } + } + DocumentList(std::string _beginToken = "", std::string _separator = "", + std::string _endToken = "", bool _alignment = true); + + void addDocumentToList(Document* d) { + docs.push_back(d); + d->setParent(this); + } + + void setParent(Document* d) { + Document::setParent(d); + std::vector::iterator it; + for (it = docs.begin(); it != docs.end(); it++) { + (*it)->setParent(this); + } + } + + void addStringToList(std::string s) { addDocumentToList(new StringDocument(s)); } + + void addBreakPoint(bool b = false) { addDocumentToList(new BreakPoint(b)); } + + std::vector getDocs() { return docs; } + + void setList(std::vector ld) { docs = ld; } + + std::string getBeginToken() { return beginToken; } + + std::string getEndToken() { return endToken; } + + std::string getSeparator() { return separator; } + + bool getUnbreakable() { return unbreakable; } + + void setUnbreakable(bool b) { unbreakable = b; } + + bool getAlignment() { return alignment; } +}; + +DocumentList::DocumentList(std::string _beginToken, std::string _separator, std::string _endToken, + bool _alignment) { + beginToken = _beginToken; + separator = _separator; + endToken = _endToken; + alignment = _alignment; + unbreakable = false; +} + +class Line { +private: + int indentation; + int lineLength; + std::vector text; + +public: + Line() : indentation(0), lineLength(0), text(0) {} + Line(const Line& l) : indentation(l.indentation), lineLength(l.lineLength), text(l.text) {} + Line(const int indent) : indentation(indent), lineLength(0), text(0) {} + bool operator==(const Line& l) { return &l == this; } + + void setIndentation(int i) { indentation = i; } + + int getLength() const { return lineLength; } + int getIndentation() const { return indentation; } + int getSpaceLeft(int maxwidth); + void addString(const std::string& s); + void concatenateLines(Line& l); + + void print(std::ostream& os) const { + for (int i = 0; i < getIndentation(); i++) { + os << " "; + } + std::vector::const_iterator it; + for (it = text.begin(); it != text.end(); it++) { + os << (*it); + } + os << "\n"; + } +}; + +int Line::getSpaceLeft(int maxwidth) { return maxwidth - lineLength - indentation; } +void Line::addString(const std::string& s) { + lineLength += static_cast(s.size()); + text.push_back(s); +} +void Line::concatenateLines(Line& l) { + text.insert(text.end(), l.text.begin(), l.text.end()); + lineLength += l.lineLength; +} + +class LinesToSimplify { +private: + std::map > lines; + + // (i,j) in parent <=> j can only be simplified if i is simplified + std::vector > parent; + /* + * if i can't simplify, remove j and his parents + */ + // mostRecentlyAdded[level] = line of the most recently added + std::map mostRecentlyAdded; + +public: + std::vector* getLinesForPriority(int p) { + std::map >::iterator it; + for (it = lines.begin(); it != lines.end(); it++) { + if (it->first == p) return &(it->second); + } + return NULL; + } + void addLine(int p, int l, int par = -1) { + if (par == -1) { + for (int i = p - 1; i >= 0; i--) { + std::map::iterator it = mostRecentlyAdded.find(i); + if (it != mostRecentlyAdded.end()) { + par = it->second; + break; + } + } + } + if (par != -1) parent.push_back(std::pair(l, par)); + mostRecentlyAdded.insert(std::pair(p, l)); + std::map >::iterator it; + for (it = lines.begin(); it != lines.end(); it++) { + if (it->first == p) { + it->second.push_back(l); + return; + } + } + std::vector v; + v.push_back(l); + lines.insert(std::pair >(p, v)); + } + void decrementLine(std::vector* vec, int l) { + std::vector::iterator vit; + if (vec != NULL) { + for (vit = vec->begin(); vit != vec->end(); vit++) { + if (*vit >= l) *vit = *vit - 1; + } + } + // Now the map + std::map >::iterator it; + for (it = lines.begin(); it != lines.end(); it++) { + for (vit = it->second.begin(); vit != it->second.end(); vit++) { + if (*vit >= l) *vit = *vit - 1; + } + } + // And the parent table + std::vector >::iterator vpit; + for (vpit = parent.begin(); vpit != parent.end(); vpit++) { + if (vpit->first >= l) vpit->first--; + if (vpit->second >= l) vpit->second--; + } + } + void remove(LinesToSimplify& lts) { + std::map >::iterator it; + for (it = lts.lines.begin(); it != lts.lines.end(); it++) { + std::vector::iterator vit; + for (vit = it->second.begin(); vit != it->second.end(); vit++) { + remove(NULL, *vit, false); + } + } + } + void remove(std::vector* v, int i, bool success = true) { + if (v != NULL) { + v->erase(std::remove(v->begin(), v->end(), i), v->end()); + } + std::map >::iterator it; + for (it = lines.begin(); it != lines.end(); it++) { + std::vector* v = &(it->second); + v->erase(std::remove(v->begin(), v->end(), i), v->end()); + } + // Call on its parent + if (!success) { + std::vector >::iterator vpit; + for (vpit = parent.begin(); vpit != parent.end(); vpit++) { + if (vpit->first == i && vpit->second != i && vpit->second != -1) { + remove(v, vpit->second, false); + } + } + } + } + std::vector* getLinesToSimplify() { + std::vector* vec = new std::vector(); + std::map >::iterator it; + for (it = lines.begin(); it != lines.end(); it++) { + std::vector& svec = it->second; + vec->insert(vec->begin(), svec.begin(), svec.end()); + } + return vec; + } +}; + +Document* expressionToDocument(const Expression* e); +Document* annotationToDocument(const Annotation& ann); +Document* tiexpressionToDocument(const Type& type, const Expression* e) { + DocumentList* dl = new DocumentList("", "", "", false); + switch (type.ti()) { + case Type::TI_PAR: + break; + case Type::TI_VAR: + dl->addStringToList("var "); + break; + } + if (type.ot() == Type::OT_OPTIONAL) dl->addStringToList("opt "); + if (type.st() == Type::ST_SET) dl->addStringToList("set of "); + if (e == NULL) { + switch (type.bt()) { + case Type::BT_INT: + dl->addStringToList("int"); + break; + case Type::BT_BOOL: + dl->addStringToList("bool"); + break; + case Type::BT_FLOAT: + dl->addStringToList("float"); + break; + case Type::BT_STRING: + dl->addStringToList("string"); + break; + case Type::BT_ANN: + dl->addStringToList("ann"); + break; + case Type::BT_BOT: + dl->addStringToList("bot"); + break; + case Type::BT_TOP: + dl->addStringToList("top"); + break; + case Type::BT_UNKNOWN: + dl->addStringToList("???"); + break; + } + } else { + dl->addDocumentToList(expressionToDocument(e)); + } + return dl; +} + +class ExpressionDocumentMapper { +public: + typedef Document* ret; + ret mapIntLit(const IntLit& il) { + std::ostringstream oss; + oss << il.v(); + return new StringDocument(oss.str()); + } + ret mapFloatLit(const FloatLit& fl) { + std::ostringstream oss; + ppFloatVal(oss, fl.v()); + return new StringDocument(oss.str()); + } + ret mapSetLit(const SetLit& sl) { + DocumentList* dl; + if (sl.isv()) { + if (sl.isv()->size() == 0) { + dl = new DocumentList("1..0", "", ""); + } else if (sl.isv()->size() == 1) { + dl = new DocumentList("", "..", ""); + { + std::ostringstream oss; + oss << sl.isv()->min(0); + dl->addDocumentToList(new StringDocument(oss.str())); + } + { + std::ostringstream oss; + oss << sl.isv()->max(0); + dl->addDocumentToList(new StringDocument(oss.str())); + } + } else { + dl = new DocumentList("{", ", ", "}", true); + IntSetRanges isr(sl.isv()); + for (Ranges::ToValues isv(isr); isv(); ++isv) { + std::ostringstream oss; + oss << isv.val(); + dl->addDocumentToList(new StringDocument(oss.str())); + } + } + } else if (sl.fsv()) { + if (sl.fsv()->size() == 0) { + dl = new DocumentList("1.0..0.0", "", ""); + } else if (sl.fsv()->size() == 1) { + dl = new DocumentList("", "..", ""); + { + std::ostringstream oss; + ppFloatVal(oss, sl.fsv()->min(0)); + dl->addDocumentToList(new StringDocument(oss.str())); + } + { + std::ostringstream oss; + ppFloatVal(oss, sl.fsv()->max(0)); + dl->addDocumentToList(new StringDocument(oss.str())); + } + } else { + dl = new DocumentList("", "++", "", true); + FloatSetRanges fsr(sl.fsv()); + for (; fsr(); ++fsr) { + std::ostringstream oss; + ppFloatVal(oss, fsr.min()); + oss << ".."; + ppFloatVal(oss, fsr.max()); + dl->addDocumentToList(new StringDocument(oss.str())); + } + } + + } else { + dl = new DocumentList("{", ", ", "}", true); + for (unsigned int i = 0; i < sl.v().size(); i++) { + dl->addDocumentToList(expressionToDocument((sl.v()[i]))); + } + } + return dl; + } + ret mapBoolLit(const BoolLit& bl) { + return new StringDocument(std::string(bl.v() ? "true" : "false")); + } + ret mapStringLit(const StringLit& sl) { + std::ostringstream oss; + oss << "\"" << Printer::escapeStringLit(sl.v()) << "\""; + return new StringDocument(oss.str()); + } + ret mapId(const Id& id) { + if (&id == constants().absent) return new StringDocument("<>"); + if (id.idn() == -1) + return new StringDocument(id.v().str()); + else { + std::ostringstream oss; + oss << "X_INTRODUCED_" << id.idn() << "_"; + return new StringDocument(oss.str()); + } + } + ret mapTIId(const TIId& id) { return new StringDocument("$" + id.v().str()); } + ret mapAnonVar(const AnonVar&) { return new StringDocument("_"); } + ret mapArrayLit(const ArrayLit& al) { + /// TODO: test multi-dimensional arrays handling + DocumentList* dl; + int n = al.dims(); + if (n == 1 && al.min(0) == 1) { + dl = new DocumentList("[", ", ", "]"); + for (unsigned int i = 0; i < al.size(); i++) + dl->addDocumentToList(expressionToDocument(al[i])); + } else if (n == 2 && al.min(0) == 1 && al.min(1) == 1) { + dl = new DocumentList("[| ", " | ", " |]"); + for (int i = 0; i < al.max(0); i++) { + DocumentList* row = new DocumentList("", ", ", ""); + for (int j = 0; j < al.max(1); j++) { + row->addDocumentToList(expressionToDocument(al[i * al.max(1) + j])); + } + dl->addDocumentToList(row); + if (i != al.max(0) - 1) dl->addBreakPoint(true); // dont simplify + } + } else { + dl = new DocumentList("", "", ""); + std::stringstream oss; + oss << "array" << n << "d"; + dl->addStringToList(oss.str()); + DocumentList* args = new DocumentList("(", ", ", ")"); + + for (int i = 0; i < al.dims(); i++) { + oss.str(""); + oss << al.min(i) << ".." << al.max(i); + args->addStringToList(oss.str()); + } + DocumentList* array = new DocumentList("[", ", ", "]"); + for (unsigned int i = 0; i < al.size(); i++) + array->addDocumentToList(expressionToDocument(al[i])); + args->addDocumentToList(array); + dl->addDocumentToList(args); + } + return dl; + } + ret mapArrayAccess(const ArrayAccess& aa) { + DocumentList* dl = new DocumentList("", "", ""); + + dl->addDocumentToList(expressionToDocument(aa.v())); + DocumentList* args = new DocumentList("[", ", ", "]"); + for (unsigned int i = 0; i < aa.idx().size(); i++) { + args->addDocumentToList(expressionToDocument(aa.idx()[i])); + } + dl->addDocumentToList(args); + return dl; + } + ret mapComprehension(const Comprehension& c) { + std::ostringstream oss; + DocumentList* dl; + if (c.set()) + dl = new DocumentList("{ ", " | ", " }"); + else + dl = new DocumentList("[ ", " | ", " ]"); + dl->addDocumentToList(expressionToDocument(c.e())); + DocumentList* head = new DocumentList("", " ", ""); + DocumentList* generators = new DocumentList("", ", ", ""); + for (int i = 0; i < c.n_generators(); i++) { + DocumentList* gen = new DocumentList("", "", ""); + DocumentList* idents = new DocumentList("", ", ", ""); + for (int j = 0; j < c.n_decls(i); j++) { + idents->addStringToList(c.decl(i, j)->id()->v().str()); + } + gen->addDocumentToList(idents); + if (c.in(i) == NULL) { + gen->addStringToList(" = "); + gen->addDocumentToList(expressionToDocument(c.where(i))); + } else { + gen->addStringToList(" in "); + gen->addDocumentToList(expressionToDocument(c.in(i))); + if (c.where(i) != NULL) { + gen->addStringToList(" where "); + gen->addDocumentToList(expressionToDocument(c.where(i))); + } + } + generators->addDocumentToList(gen); + } + head->addDocumentToList(generators); + dl->addDocumentToList(head); + + return dl; + } + ret mapITE(const ITE& ite) { + DocumentList* dl = new DocumentList("", "", ""); + for (int i = 0; i < ite.size(); i++) { + std::string beg = (i == 0 ? "if " : " elseif "); + dl->addStringToList(beg); + dl->addDocumentToList(expressionToDocument(ite.e_if(i))); + dl->addStringToList(" then "); + + DocumentList* ifdoc = new DocumentList("", "", "", false); + ifdoc->addBreakPoint(); + ifdoc->addDocumentToList(expressionToDocument(ite.e_then(i))); + dl->addDocumentToList(ifdoc); + dl->addStringToList(" "); + } + dl->addBreakPoint(); + dl->addStringToList("else "); + + DocumentList* elsedoc = new DocumentList("", "", "", false); + elsedoc->addBreakPoint(); + elsedoc->addDocumentToList(expressionToDocument(ite.e_else())); + dl->addDocumentToList(elsedoc); + dl->addStringToList(" "); + dl->addBreakPoint(); + dl->addStringToList("endif"); + + return dl; + } + ret mapBinOp(const BinOp& bo) { + Parentheses ps = needParens(&bo, bo.lhs(), bo.rhs()); + DocumentList* opLeft; + DocumentList* dl; + DocumentList* opRight; + bool linebreak = false; + if (ps & PN_LEFT) + opLeft = new DocumentList("(", " ", ")"); + else + opLeft = new DocumentList("", " ", ""); + opLeft->addDocumentToList(expressionToDocument(bo.lhs())); + std::string op; + switch (bo.op()) { + case BOT_PLUS: + op = "+"; + break; + case BOT_MINUS: + op = "-"; + break; + case BOT_MULT: + op = "*"; + break; + case BOT_POW: + op = "^"; + break; + case BOT_DIV: + op = "/"; + break; + case BOT_IDIV: + op = " div "; + break; + case BOT_MOD: + op = " mod "; + break; + case BOT_LE: + op = " < "; + break; + case BOT_LQ: + op = "<="; + break; + case BOT_GR: + op = " > "; + break; + case BOT_GQ: + op = ">="; + break; + case BOT_EQ: + op = "=="; + break; + case BOT_NQ: + op = "!="; + break; + case BOT_IN: + op = " in "; + break; + case BOT_SUBSET: + op = " subset "; + break; + case BOT_SUPERSET: + op = " superset "; + break; + case BOT_UNION: + op = " union "; + break; + case BOT_DIFF: + op = " diff "; + break; + case BOT_SYMDIFF: + op = " symdiff "; + break; + case BOT_INTERSECT: + op = " intersect "; + break; + case BOT_PLUSPLUS: + op = "++"; + linebreak = true; + break; + case BOT_EQUIV: + op = " <-> "; + break; + case BOT_IMPL: + op = " -> "; + break; + case BOT_RIMPL: + op = " <- "; + break; + case BOT_OR: + op = " \\/ "; + linebreak = true; + break; + case BOT_AND: + op = " /\\ "; + linebreak = true; + break; + case BOT_XOR: + op = " xor "; + break; + case BOT_DOTDOT: + op = ".."; + break; + default: + assert(false); + break; + } + dl = new DocumentList("", op, ""); + + if (ps & PN_RIGHT) + opRight = new DocumentList("(", " ", ")"); + else + opRight = new DocumentList("", "", ""); + opRight->addDocumentToList(expressionToDocument(bo.rhs())); + dl->addDocumentToList(opLeft); + if (linebreak) dl->addBreakPoint(); + dl->addDocumentToList(opRight); + + return dl; + } + ret mapUnOp(const UnOp& uo) { + DocumentList* dl = new DocumentList("", "", ""); + std::string op; + switch (uo.op()) { + case UOT_NOT: + op = "not "; + break; + case UOT_PLUS: + op = "+"; + break; + case UOT_MINUS: + op = "-"; + break; + default: + assert(false); + break; + } + dl->addStringToList(op); + DocumentList* unop; + bool needParen = (uo.e()->isa() || uo.e()->isa()); + if (needParen) + unop = new DocumentList("(", " ", ")"); + else + unop = new DocumentList("", " ", ""); + + unop->addDocumentToList(expressionToDocument(uo.e())); + dl->addDocumentToList(unop); + return dl; + } + ret mapCall(const Call& c) { + if (c.n_args() == 1) { + /* + * if we have only one argument, and this is an array comprehension, + * we convert it into the following syntax + * forall (f(i,j) | i in 1..10) + * --> + * forall (i in 1..10) (f(i,j)) + */ + + const Expression* e = c.arg(0); + if (e->isa()) { + const Comprehension* com = e->cast(); + if (!com->set()) { + DocumentList* dl = new DocumentList("", " ", ""); + dl->addStringToList(c.id().str()); + DocumentList* args = new DocumentList("", " ", "", false); + DocumentList* generators = new DocumentList("", ", ", ""); + + for (int i = 0; i < com->n_generators(); i++) { + DocumentList* gen = new DocumentList("", "", ""); + DocumentList* idents = new DocumentList("", ", ", ""); + for (int j = 0; j < com->n_decls(i); j++) { + idents->addStringToList(com->decl(i, j)->id()->v().str()); + } + gen->addDocumentToList(idents); + if (com->in(i) == NULL) { + gen->addStringToList(" = "); + gen->addDocumentToList(expressionToDocument(com->where(i))); + } else { + gen->addStringToList(" in "); + gen->addDocumentToList(expressionToDocument(com->in(i))); + if (com->where(i) != NULL) { + gen->addStringToList(" where "); + gen->addDocumentToList(expressionToDocument(com->where(i))); + } + } + generators->addDocumentToList(gen); + } + + args->addStringToList("("); + args->addDocumentToList(generators); + args->addStringToList(")"); + + args->addStringToList("("); + args->addBreakPoint(); + args->addDocumentToList(expressionToDocument(com->e())); + + dl->addDocumentToList(args); + dl->addBreakPoint(); + dl->addStringToList(")"); + + return dl; + } + } + } + std::string beg = c.id().str() + "("; + DocumentList* dl = new DocumentList(beg, ", ", ")"); + for (unsigned int i = 0; i < c.n_args(); i++) { + dl->addDocumentToList(expressionToDocument(c.arg(i))); + } + return dl; + } + ret mapVarDecl(const VarDecl& vd) { + std::ostringstream oss; + DocumentList* dl = new DocumentList("", "", ""); + dl->addDocumentToList(expressionToDocument(vd.ti())); + dl->addStringToList(": "); + if (vd.id()->idn() == -1) { + dl->addStringToList(vd.id()->v().str()); + } else { + std::ostringstream oss; + oss << "X_INTRODUCED_" << vd.id()->idn() << "_"; + dl->addStringToList(oss.str()); + } + + if (vd.introduced()) { + dl->addStringToList(" ::var_is_introduced "); + } + if (!vd.ann().isEmpty()) { + dl->addDocumentToList(annotationToDocument(vd.ann())); + } + if (vd.e()) { + dl->addStringToList(" = "); + dl->addDocumentToList(expressionToDocument(vd.e())); + } + return dl; + } + ret mapLet(const Let& l) { + DocumentList* letin = new DocumentList("", "", "", false); + DocumentList* lets = new DocumentList("", " ", "", true); + DocumentList* inexpr = new DocumentList("", "", ""); + bool ds = l.let().size() > 1; + + for (unsigned int i = 0; i < l.let().size(); i++) { + if (i != 0) lets->addBreakPoint(ds); + DocumentList* exp = new DocumentList("", " ", ","); + const Expression* li = l.let()[i]; + if (!li->isa()) exp->addStringToList("constraint"); + exp->addDocumentToList(expressionToDocument(li)); + lets->addDocumentToList(exp); + } + + inexpr->addDocumentToList(expressionToDocument(l.in())); + letin->addBreakPoint(ds); + letin->addDocumentToList(lets); + + DocumentList* letin2 = new DocumentList("", "", "", false); + + letin2->addBreakPoint(); + letin2->addDocumentToList(inexpr); + + DocumentList* dl = new DocumentList("", "", ""); + dl->addStringToList("let {"); + dl->addDocumentToList(letin); + dl->addBreakPoint(ds); + dl->addStringToList("} in ("); + dl->addDocumentToList(letin2); + // dl->addBreakPoint(); + dl->addStringToList(")"); + return dl; + } + ret mapTypeInst(const TypeInst& ti) { + DocumentList* dl = new DocumentList("", "", ""); + if (ti.isarray()) { + dl->addStringToList("array ["); + DocumentList* ran = new DocumentList("", ", ", ""); + for (unsigned int i = 0; i < ti.ranges().size(); i++) { + ran->addDocumentToList(tiexpressionToDocument(Type::parint(), ti.ranges()[i])); + } + dl->addDocumentToList(ran); + dl->addStringToList("] of "); + } + dl->addDocumentToList(tiexpressionToDocument(ti.type(), ti.domain())); + return dl; + } +}; + +Document* annotationToDocument(const Annotation& ann) { + DocumentList* dl = new DocumentList(" :: ", " :: ", ""); + for (ExpressionSetIter it = ann.begin(); it != ann.end(); ++it) { + dl->addDocumentToList(expressionToDocument(*it)); + } + return dl; +} + +Document* expressionToDocument(const Expression* e) { + if (e == NULL) return new StringDocument("NULL"); + ExpressionDocumentMapper esm; + ExpressionMapper em(esm); + DocumentList* dl = new DocumentList("", "", ""); + Document* s = em.map(e); + dl->addDocumentToList(s); + if (!e->isa() && !e->ann().isEmpty()) { + dl->addDocumentToList(annotationToDocument(e->ann())); + } + return dl; +} + +class ItemDocumentMapper { +public: + typedef Document* ret; + ret mapIncludeI(const IncludeI& ii) { + std::ostringstream oss; + oss << "include \"" << ii.f() << "\";"; + return new StringDocument(oss.str()); + } + ret mapVarDeclI(const VarDeclI& vi) { + DocumentList* dl = new DocumentList("", " ", ";"); + dl->addDocumentToList(expressionToDocument(vi.e())); + return dl; + } + ret mapAssignI(const AssignI& ai) { + DocumentList* dl = new DocumentList("", " = ", ";"); + dl->addStringToList(ai.id().str()); + dl->addDocumentToList(expressionToDocument(ai.e())); + return dl; + } + ret mapConstraintI(const ConstraintI& ci) { + DocumentList* dl = new DocumentList("constraint ", " ", ";"); + dl->addDocumentToList(expressionToDocument(ci.e())); + return dl; + } + ret mapSolveI(const SolveI& si) { + DocumentList* dl = new DocumentList("", "", ";"); + dl->addStringToList("solve"); + if (!si.ann().isEmpty()) dl->addDocumentToList(annotationToDocument(si.ann())); + switch (si.st()) { + case SolveI::ST_SAT: + dl->addStringToList(" satisfy"); + break; + case SolveI::ST_MIN: + dl->addStringToList(" minimize "); + dl->addDocumentToList(expressionToDocument(si.e())); + break; + case SolveI::ST_MAX: + dl->addStringToList(" maximize "); + dl->addDocumentToList(expressionToDocument(si.e())); + break; + } + return dl; + } + ret mapOutputI(const OutputI& oi) { + DocumentList* dl = new DocumentList("output ", " ", ";"); + dl->addDocumentToList(expressionToDocument(oi.e())); + return dl; + } + ret mapFunctionI(const FunctionI& fi) { + DocumentList* dl; + if (fi.ti()->type().isann() && fi.e() == NULL) { + dl = new DocumentList("annotation ", " ", ";", false); + } else if (fi.ti()->type() == Type::parbool()) { + dl = new DocumentList("test ", "", ";", false); + } else if (fi.ti()->type() == Type::varbool()) { + dl = new DocumentList("predicate ", "", ";", false); + } else { + dl = new DocumentList("function ", "", ";", false); + dl->addDocumentToList(expressionToDocument(fi.ti())); + dl->addStringToList(": "); + } + dl->addStringToList(fi.id().str()); + if (fi.params().size() > 0) { + DocumentList* params = new DocumentList("(", ", ", ")"); + for (unsigned int i = 0; i < fi.params().size(); i++) { + DocumentList* par = new DocumentList("", "", ""); + par->setUnbreakable(true); + par->addDocumentToList(expressionToDocument(fi.params()[i])); + params->addDocumentToList(par); + } + dl->addDocumentToList(params); + } + if (!fi.ann().isEmpty()) { + dl->addDocumentToList(annotationToDocument(fi.ann())); + } + if (fi.e()) { + dl->addStringToList(" = "); + dl->addBreakPoint(); + dl->addDocumentToList(expressionToDocument(fi.e())); + } + + return dl; + } +}; + +class PrettyPrinter { +public: + /* + * \brief Constructor for class Pretty Printer + * \param maxwidth (default 80) : number of rows + * \param indentationBase : spaces that represent the atomic number of spaces + * \param sim : whether we want to simplify the result + * \param deepSimp : whether we want to simplify at each breakpoint or not + */ + PrettyPrinter(int _maxwidth = 80, int _indentationBase = 4, bool sim = false, + bool deepSimp = false); + + void print(Document* d); + void print(std::ostream& os) const; + +private: + int maxwidth; + int indentationBase; + int currentLine; + int currentItem; + std::vector > items; + std::vector linesToSimplify; + std::vector linesNotToSimplify; + bool simp; + bool deeplySimp; + + void addItem(); + + void addLine(int indentation, bool bp = false, bool ds = false, int level = 0); + static std::string printSpaces(int n); + const std::vector& getCurrentItemLines() const; + + void printDocument(Document* d, bool alignment, int startColAlignment, + const std::string& before = "", const std::string& after = ""); + void printDocList(DocumentList* d, int startColAlignment, const std::string& before = "", + const std::string& after = ""); + void printStringDoc(StringDocument* d, bool alignment, int startColAlignment, + const std::string& before = "", const std::string& after = ""); + void printString(const std::string& s, bool alignment, int startColAlignment); + bool simplify(int item, int line, std::vector* vec); + void simplifyItem(int item); +}; + +void PrettyPrinter::print(Document* d) { + addItem(); + addLine(0); + printDocument(d, true, 0); + if (simp) simplifyItem(currentItem); +} + +PrettyPrinter::PrettyPrinter(int _maxwidth, int _indentationBase, bool sim, bool deepsim) { + maxwidth = _maxwidth; + indentationBase = _indentationBase; + currentLine = -1; + currentItem = -1; + + simp = sim; + deeplySimp = deepsim; +} +const std::vector& PrettyPrinter::getCurrentItemLines() const { return items[currentItem]; } + +void PrettyPrinter::addLine(int indentation, bool bp, bool simpl, int level) { + items[currentItem].push_back(Line(indentation)); + currentLine++; + if (bp && deeplySimp) { + linesToSimplify[currentItem].addLine(level, currentLine); + if (!simpl) linesNotToSimplify[currentItem].addLine(0, currentLine); + } +} +void PrettyPrinter::addItem() { + items.push_back(std::vector()); + linesToSimplify.push_back(LinesToSimplify()); + linesNotToSimplify.push_back(LinesToSimplify()); + currentItem++; + currentLine = -1; +} + +void PrettyPrinter::print(std::ostream& os) const { + std::vector::const_iterator it; + int nItems = static_cast(items.size()); + for (int item = 0; item < nItems; item++) { + for (it = items[item].begin(); it != items[item].end(); it++) { + it->print(os); + } + // os << std::endl; + } +} +std::string PrettyPrinter::printSpaces(int n) { + std::string result; + for (int i = 0; i < n; i++) { + result += " "; + } + return result; +} + +void PrettyPrinter::printDocument(Document* d, bool alignment, int alignmentCol, + const std::string& before, const std::string& after) { + if (DocumentList* dl = dynamic_cast(d)) { + printDocList(dl, alignmentCol, before, after); + } else if (StringDocument* sd = dynamic_cast(d)) { + printStringDoc(sd, alignment, alignmentCol, before, after); + } else if (BreakPoint* bp = dynamic_cast(d)) { + printString(before, alignment, alignmentCol); + addLine(alignmentCol, deeplySimp, !bp->getDontSimplify(), d->getLevel()); + printString(after, alignment, alignmentCol); + } else { + throw InternalError("PrettyPrinter::print : Wrong type of document"); + } +} + +void PrettyPrinter::printStringDoc(StringDocument* d, bool alignment, int alignmentCol, + const std::string& before, const std::string& after) { + std::string s; + if (d != NULL) s = d->getString(); + s = before + s + after; + printString(s, alignment, alignmentCol); +} + +void PrettyPrinter::printString(const std::string& s, bool alignment, int alignmentCol) { + Line& l = items[currentItem][currentLine]; + int size = static_cast(s.size()); + if (size <= l.getSpaceLeft(maxwidth)) { + l.addString(s); + } else { + int col = alignment && maxwidth - alignmentCol >= size ? alignmentCol : indentationBase; + addLine(col); + items[currentItem][currentLine].addString(s); + } +} + +void PrettyPrinter::printDocList(DocumentList* d, int alignmentCol, const std::string& super_before, + const std::string& super_after) { + std::vector ld = d->getDocs(); + std::string beginToken = d->getBeginToken(); + std::string separator = d->getSeparator(); + std::string endToken = d->getEndToken(); + bool _alignment = d->getAlignment(); + if (d->getUnbreakable()) { + addLine(alignmentCol); + } + int currentCol = items[currentItem][currentLine].getIndentation() + + items[currentItem][currentLine].getLength(); + int newAlignmentCol = + _alignment ? currentCol + static_cast(beginToken.size()) : alignmentCol; + int vectorSize = static_cast(ld.size()); + int lastVisibleElementIndex; + for (int i = 0; i < vectorSize; i++) { + if (!dynamic_cast(ld[i])) lastVisibleElementIndex = i; + } + if (vectorSize == 0) { + printStringDoc(NULL, true, newAlignmentCol, super_before + beginToken, endToken + super_after); + } + for (int i = 0; i < vectorSize; i++) { + Document* subdoc = ld[i]; + bool bp = false; + if (dynamic_cast(subdoc)) { + if (!_alignment) newAlignmentCol += indentationBase; + bp = true; + } + std::string af, be; + if (i != vectorSize - 1) { + if (bp || lastVisibleElementIndex <= i) + af = ""; + else + af = separator; + } else { + af = endToken + super_after; + } + if (i == 0) { + be = super_before + beginToken; + } else { + be = ""; + } + printDocument(subdoc, _alignment, newAlignmentCol, be, af); + } + if (d->getUnbreakable()) { + simplify(currentItem, currentLine, NULL); + } +} +void PrettyPrinter::simplifyItem(int item) { + linesToSimplify[item].remove(linesNotToSimplify[item]); + std::vector* vec = (linesToSimplify[item].getLinesToSimplify()); + while (!vec->empty()) { + if (!simplify(item, (*vec)[0], vec)) break; + } + delete vec; +} + +bool PrettyPrinter::simplify(int item, int line, std::vector* vec) { + if (line == 0) { + linesToSimplify[item].remove(vec, line, false); + return false; + } + if (items[item][line].getLength() > items[item][line - 1].getSpaceLeft(maxwidth)) { + linesToSimplify[item].remove(vec, line, false); + return false; + } else { + linesToSimplify[item].remove(vec, line, true); + items[item][line - 1].concatenateLines(items[item][line]); + items[item].erase(items[item].begin() + line); + + linesToSimplify[item].decrementLine(vec, line); + currentLine--; + } + + return true; +} + +Printer::Printer(std::ostream& os, int width, bool flatZinc, EnvI* env0) + : env(env0), ism(NULL), printer(NULL), _os(os), _width(width), _flatZinc(flatZinc) {} +void Printer::init(void) { + if (ism == NULL) { + ism = new ItemDocumentMapper(); + printer = new PrettyPrinter(_width, 4, true, true); + } +} +Printer::~Printer(void) { + delete printer; + delete ism; +} + +void Printer::p(Document* d) { + printer->print(d); + printer->print(_os); + delete printer; + printer = new PrettyPrinter(_width, 4, true, true); +} +void Printer::p(const Item* i) { + Document* d; + switch (i->iid()) { + case Item::II_INC: + d = ism->mapIncludeI(*i->cast()); + break; + case Item::II_VD: + d = ism->mapVarDeclI(*i->cast()); + break; + case Item::II_ASN: + d = ism->mapAssignI(*i->cast()); + break; + case Item::II_CON: + d = ism->mapConstraintI(*i->cast()); + break; + case Item::II_SOL: + d = ism->mapSolveI(*i->cast()); + break; + case Item::II_OUT: + d = ism->mapOutputI(*i->cast()); + break; + case Item::II_FUN: + d = ism->mapFunctionI(*i->cast()); + break; + } + p(d); + delete d; +} + +void Printer::print(const Expression* e) { + if (_width == 0) { + PlainPrinter p(_os, _flatZinc, env); + p.p(e); + } else { + init(); + Document* d = expressionToDocument(e); + p(d); + delete d; + } +} +void Printer::print(const Item* i) { + if (_width == 0) { + PlainPrinter p(_os, _flatZinc, env); + p.p(i); + } else { + init(); + p(i); + } +} +void Printer::print(const Model* m) { + if (_width == 0) { + PlainPrinter p(_os, _flatZinc, env); + for (unsigned int i = 0; i < m->size(); i++) { + p.p((*m)[i]); + } + } else { + init(); + for (unsigned int i = 0; i < m->size(); i++) { + p((*m)[i]); + } + } +} + +} // namespace MiniZinc + +void debugprint(MiniZinc::Expression* e) { std::cerr << *e << "\n"; } +void debugprint(MiniZinc::Item* i) { std::cerr << *i; } +void debugprint(MiniZinc::Model* m) { + MiniZinc::Printer p(std::cerr, 0); + p.print(m); +} +void debugprint(const MiniZinc::Location& loc) { std::cerr << loc << std::endl; } diff --git a/lib/process.cpp b/lib/process.cpp new file mode 100644 index 0000000..7499076 --- /dev/null +++ b/lib/process.cpp @@ -0,0 +1,32 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc { + +#ifdef _WIN32 + +void TimeOut(HANDLE hProcess, bool* doneStdout, bool* doneStderr, int timeout, + std::timed_mutex* mtx) { + if (timeout > 0) { + if (!mtx->try_lock_for(std::chrono::milliseconds(timeout))) { + if ((!*doneStdout) || (!*doneStderr)) { + *doneStdout = true; + *doneStderr = true; + TerminateProcess(hProcess, 0); + } + } + } +} +#endif + +} // namespace MiniZinc diff --git a/lib/reader.cpp b/lib/reader.cpp new file mode 100644 index 0000000..e2b1779 --- /dev/null +++ b/lib/reader.cpp @@ -0,0 +1,856 @@ + +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This (main) file coordinates flattening and solving. + * The corresponding modules are flexibly plugged in + * as derived classes, prospectively from DLLs. + * A flattening module should provide MinZinc::GetFlattener() + * A solving module should provide an object of a class derived from SolverFactory. + * Need to get more flexible for multi-pass & multi-solving stuff TODO + */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include +#include +#include +#include +#include + +namespace MiniZinc { + +MznReader::MznReader(std::ostream& os0, std::ostream& log0) + : solver_configs(log0), /*flt(os0,log0,solver_configs.mznlibDir()), */ os(os0), log(log0) {} + +MznReader::~MznReader() { GC::trigger(); } + +void MznReader::printUsage() { + os << "TODO: print usage" << std::endl; + /* + os << executable_name << ": "; + if ( ifMzn2Fzn() ) { + os + << "MiniZinc to FlatZinc converter.\n" + << "Usage: " << executable_name + << " [] [-I ] .mzn [.dzn ...]" << std::endl; + } else if (ifSolns2out()) { + os + << "Solutions to output translator.\n" + << "Usage: " << executable_name + << " [] .ozn" << std::endl; + } else { + os + << "MiniZinc driver.\n" + << "Usage: " << executable_name + << " [] [-I ] .mzn [.dzn ...] or just .fzn" << + std::endl; + } + */ +} + +void MznReader::printHelp(const std::string& selectedSolver) { + printUsage(); + os << "TODO: print help" << std::endl; + /* + os + << "General options:" << std::endl + << " --help, -h\n Print this help message." << std::endl + << " --version\n Print version information." << std::endl + << " --solvers\n Print list of available solvers." << std::endl + << " --time-limit \n Stop after milliseconds (includes compilation and solving)." + << std::endl + << " --solver , --solver .msc\n Select solver to use." << + std::endl + << " --help \n Print help for a particular solver." << std::endl + << " -v, -l, --verbose\n Print progress/log statements. Note that some solvers may log to + stdout." << std::endl + << " --verbose-compilation\n Print progress/log statements for compilation." << std::endl + << " -s, --statistics\n Print statistics." << std::endl + << " --compiler-statistics\n Print statistics for compilation." << std::endl + << " -c, --compile\n Compile only (do not run solver)." << std::endl + << " --config-dirs\n Output configuration directories." << std::endl; + + if (selectedSolver.empty()) { + flt.printHelp(os); + os << endl; + if ( !ifMzn2Fzn() ) { + s2out.printHelp(os); + os << endl; + } + os << "Available solvers (get help using --help ):" << endl; + std::vector solvers = solver_configs.solvers(); + if (solvers.size()==0) + cout << " none.\n"; + for (unsigned int i=0; igetSolverFactories().rbegin(); it != + getGlobalSolverRegistry()->getSolverFactories().rend(); ++it) { if ((*it)->getId()==solverId) { os + << endl; + (*it)->printHelp(os); + if (!sc.executable().empty() && !sc.extraFlags().empty()) { + os << "Extra solver flags (use with "; + os << (sc.supportsMzn() ? "--mzn-flags" : "--fzn-flags") << ")" << endl; + for (const SolverConfig::ExtraFlag& ef: sc.extraFlags()) { + os << " " << ef.flag << endl << " " << ef.description << endl; + } + } + found = true; + } + } + if (!found) { + os << "No help found for solver " << selectedSolver << endl; + } + } + */ +} + +bool MznReader::processOption(int& i, std::vector& argv) { + CLOParser cop(i, argv); + string buffer; + + if (cop.getOption("-I --search-dir", &buffer)) { + includePaths.push_back(buffer + string("/")); + } else if (cop.getOption("--ignore-stdlib")) { + flag_ignoreStdlib = true; + /* + } else if ( cop.getOption( "--instance-check-only") ) { + flag_instance_check_only = true; + } else if ( cop.getOption( "-e --model-check-only") ) { + flag_model_check_only = true; + } else if ( cop.getOption( "--model-interface-only") ) { + flag_model_interface_only = true; + } else if ( cop.getOption( "--model-types-only") ) { + flag_model_types_only = true; + */ + } else if (cop.getOption("-v --verbose")) { + flag_verbose = true; + /* + } else if ( cop.getOption( "--no-optimize --no-optimise") ) { + flag_optimize = false; + } else if ( cop.getOption( "--no-output-ozn -O-") ) { + flag_no_output_ozn = true; + } else if ( cop.getOption( "--output-base", &flag_output_base ) ) { + } else if ( cop.getOption( + fOutputByDefault ? + "-o --fzn --output-to-file --output-fzn-to-file" + : "--fzn --output-fzn-to-file", &flag_output_fzn) ) { + } else if ( cop.getOption( "--output-paths-to-file", &flag_output_paths) ) { + fopts.collect_mzn_paths = true; + } else if ( cop.getOption( "--output-paths") ) { + fopts.collect_mzn_paths = true; + */ + /* + } else if ( cop.getOption( "--output-to-stdout --output-fzn-to-stdout" ) ) { + flag_output_fzn_stdout = true; + } else if ( cop.getOption( "--output-ozn-to-stdout" ) ) { + flag_output_ozn_stdout = true; + } else if ( cop.getOption( "--output-paths-to-stdout" ) ) { + fopts.collect_mzn_paths = true; + flag_output_paths_stdout = true; + } else if ( cop.getOption( "--output-mode", &buffer ) ) { + if (buffer == "dzn") { + flag_output_mode = FlatteningOptions::OUTPUT_DZN; + } else if (buffer == "json") { + flag_output_mode = FlatteningOptions::OUTPUT_JSON; + } else if (buffer == "item") { + flag_output_mode = FlatteningOptions::OUTPUT_ITEM; + } else { + return false; + } + } else if ( cop.getOption( "--output-objective" ) ) { + flag_output_objective = true; + } else if ( cop.getOption( "- --input-from-stdin" ) ) { + flag_stdinInput = true; + */ + } else if (cop.getOption("-d --data", &buffer)) { + if (buffer.length() <= 4 || buffer.substr(buffer.length() - 4, string::npos) != ".dzn") + return false; + datafiles.push_back(buffer); + } else if (cop.getOption("--stdlib-dir", &std_lib_dir)) { + } else if (cop.getOption("-G --globals-dir --mzn-globals-dir", &globals_dir)) { + } else if (cop.getOption("-D --cmdline-data", &buffer)) { + datafiles.push_back("cmd:/" + buffer); + } else if (cop.getOption("--allow-unbounded-vars")) { + flag_allow_unbounded_vars = true; + } else if (cop.getOption("--only-range-domains")) { + flag_only_range_domains = true; + /* + } else if ( cop.getOption( "--no-MIPdomains" ) ) { // internal + flag_noMIPdomains = true; + } else if ( cop.getOption( "--MIPDMaxIntvEE", &opt_MIPDmaxIntvEE ) ) { + } else if ( cop.getOption( "--MIPDMaxDensEE", &opt_MIPDmaxDensEE ) ) { + */ + } else if (cop.getOption("-Werror")) { + flag_werror = true; + /* + } else if (string(argv[i])=="--use-gecode") { +#ifdef HAS_GECODE + flag_two_pass = true; + flag_gecode = true; +#else + log << "warning: Gecode not available. Ignoring '--use-gecode'\n"; +#endif + } else if (string(argv[i])=="--sac") { +#ifdef HAS_GECODE + flag_two_pass = true; + flag_gecode = true; + flag_sac = true; +#else + log << "warning: Gecode not available. Ignoring '--sac'\n"; +#endif + + } else if (string(argv[i])=="--shave") { +#ifdef HAS_GECODE + flag_two_pass = true; + flag_gecode = true; + flag_shave = true; +#else + log << "warning: Gecode not available. Ignoring '--shave'\n"; +#endif + } else if (string(argv[i])=="--two-pass") { + flag_two_pass = true; + } else if (string(argv[i])=="--npass") { + i++; + if (i==argv.size()) return false; + log << "warning: --npass option is deprecated --two-pass\n"; + int passes = atoi(argv[i].c_str()); + if(passes == 1) flag_two_pass = false; + else if(passes == 2) flag_two_pass = true; + } else if (string(argv[i])=="--pre-passes") { + i++; + if (i==argv.size()) return false; + int passes = atoi(argv[i].c_str()); + if(passes >= 0) { + flag_pre_passes = static_cast(passes); + } + } else if (string(argv[i])=="-O0") { + flag_optimize = false; + } else if (string(argv[i])=="-O1") { + // Default settings + } else if (string(argv[i])=="-O2") { + flag_two_pass = true; +#ifdef HAS_GECODE + } else if (string(argv[i])=="-O3") { + flag_two_pass = true; + flag_gecode = true; + } else if (string(argv[i])=="-O4") { + flag_two_pass = true; + flag_gecode = true; + flag_shave = true; + } else if (string(argv[i])=="-O5") { + flag_two_pass = true; + flag_gecode = true; + flag_sac = true; +#else + } else if (string(argv[i])=="-O3" || string(argv[i])=="-O4" || string(argv[i])=="-O5") { + log << "% Warning: This compiler does not have Gecode builtin, cannot process -O3,-O4,-O5.\n"; + return false; +#endif + */ + // ozn options must be after the -O optimisation options + /* + } else if ( cop.getOption( "-O --ozn --output-ozn-to-file", &flag_output_ozn) ) { + } else if (string(argv[i])=="--keep-paths") { + flag_keep_mzn_paths = true; + fopts.collect_mzn_paths = true; + } else if (string(argv[i])=="--only-toplevel-presolve") { + fopts.only_toplevel_paths = true; + } else if ( cop.getOption( "--allow-multiple-assignments" ) ) { + flag_allow_multi_assign = true; + } else if (string(argv[i])=="--input-is-flatzinc") { + is_flatzinc = true; + } else if ( cop.getOption( "--compile-solution-checker", &buffer) ) { + if (buffer.length()>=8 && buffer.substr(buffer.length()-8,string::npos) == ".mzc.mzn") { + flag_compile_solution_check_model = true; + flag_model_check_only = true; + filenames.push_back(buffer); + } else { + log << "Error: solution checker model must have extension .mzc.mzn" << std::endl; + return false; + } + */ + } else { + std::string input_file(argv[i]); + if (input_file.length() <= 4) { + return false; + } + size_t last_dot = input_file.find_last_of('.'); + if (last_dot == string::npos) { + return false; + } + std::string extension = input_file.substr(last_dot, string::npos); + /* + if ( extension == ".mzc" || (input_file.length()>=8 && + input_file.substr(input_file.length()-8,string::npos) == ".mzc.mzn") ) { + flag_solution_check_model = input_file; + } else */ + if (extension == ".mzn" || extension == ".fzn") { + /* + if ( extension == ".fzn" ) { + is_flatzinc = true; + if ( fOutputByDefault ) // mzn2fzn mode + return false; + } + */ + filenames.push_back(input_file); + } else if (extension == ".dzn" || extension == ".json") { + datafiles.push_back(input_file); + } else { + // if ( fOutputByDefault ) + log << "Error: cannot handle file extension " << extension << "." << std::endl; + return false; + } + } + return true; +} + +MznReader::OptionStatus MznReader::processOptions(std::vector& argv) { + std_lib_dir = solver_configs.mznlibDir(); + + int i(0); + int argc(argv.size()); + for (; i < argv.size(); ++i) { + if (argv[i] == "-h" || argv[i] == "--help") { + if (argc > i + 1) { + printHelp(argv[i + 1]); + } else { + printHelp(); + } + return OPTION_FINISH; + } + if (argv[i] == "--version") { + // flt.printVersion(cout); + cout << "MiniZinc model reader. Version EXTREMELY-ALPHA." << endl; + return OPTION_FINISH; + } + if (argv[i] == "--solvers") { + cout << "MiniZinc driver.\nAvailable solver configurations:\n"; + std::vector solvers = solver_configs.solvers(); + if (solvers.size() == 0) cout << " none.\n"; + for (unsigned int i = 0; i < solvers.size(); i++) { + cout << " " << solvers[i] << endl; + } + cout << "Search path for solver configurations:\n"; + for (const string& p : solver_configs.solverConfigsPath()) { + cout << " " << p << endl; + } + return OPTION_FINISH; + } + /* + if (argv[i]=="--solvers-json") { + cout << solver_configs.solverConfigsJSON(); + return OPTION_FINISH; + } + */ + if (argv[i] == "--config-dirs") { + GCLock lock; + cout << "{\n"; + cout << " \"globalConfigFile\" : \"" + << Printer::escapeStringLit(FileUtils::global_config_file()) << "\",\n"; + cout << " \"userConfigFile\" : \"" << Printer::escapeStringLit(FileUtils::user_config_file()) + << "\",\n"; + cout << " \"userSolverConfigDir\" : \"" + << Printer::escapeStringLit(FileUtils::user_config_dir()) << "/solvers\",\n"; + cout << " \"mznStdlibDir\" : \"" << Printer::escapeStringLit(solver_configs.mznlibDir()) + << "\"\n"; + cout << "}\n"; + return OPTION_FINISH; + } + if (!processOption(i, argv)) return OPTION_ERROR; + } + return OPTION_OK; +} + +Model* MznReader::read(const std::string& modelString, const std::string& modelName) { + Env env(NULL, os, log); + + if (std_lib_dir == "") { + throw Error( + "Error: unknown minizinc standard library directory.\n" + "Specify --stdlib-dir on the command line or set the\n" + "MZN_STDLIB_DIR environment variable."); + } + if (globals_dir != "") { + includePaths.insert(includePaths.begin(), std_lib_dir + "/" + globals_dir + "/"); + } + includePaths.push_back(std_lib_dir + "/std/"); + + for (unsigned int i = 0; i < includePaths.size(); i++) { + if (!FileUtils::directory_exists(includePaths[i])) { + throw Error("Cannot access include directory " + includePaths[i]); + } + } + + Model* m(nullptr); + + m = parse(env, filenames, datafiles, modelString, modelName.empty() ? "stdin" : modelName, + includePaths, flag_ignoreStdlib, false, flag_verbose, log); + return m; +} + +#if 0 +void addFlags(const std::string& sep, + const std::vector& in_args, + std::vector& out_args) { + for(const std::string& arg : in_args) { + out_args.push_back(sep); + out_args.push_back(arg); + } +} + +MznReader::OptionStatus MznReader::processOptions(std::vector& argv) +{ + /* + executable_name = argv[0]; + executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); + size_t lastdot = executable_name.find_last_of('.'); + if (lastdot != std::string::npos) { + executable_name = executable_name.substr(0, lastdot); + } + */ + string solver; + /* + bool mzn2fzn_exe = (executable_name=="mzn2fzn"); + if (mzn2fzn_exe) { + is_mzn2fzn=true; + } else if (executable_name=="solns2out") { + s2out._opt.flag_standaloneSolns2Out=true; + } + bool compileSolutionChecker = false; + */ + int i=1, j=1; + int argc = static_cast(argv.size()); + if (argc < 2) + return OPTION_ERROR; + for (i=1; i i+1) { + printHelp(argv[i+1]); + } else { + printHelp(); + } + return OPTION_FINISH; + } + if (argv[i]=="--version") { + flt.printVersion(cout); + return OPTION_FINISH; + } + if (argv[i]=="--solvers") { + cout << "MiniZinc driver.\nAvailable solver configurations:\n"; + std::vector solvers = solver_configs.solvers(); + if (solvers.size()==0) + cout << " none.\n"; + for (unsigned int i=0; i0 && solver != argv[i]) { + log << "Only one --solver option allowed" << endl; + return OPTION_ERROR; + } + solver = argv[i]; + } else if (argv[i]=="-c" || argv[i]=="--compile") { + is_mzn2fzn = true; + } else if (argv[i]=="-v" || argv[i]=="--verbose" || argv[i]=="-l") { + flag_verbose = true; + flag_compiler_verbose = true; + } else if (argv[i]=="--verbose-compilation") { + flag_compiler_verbose = true; + } else if (argv[i]=="-s" || argv[i]=="--statistics") { + flag_statistics = true; + flag_compiler_statistics = true; + } else if (argv[i]=="--compiler-statistics") { + flag_compiler_statistics = true; + } else { + if ((argv[i]=="--fzn-cmd" || argv[i]=="--flatzinc-cmd") && solver.empty()) { + solver = "org.minizinc.mzn-fzn"; + } + if (argv[i]=="--compile-solution-checker") { + compileSolutionChecker = true; + } + argv[j++] = argv[i]; + } + } + argv.resize(j); + argc = j; + + /* + if ( (mzn2fzn_exe || compileSolutionChecker) && solver.empty()) { + solver = "org.minizinc.mzn-fzn"; + } + */ + + if (flag_verbose) { + argv.push_back("--verbose-solving"); + argc++; + } + if (flag_statistics) { + argv.push_back("--solver-statistics"); + argc++; + } + + // flt.set_flag_output_by_default(ifMzn2Fzn()); + + // bool isMznMzn = false; + + if (/*!ifSolns2out()*/ true) { + try { + const SolverConfig& sc = solver_configs.config(solver); + string solverId = sc.executable().empty() ? sc.id() : (sc.supportsMzn() ? string("org.minizinc.mzn-mzn") : string("org.minizinc.mzn-fzn")); + for (auto it = getGlobalSolverRegistry()->getSolverFactories().begin(); + it != getGlobalSolverRegistry()->getSolverFactories().end(); ++it) { + if ((*it)->getId()==solverId) { /// TODO: also check version (currently assumes all ids are unique) + sf = *it; + si_opt = sf->createOptions(); + if (!sc.executable().empty() || solverId=="org.minizinc.mzn-fzn") { + std::vector acceptedFlags; + for (auto& sf : sc.stdFlags()) + acceptedFlags.push_back(MZNFZNSolverFlag::std(sf)); + for (auto& ef : sc.extraFlags()) + acceptedFlags.push_back(MZNFZNSolverFlag::extra(ef.flag,ef.flag_type)); + + // Collect arguments required for underlying exe + vector fzn_mzn_flags; + if (sc.needsStdlibDir()) { + fzn_mzn_flags.push_back("--stdlib-dir"); + fzn_mzn_flags.push_back(FileUtils::share_directory()); + } + if (sc.needsMznExecutable()) { + fzn_mzn_flags.push_back("--minizinc-exe"); + fzn_mzn_flags.push_back(FileUtils::progpath() + "/" + executable_name); + } + + if (sc.supportsMzn()) { + isMznMzn = true; + static_cast(sf)->setAcceptedFlags(si_opt, acceptedFlags); + std::vector additionalArgs_s; + additionalArgs_s.push_back("-m"); + if (sc.executable_resolved().size()) { + additionalArgs_s.push_back(sc.executable_resolved()); + } else { + additionalArgs_s.push_back(sc.executable()); + } + + if(!fzn_mzn_flags.empty()) { + addFlags("--mzn-flag", fzn_mzn_flags, additionalArgs_s); + } + + // This should maybe be moved to fill in fzn_mzn_flags when + // --find-muses is implemented (these arguments will be passed + // through to the subsolver of findMUS) + if (!sc.mznlib().empty()) { + if (sc.mznlib().substr(0,2)=="-G") { + additionalArgs_s.push_back("--mzn-flag"); + additionalArgs_s.push_back(sc.mznlib()); + } else { + additionalArgs_s.push_back("--mzn-flag"); + additionalArgs_s.push_back("-I"); + additionalArgs_s.push_back("--mzn-flag"); + std::string _mznlib; + if (sc.mznlib_resolved().size()) { + _mznlib = sc.mznlib_resolved(); + } else { + _mznlib = sc.mznlib(); + } + additionalArgs_s.push_back(_mznlib); + } + } + + for (i=0; iprocessOption(si_opt, i, additionalArgs_s); + if (!success) { + log << "Solver backend " << solverId << " does not recognise option " << additionalArgs_s[i] << "." << endl; + return OPTION_ERROR; + } + } + } else { + static_cast(sf)->setAcceptedFlags(si_opt, acceptedFlags); + std::vector additionalArgs; + additionalArgs.push_back("--fzn-cmd"); + if (sc.executable_resolved().size()) { + additionalArgs.push_back(sc.executable_resolved()); + } else { + additionalArgs.push_back(sc.executable()); + } + if(!fzn_mzn_flags.empty()) { + addFlags("--fzn-flag", fzn_mzn_flags, additionalArgs); + } + if (sc.needsPathsFile()) { + // Instruct flattener to hold onto paths + int i=0; + vector args {"--keep-paths"}; + flt.processOption(i, args); + + // Instruct FznSolverInstance to write a path file + // and pass it to the executable with --paths arg + additionalArgs.push_back("--fzn-needs-paths"); + } + if (!sc.needsSolns2Out()) { + additionalArgs.push_back("--fzn-output-passthrough"); + } + int i=0; + for (i=0; iprocessOption(si_opt, i, additionalArgs); + if (!success) { + log << "Solver backend " << solverId << " does not recognise option " << additionalArgs[i] << "." << endl; + return OPTION_ERROR; + } + } + } + } + if (!sc.mznlib().empty()) { + if (sc.mznlib().substr(0,2)=="-G") { + std::vector additionalArgs({sc.mznlib()}); + int i=0; + if (!flt.processOption(i, additionalArgs)) { + log << "Flattener does not recognise option " << sc.mznlib() << endl; + return OPTION_ERROR; + } + } else { + std::vector additionalArgs(2); + additionalArgs[0] = "-I"; + if (sc.mznlib_resolved().size()) { + additionalArgs[1] = sc.mznlib_resolved(); + } else { + additionalArgs[1] = sc.mznlib(); + } + int i=0; + if (!flt.processOption(i, additionalArgs)) { + log << "Flattener does not recognise option -I." << endl; + return OPTION_ERROR; + } + } + } + if (!sc.defaultFlags().empty()) { + std::vector addedArgs; + addedArgs.push_back(argv[0]); // excutable name + for (auto& df : sc.defaultFlags()) { + addedArgs.push_back(df); + } + for (int i=1; iprocessOption(si_opt, i, argv)) { + } else { + std::string executable_name(argv[0]); + executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); + log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'" << endl; + return OPTION_ERROR; + } + } + return OPTION_OK; + + } else { + for (i=1; iprocessFlatZinc(); + } + SolverInstance::Status status = getSI()->solve(); + GCLock lock; + if (status==SolverInstance::SAT || status==SolverInstance::OPT) { + if ( !getSI()->getSolns2Out()->fStatusPrinted ) + getSI()->getSolns2Out()->evalStatus( status ); + } + else { + if ( !getSI()->getSolns2Out()->fStatusPrinted ) + getSI()->getSolns2Out()->evalStatus( status ); + } + if (si_opt->printStatistics) + printStatistics(); + return status; +} + +void MznSolver::printStatistics() +{ // from flattener too? TODO + if (si) + getSI()->printStatistics(); +} + +SolverInstance::Status MznSolver::run(const std::vector& args0, const std::string& model, + const std::string& exeName, const std::string& modelName) { + using namespace std::chrono; + steady_clock::time_point startTime = steady_clock::now(); + std::vector args = {exeName}; + for (auto a : args0) + args.push_back(a); + switch (processOptions(args)) { + case OPTION_FINISH: + return SolverInstance::NONE; + case OPTION_ERROR: + printUsage(); + os << "More info with \"" << exeName << " --help\"\n"; + return SolverInstance::ERROR; + case OPTION_OK: + break; + } + if (!(!ifMzn2Fzn() && sf!=NULL && sf->getId() == "org.minizinc.mzn-mzn") && !flt.hasInputFiles() && model.empty()) { + // We are in solns2out mode + while ( std::cin.good() ) { + string line; + getline( std::cin, line ); + line += '\n'; // need eols as in t=raw stream + s2out.feedRawDataChunk( line.c_str() ); + } + return SolverInstance::NONE; + } + + if (!ifMzn2Fzn() && sf->getId() == "org.minizinc.mzn-mzn") { + Env env; + si = sf->createSI(env, log, si_opt); + si->setSolns2Out( &s2out ); + { // To be able to clean up flatzinc after PrcessFlt() + GCLock lock; + getSI()->_options->verbose = get_flag_verbose(); + getSI()->_options->printStatistics = get_flag_statistics(); + } + getSI()->solve(); + return SolverInstance::NONE; + } + + flatten(model,modelName); + + if (!ifMzn2Fzn() && flag_overall_time_limit != 0) { + steady_clock::time_point afterFlattening = steady_clock::now(); + milliseconds passed = duration_cast(afterFlattening-startTime); + milliseconds time_limit(flag_overall_time_limit); + if (passed > time_limit) { + s2out.evalStatus( getFltStatus() ); + return SolverInstance::UNKNOWN; + } + int time_left = (time_limit-passed).count(); + std::vector timeoutArgs(2); + timeoutArgs[0] = "--solver-time-limit"; + std::ostringstream oss; + oss << time_left; + timeoutArgs[1] = oss.str(); + int i=0; + sf->processOption(si_opt, i, timeoutArgs); + } + + if (SolverInstance::UNKNOWN == getFltStatus()) + { + if ( !ifMzn2Fzn() ) { // only then + // GCLock lock; // better locally, to enable cleanup after ProcessFlt() + addSolverInterface(); + return solve(); + } + return SolverInstance::NONE; + } else { + if ( !ifMzn2Fzn() ) + s2out.evalStatus( getFltStatus() ); + return getFltStatus(); + } // Add evalOutput() here? TODO +} +#endif + +}; // namespace MiniZinc diff --git a/lib/solns2out.cpp b/lib/solns2out.cpp new file mode 100644 index 0000000..e4c7774 --- /dev/null +++ b/lib/solns2out.cpp @@ -0,0 +1,490 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was ! distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include + +#include + +using namespace std; +using namespace MiniZinc; + +void Solns2Out::printHelp(ostream& os) { + os << "Solution output options:" << std::endl + << " --ozn-file \n Read output specification from ozn file." << std::endl + << " -o , --output-to-file \n Filename for generated output." << std::endl + << " -i , --ignore-lines , --ignore-leading-lines \n Ignore the first lines " + "in the FlatZinc solution stream." + << std::endl + << " --soln-sep , --soln-separator , --solution-separator \n Specify the string " + "printed after each solution (as a separate line).\n The default is to use the same as " + "FlatZinc, \"----------\"." + << std::endl + << " --soln-comma , --solution-comma \n Specify the string used to separate " + "solutions.\n The default is the empty string." + << std::endl + << " --unsat-msg (--unsatisfiable-msg), --unbounded-msg, --unsatorunbnd-msg,\n" + " --unknown-msg, --error-msg, --search-complete-msg \n" + " Specify solution status messages. The defaults:\n" + " \"=====UNSATISFIABLE=====\", \"=====UNSATorUNBOUNDED=====\", " + "\"=====UNBOUNDED=====\",\n" + " \"=====UNKNOWN=====\", \"=====ERROR=====\", \"==========\", respectively." + << std::endl + << " --non-unique\n Allow duplicate solutions.\n" + << " -c, --canonicalize\n Canonicalize the output solution stream (i.e., buffer and " + "sort).\n" + << " --output-non-canonical \n Non-buffered solution output file in case of " + "canonicalization.\n" + << " --output-raw \n File to dump the solver's raw output (not for hard-linked " + "solvers)\n" + // Unclear how to exit then: + // << " --number-output \n Maximal number of different solutions printed." << + // std::endl + << " --no-output-comments\n Do not print comments in the FlatZinc solution stream." + << std::endl + << " --output-time\n Print timing information in the FlatZinc solution stream." + << std::endl + << " --no-flush-output\n Don't flush output stream after every line." << std::endl; +} + +bool Solns2Out::processOption(int& i, std::vector& argv) { + CLOParser cop(i, argv); + std::string oznfile; + if (cop.getOption("--ozn-file", &oznfile)) { + initFromOzn(oznfile); + } else if (cop.getOption("-o --output-to-file", &_opt.flag_output_file)) { + } else if (cop.getOption("--no-flush-output")) { + _opt.flag_output_flush = false; + } else if (cop.getOption("--no-output-comments")) { + _opt.flag_output_comments = false; + } else if (cop.getOption("-i --ignore-lines --ignore-leading-lines", &_opt.flag_ignore_lines)) { + } else if (cop.getOption("--output-time")) { + _opt.flag_output_time = true; + } else if (cop.getOption("--soln-sep --soln-separator --solution-separator", + &_opt.solution_separator)) { + } else if (cop.getOption("--soln-comma --solution-comma", &_opt.solution_comma)) { + } else if (cop.getOption("--unsat-msg --unsatisfiable-msg", &_opt.unsatisfiable_msg)) { + } else if (cop.getOption("--unbounded-msg", &_opt.unbounded_msg)) { + } else if (cop.getOption("--unsatorunbnd-msg", &_opt.unsatorunbnd_msg)) { + } else if (cop.getOption("--unknown-msg", &_opt.unknown_msg)) { + } else if (cop.getOption("--error-msg", &_opt.error_msg)) { + } else if (cop.getOption("--search-complete-msg", &_opt.search_complete_msg)) { + } else if (cop.getOption("--unique")) { + _opt.flag_unique = true; + } else if (cop.getOption("--non-unique")) { + _opt.flag_unique = false; + } else if (cop.getOption("-c --canonicalize")) { + _opt.flag_canonicalize = true; + } else if (cop.getOption("--output-non-canonical --output-non-canon", + &_opt.flag_output_noncanonical)) { + } else if (cop.getOption("--output-raw", &_opt.flag_output_raw)) { + // } else if ( cop.getOption( "--number-output", &_opt.flag_number_output ) ) { + } else if (_opt.flag_standaloneSolns2Out) { + std::string oznfile(argv[i]); + if (oznfile.length() <= 4) { + return false; + } + size_t last_dot = oznfile.find_last_of('.'); + if (last_dot == string::npos) { + return false; + } + std::string extension = oznfile.substr(last_dot, string::npos); + if (extension == ".ozn") { + initFromOzn(oznfile); + return true; + } + return false; + } else { + return false; + } + return true; +} + +bool Solns2Out::initFromEnv(Env* pE) { + assert(pE); + pEnv = pE; + init(); + return true; +} + +void Solns2Out::initFromOzn(const std::string& filename) { + std::vector filenames(1, filename); + + includePaths.push_back(stdlibDir + "/std/"); + + for (unsigned int i = 0; i < includePaths.size(); i++) { + if (!FileUtils::directory_exists(includePaths[i])) { + std::cerr << "solns2out: cannot access include directory " << includePaths[i] << "\n"; + std::exit(EXIT_FAILURE); + } + } + + { + pEnv = new Env(); + std::stringstream errstream; + if ((pOutput = parse(*pEnv, filenames, std::vector(), "", "", includePaths, false, + false, false, errstream))) { + std::vector typeErrors; + pEnv->model(pOutput); + MZN_ASSERT_HARD_MSG(pEnv, "solns2out: could not allocate Env"); + pEnv_guard.reset(pEnv); + MiniZinc::typecheck(*pEnv, pOutput, typeErrors, false, false); + MiniZinc::registerBuiltins(*pEnv); + pEnv->envi().swap_output(); + init(); + } else { + throw Error(errstream.str()); + } + } +} + +void Solns2Out::createOutputMap() { + for (unsigned int i = 0; i < getModel()->size(); i++) { + if (VarDeclI* vdi = (*getModel())[i]->dyn_cast()) { + GCLock lock; + declmap.insert( + pair(vdi->e()->id()->str().str(), DE(vdi->e(), vdi->e()->e()))); + } else if (OutputI* oi = (*getModel())[i]->dyn_cast()) { + MZN_ASSERT_HARD_MSG(outputExpr == oi->e(), + "solns2out_base: <=1 output items allowed currently TODO?"); + } + } +} + +Solns2Out::DE& Solns2Out::findOutputVar(ASTString id) { + declNewOutput(); + if (declmap.empty()) createOutputMap(); + auto it = declmap.find(id.str()); + MZN_ASSERT_HARD_MSG(declmap.end() != it, "solns2out_base: unexpected id in output: " << id); + return it->second; +} + +void Solns2Out::restoreDefaults() { + for (unsigned int i = 0; i < getModel()->size(); i++) { + if (VarDeclI* vdi = (*getModel())[i]->dyn_cast()) { + GCLock lock; + auto& de = findOutputVar(vdi->e()->id()->str()); + vdi->e()->e(de.second()); + vdi->e()->evaluated(false); + } + } + fNewSol2Print = false; +} + +void Solns2Out::parseAssignments(string& solution) { + std::vector se; + unique_ptr sm(parseFromString(*pEnv, solution, "solution received from solver", + includePaths, true, false, false, log, se)); + if (sm.get() == NULL) throw Error("solns2out_base: could not parse solution"); + solution = ""; + for (unsigned int i = 0; i < sm->size(); i++) { + if (AssignI* ai = (*sm)[i]->dyn_cast()) { + auto& de = findOutputVar(ai->id()); + ai->e()->type(de.first->type()); + ai->decl(de.first); + typecheck(*pEnv, getModel(), ai); + if (Call* c = ai->e()->dyn_cast()) { + // This is an arrayXd call, make sure we get the right builtin + assert(c->arg(c->n_args() - 1)->isa()); + for (unsigned int i = 0; i < c->n_args(); i++) c->arg(i)->type(Type::parsetint()); + c->arg(c->n_args() - 1)->type(de.first->type()); + c->decl(getModel()->matchFn(pEnv->envi(), c, false)); + } + de.first->e(ai->e()); + } + } + declNewOutput(); +} + +void Solns2Out::declNewOutput() { + fNewSol2Print = true; + status = SolverInstance::SAT; +} + +bool Solns2Out::evalOutput(const string& s_ExtraInfo) { + if (!fNewSol2Print) return true; + ostringstream oss; + if (!__evalOutput(oss)) return false; + if (!checkerModel.empty()) checkSolution(oss); + bool fNew = true; + if (_opt.flag_unique || _opt.flag_canonicalize) { + auto res = sSolsCanon.insert(oss.str()); + if (!res.second) // repeated solution + fNew = false; + } + if (fNew) { + ++nSolns; + if (_opt.flag_canonicalize) { + if (pOfs_non_canon.get()) + if (pOfs_non_canon->good()) { + (*pOfs_non_canon) << oss.str(); + (*pOfs_non_canon) << comments; + if (s_ExtraInfo.size()) { + (*pOfs_non_canon) << s_ExtraInfo; + if ('\n' != s_ExtraInfo.back()) /// TODO is this enough to check EOL? + (*pOfs_non_canon) << '\n'; + } + if (_opt.flag_output_time) + (*pOfs_non_canon) << "% time elapsed: " << starttime.stoptime() << "\n"; + if (!_opt.solution_separator.empty()) + (*pOfs_non_canon) << _opt.solution_separator << '\n'; + if (_opt.flag_output_flush) pOfs_non_canon->flush(); + } + } else { + if (_opt.solution_comma.size() && nSolns > 1) getOutput() << _opt.solution_comma << '\n'; + getOutput() << oss.str(); + } + } + getOutput() << comments; // print them now ???? + comments = ""; + if (s_ExtraInfo.size()) { + getOutput() << s_ExtraInfo; + if ('\n' != s_ExtraInfo.back()) /// TODO is this enough to check EOL? + getOutput() << '\n'; + } + if (fNew && _opt.flag_output_time) + getOutput() << "% time elapsed: " << starttime.stoptime() << "\n"; + if (fNew && !_opt.flag_canonicalize && !_opt.solution_separator.empty()) + getOutput() << _opt.solution_separator << '\n'; + if (_opt.flag_output_flush) getOutput().flush(); + restoreDefaults(); // cleans data. evalOutput() should not be called again w/o assigning new + // data. + return true; +} + +void Solns2Out::checkSolution(std::ostream& os) { +#ifdef HAS_GECODE + + std::ostringstream checker; + checker << checkerModel; + { + GCLock lock; + for (unsigned int i = 0; i < getModel()->size(); i++) { + if (VarDeclI* vdi = (*getModel())[i]->dyn_cast()) { + if (vdi->e()->ann().contains(constants().ann.mzn_check_var)) { + if (Call* cev = vdi->e()->ann().getCall(constants().ann.mzn_check_enum_var)) { + checker << vdi->e()->id()->str() << "= to_enum(" << *cev->arg(0)->cast() << "," + << *eval_par(getEnv()->envi(), vdi->e()->e()) << ");"; + } else { + checker << vdi->e()->id()->str() << "=" << *eval_par(getEnv()->envi(), vdi->e()->e()) + << ";"; + } + } + } + } + } + + std::ostringstream oss_err; + assert(false); + MznSolver slv; + slv.s2out._opt.solution_separator = ""; + try { + std::vector args({"--solver", "org.minizinc.gecode_presolver"}); + // slv.run(args, checker.str(), "minizinc", "checker.mzc"); + } catch (const LocationException& e) { + oss_err << e.loc() << ":" << std::endl; + oss_err << e.what() << ": " << e.msg() << std::endl; + } catch (const Exception& e) { + std::string what = e.what(); + oss_err << what << (what.empty() ? "" : ": ") << e.msg() << std::endl; + } catch (const exception& e) { + oss_err << e.what() << std::endl; + } catch (...) { + oss_err << " UNKNOWN EXCEPTION." << std::endl; + } + std::istringstream iss(oss_err.str()); + std::string line; + os << "% Solution checker report:\n"; + while (std::getline(iss, line)) { + os << "% " << line << "\n"; + } + +#else + os << "% solution checking not supported (need built-in Gecode)" << std::endl; +#endif +} + +bool Solns2Out::__evalOutput(ostream& fout) { + if (0 != outputExpr) { + pEnv->envi().evalOutput(fout); + } + return true; +} + +bool Solns2Out::evalStatus(SolverInstance::Status status) { + if (_opt.flag_canonicalize) __evalOutputFinal(_opt.flag_output_flush); + __evalStatusMsg(status); + fStatusPrinted = 1; + return true; +} + +bool Solns2Out::__evalOutputFinal(bool) { + /// Print the canonical list + for (auto& sol : sSolsCanon) { + if (_opt.solution_comma.size() && &sol != &*sSolsCanon.begin()) + getOutput() << _opt.solution_comma << '\n'; + getOutput() << sol; + if (!_opt.solution_separator.empty()) getOutput() << _opt.solution_separator << '\n'; + } + return true; +} + +bool Solns2Out::__evalStatusMsg(SolverInstance::Status status) { + std::map stat2msg; + stat2msg[SolverInstance::OPT] = _opt.search_complete_msg; + stat2msg[SolverInstance::UNSAT] = _opt.unsatisfiable_msg; + stat2msg[SolverInstance::UNBND] = _opt.unbounded_msg; + stat2msg[SolverInstance::UNSATorUNBND] = _opt.unsatorunbnd_msg; + stat2msg[SolverInstance::UNKNOWN] = _opt.unknown_msg; + stat2msg[SolverInstance::ERROR] = _opt.error_msg; + stat2msg[SolverInstance::NONE] = ""; + auto it = stat2msg.find(status); + if (stat2msg.end() != it) { + getOutput() << comments; + if (!it->second.empty()) getOutput() << it->second << '\n'; + if (_opt.flag_output_time) getOutput() << "% time elapsed: " << starttime.stoptime() << "\n"; + if (_opt.flag_output_flush) getOutput().flush(); + Solns2Out::status = status; + } else { + getOutput() << comments; + if (_opt.flag_output_flush) getOutput().flush(); + MZN_ASSERT_HARD_MSG(SolverInstance::SAT == status, // which is ignored + "solns2out_base: undefined solution status code " << status); + Solns2Out::status = SolverInstance::SAT; + } + comments = ""; + return true; +} + +void Solns2Out::init() { + for (unsigned int i = 0; i < getModel()->size(); i++) { + if (OutputI* oi = (*getModel())[i]->dyn_cast()) { + outputExpr = oi->e(); + break; + } + if (VarDeclI* vdi = (*getModel())[i]->dyn_cast()) { + if (vdi->e()->id()->idn() == -1 && vdi->e()->id()->v() == "_mzn_solution_checker") { + checkerModel = eval_string(getEnv()->envi(), vdi->e()->e()); + if (checkerModel.size() > 0 && checkerModel[0] == '@') { + checkerModel = FileUtils::decodeBase64(checkerModel); + FileUtils::inflateString(checkerModel); + } + } + } + } + + /// Main output file + if (0 == pOut) { + if (_opt.flag_output_file.size()) { + pOut.reset(new ofstream(_opt.flag_output_file)); + MZN_ASSERT_HARD_MSG(pOut.get(), + "solns2out_base: could not allocate stream object for file output into " + << _opt.flag_output_file); + checkIOStatus(pOut->good(), _opt.flag_output_file); + } + } + /// Non-canonical output + if (_opt.flag_canonicalize && _opt.flag_output_noncanonical.size()) { + pOfs_non_canon.reset(new ofstream(_opt.flag_output_noncanonical)); + MZN_ASSERT_HARD_MSG(pOfs_non_canon.get(), + "solns2out_base: could not allocate stream object for non-canon output"); + checkIOStatus(pOfs_non_canon->good(), _opt.flag_output_noncanonical, 0); + } + /// Raw output + if (_opt.flag_output_raw.size()) { + pOfs_raw.reset(new ofstream(_opt.flag_output_raw)); + MZN_ASSERT_HARD_MSG(pOfs_raw.get(), + "solns2out_base: could not allocate stream object for raw output"); + checkIOStatus(pOfs_raw->good(), _opt.flag_output_raw, 0); + } + /// Assume all options are set before + nLinesIgnore = _opt.flag_ignore_lines; +} + +Solns2Out::Solns2Out(std::ostream& os0, std::ostream& log0, const std::string& stdlibDir0) + : os(os0), log(log0), stdlibDir(stdlibDir0) {} + +Solns2Out::~Solns2Out() { + getOutput() << comments; + if (_opt.flag_output_flush) getOutput() << flush; +} + +ostream& Solns2Out::getOutput() { return ((pOut.get() && pOut->good()) ? *pOut : os); } + +ostream& Solns2Out::getLog() { return log; } + +bool Solns2Out::feedRawDataChunk(const char* data) { + istringstream solstream(data); + while (solstream.good()) { + string line; + getline(solstream, line); + if (line_part.size()) { + line = line_part + line; + line_part.clear(); + } + if (solstream.eof()) { // wait next chunk + line_part = line; + break; // to get to raw output + } + if (line.size()) + if ('\r' == line.back()) line.pop_back(); // For WIN files + if (nLinesIgnore > 0) { + --nLinesIgnore; + continue; + } + if (mapInputStatus.empty()) createInputMap(); + auto it = mapInputStatus.find(line); + if (mapInputStatus.end() != it) { + if (SolverInstance::SAT == it->second) { + parseAssignments(solution); + // evalOutput(); + } else { + evalStatus(it->second); + } + } else { + solution += line + '\n'; + if (_opt.flag_output_comments) { + std::istringstream iss(line); + char c = '_'; + iss >> skipws >> c; + if (iss.good() && '%' == c) { + // Feed comments directly + getOutput() << line << '\n'; + if (_opt.flag_output_flush) getOutput().flush(); + if (pOfs_non_canon.get()) + if (pOfs_non_canon->good()) (*pOfs_non_canon) << line << '\n'; + } + } + } + } + if (pOfs_raw.get()) { + (*pOfs_raw.get()) << data; + if (_opt.flag_output_flush) pOfs_raw->flush(); + } + return true; +} + +void Solns2Out::createInputMap() { + mapInputStatus[_opt.search_complete_msg_00] = SolverInstance::OPT; + mapInputStatus[_opt.solution_separator_00] = SolverInstance::SAT; + mapInputStatus[_opt.unsatisfiable_msg_00] = SolverInstance::UNSAT; + mapInputStatus[_opt.unbounded_msg_00] = SolverInstance::UNBND; + mapInputStatus[_opt.unsatorunbnd_msg_00] = SolverInstance::UNSATorUNBND; + mapInputStatus[_opt.unknown_msg_00] = SolverInstance::UNKNOWN; + mapInputStatus[_opt.error_msg] = SolverInstance::ERROR; +} + +void Solns2Out::printStatistics(ostream& os) { os << "%%%mzn-stat: nSolutions=" << nSolns << "\n"; } diff --git a/lib/solver.cpp b/lib/solver.cpp new file mode 100644 index 0000000..159c5ad --- /dev/null +++ b/lib/solver.cpp @@ -0,0 +1,1085 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This (main) file coordinates flattening and solving. + * The corresponding modules are flexibly plugged in + * as derived classes, prospectively from DLLs. + * A flattening module should provide MinZinc::GetFlattener() + * A solving module should provide an object of a class derived from SolverFactory. + * Need to get more flexible for multi-pass & multi-solving stuff TODO + */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include +#include + +using namespace MiniZinc; + +#ifdef HAS_GUROBI +#include +#endif +#ifdef HAS_CPLEX +#include +#endif +#ifdef HAS_OSICBC +#include +#endif +#ifdef HAS_XPRESS +#include +#endif +#ifdef HAS_GECODE +#include +#endif +#ifdef HAS_GEAS +#include +#endif +#ifdef HAS_SCIP +#include +#endif +#include +#include +#include +#include +#include +#include + +SolverInitialiser::SolverInitialiser(void) { +#ifdef HAS_GUROBI + Gurobi_SolverFactoryInitialiser _gurobi_init; +#endif +#ifdef HAS_CPLEX + static Cplex_SolverFactoryInitialiser _cplex_init; +#endif +#ifdef HAS_OSICBC + static OSICBC_SolverFactoryInitialiser _osicbc_init; +#endif +#ifdef HAS_XPRESS + static Xpress_SolverFactoryInitialiser _xpress_init; +#endif +#ifdef HAS_GECODE + static Gecode_SolverFactoryInitialiser _gecode_init; +#endif +#ifdef HAS_GEAS + static Geas_SolverFactoryInitialiser _geas_init; +#endif +#ifdef HAS_SCIP + static SCIP_SolverFactoryInitialiser _scip_init; +#endif + static FZN_SolverFactoryInitialiser _fzn_init; + static MZN_SolverFactoryInitialiser _mzn_init; + static NL_SolverFactoryInitialiser _nl_init; +} + +MZNFZNSolverFlag MZNFZNSolverFlag::std(const std::string& n0) { + const std::string argFlags("-I -n -p -r"); + if (argFlags.find(n0) != std::string::npos) return MZNFZNSolverFlag(FT_ARG, n0); + return MZNFZNSolverFlag(FT_NOARG, n0); +} + +MZNFZNSolverFlag MZNFZNSolverFlag::extra(const std::string& n0, const std::string& t0) { + return MZNFZNSolverFlag(t0 == "bool" ? FT_NOARG : FT_ARG, n0); +} + +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +SolverRegistry* MiniZinc::getGlobalSolverRegistry() { + static SolverRegistry sr; + return &sr; +} + +void SolverRegistry::addSolverFactory(SolverFactory* pSF) { + assert(pSF); + sfstorage.push_back(pSF); +} + +void SolverRegistry::removeSolverFactory(SolverFactory* pSF) { + auto it = find(sfstorage.begin(), sfstorage.end(), pSF); + assert(pSF); + sfstorage.erase(it); +} + +/// Function createSI also adds each SI to the local storage +SolverInstanceBase* SolverFactory::createSI(std::ostream& log, SolverInstanceBase::Options* opt) { + SolverInstanceBase* pSI = doCreateSI(log, opt); + if (!pSI) { + throw InternalError("SolverFactory: failed to initialize solver " + getDescription()); + } + sistorage.resize(sistorage.size() + 1); + sistorage.back().reset(pSI); + return pSI; +} + +/// also providing a destroy function for a DLL or just special allocator etc. +void SolverFactory::destroySI(SolverInstanceBase* pSI) { + auto it = sistorage.begin(); + for (; it != sistorage.end(); ++it) + if (it->get() == pSI) break; + if (sistorage.end() == it) { + cerr << " SolverFactory: failed to remove solver at " << pSI << endl; + throw InternalError(" SolverFactory: failed to remove solver"); + } + sistorage.erase(it); +} + +MznSolver::MznSolver(std::vector args0) + : solver_configs(std::cerr), + executable_name("minizinc"), + os(std::cout), + log(std::cerr), + s2out(std::cout, std::cerr, solver_configs.mznlibDir()) { + std::vector args = {executable_name}; + args.insert(args.end(), args0.begin(), args0.end()); + switch (processOptions(args)) { + case OPTION_FINISH: + return; + case OPTION_ERROR: + printUsage(); + os << "More info with \"" << executable_name << " --help\"\n"; + return; + case OPTION_OK: + break; + } + + flatten(file, file); + // Definition* head = interpreter->_agg[0].def_stack; + // def_ptr = head; +} + +MznSolver::~MznSolver() { + // if (si) // first the solver + // CleanupSolverInterface(si); + // TODO cleanup the used solver interfaces + delete interpreter; + si = 0; + si_opt = nullptr; + GC::trigger(); +} + +bool MznSolver::ifMzn2Fzn() { return is_mzn2fzn; } + +bool MznSolver::ifSolns2out() { return s2out._opt.flag_standaloneSolns2Out; } + +void MznSolver::addSolverInterface(SolverFactory* sf) { + si = sf->createSI(log, si_opt); + assert(si); + Model* m = in_out_defs.model(); + if (m) { + for (FunctionIterator it = m->begin_functions(); it != m->end_functions(); ++it) { + if (!it->removed()) { + FunctionI& fi = *it; + if (fi.from_stdlib() || fi.ti()->type().isann() || fi.e()) { + continue; + } + si->addFunction(&fi); + } + } + } + si->setSolns2Out(new Solns2Out(os, log, "")); + // if (s2out.getEnv()==NULL) + // s2out.initFromEnv( flt.getEnv() ); + // si->setSolns2Out( &s2out ); + if (flag_compiler_verbose) + log + // << " ---------------------------------------------------------------------------\n" + << " % SOLVING PHASE\n" + << sf->getDescription(si_opt) << endl; +} + +void MznSolver::addSolverInterface() { + GCLock lock; + if (sf == NULL) { + if (getGlobalSolverRegistry()->getSolverFactories().empty()) { + log << " MznSolver: NO SOLVER FACTORIES LINKED." << endl; + assert(0); + } + sf = getGlobalSolverRegistry()->getSolverFactories().back(); + } + addSolverInterface(sf); +} + +void MznSolver::printUsage() { + os << executable_name << ": "; + if (ifMzn2Fzn()) { + os << "MiniZinc to FlatZinc converter.\n" + << "Usage: " << executable_name + << " [] [-I ] .mzn [.dzn ...]" << std::endl; + } else if (ifSolns2out()) { + os << "Solutions to output translator.\n" + << "Usage: " << executable_name << " [] .ozn" << std::endl; + } else { + os << "MiniZinc driver.\n" + << "Usage: " << executable_name + << " [] [-I ] .mzn [.dzn ...] or just .fzn" + << std::endl; + } +} + +void MznSolver::printHelp(const std::string& selectedSolver) { + printUsage(); + os << "General options:" << std::endl + << " --help, -h\n Print this help message." << std::endl + << " --version\n Print version information." << std::endl + << " --solvers\n Print list of available solvers." << std::endl + << " --time-limit \n Stop after milliseconds (includes compilation and solving)." + << std::endl + << " --solver , --solver .msc\n Select solver to use." + << std::endl + << " --help \n Print help for a particular solver." << std::endl + << " -v, -l, --verbose\n Print progress/log statements. Note that some solvers may log to " + "stdout." + << std::endl + << " --verbose-compilation\n Print progress/log statements for compilation." << std::endl + << " -s, --statistics\n Print statistics." << std::endl + << " --compiler-statistics\n Print statistics for compilation." << std::endl + << " -c, --compile\n Compile only (do not run solver)." << std::endl + << " --config-dirs\n Output configuration directories." << std::endl; + + if (selectedSolver.empty()) { + // flt.printHelp(os); + os << endl; + if (!ifMzn2Fzn()) { + s2out.printHelp(os); + os << endl; + } + os << "Available solvers (get help using --help ):" << endl; + std::vector solvers = solver_configs.solvers(); + if (solvers.size() == 0) cout << " none.\n"; + for (unsigned int i = 0; i < solvers.size(); i++) { + cout << " " << solvers[i] << endl; + } + } else { + const SolverConfig& sc = solver_configs.config(selectedSolver); + string solverId = sc.executable().empty() ? sc.id() + : (sc.supportsMzn() ? string("org.minizinc.mzn-mzn") + : string("org.minizinc.mzn-fzn")); + bool found = false; + for (auto it = getGlobalSolverRegistry()->getSolverFactories().rbegin(); + it != getGlobalSolverRegistry()->getSolverFactories().rend(); ++it) { + if ((*it)->getId() == solverId) { + os << endl; + (*it)->printHelp(os); + if (!sc.executable().empty() && !sc.extraFlags().empty()) { + os << "Extra solver flags (use with "; + os << (sc.supportsMzn() ? "--mzn-flags" : "--fzn-flags") << ")" << endl; + for (const SolverConfig::ExtraFlag& ef : sc.extraFlags()) { + os << " " << ef.flag << endl << " " << ef.description << endl; + } + } + found = true; + } + } + if (!found) { + os << "No help found for solver " << selectedSolver << endl; + } + } +} + +void addFlags(const std::string& sep, const std::vector& in_args, + std::vector& out_args) { + for (const std::string& arg : in_args) { + out_args.push_back(sep); + out_args.push_back(arg); + } +} + +MznSolver::OptionStatus MznSolver::processOptions(std::vector& argv) { + executable_name = argv[0]; + executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); + size_t lastdot = executable_name.find_last_of('.'); + if (lastdot != std::string::npos) { + executable_name = executable_name.substr(0, lastdot); + } + string solver; + bool mzn2fzn_exe = (executable_name == "mzn2fzn"); + if (mzn2fzn_exe) { + is_mzn2fzn = true; + } else if (executable_name == "solns2out") { + s2out._opt.flag_standaloneSolns2Out = true; + flag_is_solns2out = true; + } + bool compileSolutionChecker = false; + int i = 1, j = 1; + int argc = static_cast(argv.size()); + if (argc < 2) return OPTION_ERROR; + for (i = 1; i < argc; ++i) { + if (argv[i] == "-h" || argv[i] == "--help") { + if (argc > i + 1) { + printHelp(argv[i + 1]); + } else { + printHelp(); + } + return OPTION_FINISH; + } + if (argv[i] == "--version") { + // flt.printVersion(cout); + return OPTION_FINISH; + } + if (argv[i] == "--solvers") { + cout << "MiniZinc driver.\nAvailable solver configurations:\n"; + std::vector solvers = solver_configs.solvers(); + if (solvers.size() == 0) cout << " none.\n"; + for (unsigned int i = 0; i < solvers.size(); i++) { + cout << " " << solvers[i] << endl; + } + cout << "Search path for solver configurations:\n"; + for (const string& p : solver_configs.solverConfigsPath()) { + cout << " " << p << endl; + } + return OPTION_FINISH; + } + if (argv[i] == "--solvers-json") { + cout << solver_configs.solverConfigsJSON(); + return OPTION_FINISH; + } + if (argv[i] == "--config-dirs") { + GCLock lock; + cout << "{\n"; + cout << " \"globalConfigFile\" : \"" + << Printer::escapeStringLit(FileUtils::global_config_file()) << "\",\n"; + cout << " \"userConfigFile\" : \"" << Printer::escapeStringLit(FileUtils::user_config_file()) + << "\",\n"; + cout << " \"userSolverConfigDir\" : \"" + << Printer::escapeStringLit(FileUtils::user_config_dir()) << "/solvers\",\n"; + cout << " \"mznStdlibDir\" : \"" << Printer::escapeStringLit(solver_configs.mznlibDir()) + << "\"\n"; + cout << "}\n"; + return OPTION_FINISH; + } + if (argv[i] == "--time-limit") { + ++i; + if (i == argc) { + log << "Argument required for --time-limit" << endl; + return OPTION_ERROR; + } + flag_overall_time_limit = atoi(argv[i].c_str()); + } else if (argv[i] == "--solver") { + ++i; + if (i == argc) { + log << "Argument required for --solver" << endl; + return OPTION_ERROR; + } + if (solver.size() > 0 && solver != argv[i]) { + log << "Only one --solver option allowed" << endl; + return OPTION_ERROR; + } + solver = argv[i]; + } else if (argv[i] == "--output-dict") { + output_dict = true; + } else if (argv[i] == "-c" || argv[i] == "--compile") { + is_mzn2fzn = true; + } else if (argv[i] == "-v" || argv[i] == "--verbose" || argv[i] == "-l") { + flag_verbose = true; + flag_compiler_verbose = true; + } else if (argv[i] == "--verbose-compilation") { + flag_compiler_verbose = true; + } else if (argv[i] == "-s" || argv[i] == "--statistics") { + flag_statistics = true; + flag_compiler_statistics = true; + } else if (argv[i] == "--compiler-statistics") { + flag_compiler_statistics = true; + } else { + if ((argv[i] == "--fzn-cmd" || argv[i] == "--flatzinc-cmd") && solver.empty()) { + solver = "org.minizinc.mzn-fzn"; + } + if (argv[i] == "--compile-solution-checker") { + compileSolutionChecker = true; + } + if (argv[i] == "--ozn-file") { + flag_is_solns2out = true; + } + argv[j++] = argv[i]; + } + } + argv.resize(j); + argc = j; + + if ((mzn2fzn_exe || compileSolutionChecker) && solver.empty()) { + solver = "org.minizinc.mzn-fzn"; + } + + // if (flag_verbose) { + // argv.push_back("--verbose-solving"); + // argc++; + // } + if (flag_statistics) { + argv.push_back("--solver-statistics"); + argc++; + } + + // flt.set_flag_output_by_default(ifMzn2Fzn()); + + bool isMznMzn = false; + + if (!flag_is_solns2out) { + try { + const SolverConfig& sc = solver_configs.config(solver); + string solverId; + if (sc.executable().empty()) { + if (is_mzn2fzn) { + solverId = "org.minizinc.mzn-fzn"; + } else { + solverId = sc.id(); + } + } else if (sc.supportsMzn()) { + solverId = "org.minizinc.mzn-mzn"; + } else if (sc.supportsFzn()) { + solverId = "org.minizinc.mzn-fzn"; + } else if (sc.supportsNL()) { + solverId = "org.minizinc.mzn-nl"; + } else { + log << "Selected solver does not support MiniZinc, FlatZinc or NL input." << endl; + return OPTION_ERROR; + } + for (auto it = getGlobalSolverRegistry()->getSolverFactories().begin(); + it != getGlobalSolverRegistry()->getSolverFactories().end(); ++it) { + if ((*it)->getId() == + solverId) { /// TODO: also check version (currently assumes all ids are unique) + sf = *it; + if (si_opt) { + delete si_opt; + } + si_opt = sf->createOptions(); + if (!sc.executable().empty() || solverId == "org.minizinc.mzn-fzn" || + solverId == "org.minizinc.mzn-nl") { + std::vector acceptedFlags; + for (auto& sf : sc.stdFlags()) acceptedFlags.push_back(MZNFZNSolverFlag::std(sf)); + for (auto& ef : sc.extraFlags()) + acceptedFlags.push_back(MZNFZNSolverFlag::extra(ef.flag, ef.flag_type)); + + // Collect arguments required for underlying exe + vector fzn_mzn_flags; + if (sc.needsStdlibDir()) { + fzn_mzn_flags.push_back("--stdlib-dir"); + fzn_mzn_flags.push_back(FileUtils::share_directory()); + } + if (sc.needsMznExecutable()) { + fzn_mzn_flags.push_back("--minizinc-exe"); + fzn_mzn_flags.push_back(FileUtils::progpath() + "/" + executable_name); + } + + if (sc.supportsMzn()) { + isMznMzn = true; + static_cast(sf)->setAcceptedFlags(si_opt, acceptedFlags); + std::vector additionalArgs_s; + additionalArgs_s.push_back("-m"); + if (sc.executable_resolved().size()) { + additionalArgs_s.push_back(sc.executable_resolved()); + } else { + additionalArgs_s.push_back(sc.executable()); + } + + if (!fzn_mzn_flags.empty()) { + addFlags("--mzn-flag", fzn_mzn_flags, additionalArgs_s); + } + + // This should maybe be moved to fill in fzn_mzn_flags when + // --find-muses is implemented (these arguments will be passed + // through to the subsolver of findMUS) + if (!sc.mznlib().empty()) { + if (sc.mznlib().substr(0, 2) == "-G") { + additionalArgs_s.push_back("--mzn-flag"); + additionalArgs_s.push_back(sc.mznlib()); + } else { + additionalArgs_s.push_back("--mzn-flag"); + additionalArgs_s.push_back("-I"); + additionalArgs_s.push_back("--mzn-flag"); + std::string _mznlib; + if (sc.mznlib_resolved().size()) { + _mznlib = sc.mznlib_resolved(); + } else { + _mznlib = sc.mznlib(); + } + additionalArgs_s.push_back(_mznlib); + } + } + + for (i = 0; i < additionalArgs_s.size(); ++i) { + bool success = sf->processOption(si_opt, i, additionalArgs_s); + if (!success) { + log << "Solver backend " << solverId << " does not recognise option " + << additionalArgs_s[i] << "." << endl; + return OPTION_ERROR; + } + } + } else { + // supports fzn or nl + std::vector additionalArgs; + if (sc.supportsFzn()) { + static_cast(sf)->setAcceptedFlags(si_opt, acceptedFlags); + additionalArgs.push_back("--fzn-cmd"); + } else { + // supports nl + additionalArgs.push_back("--nl-cmd"); + } + if (sc.executable_resolved().size()) { + additionalArgs.push_back(sc.executable_resolved()); + } else { + additionalArgs.push_back(sc.executable()); + } + if (!fzn_mzn_flags.empty()) { + if (sc.supportsFzn()) { + addFlags("--fzn-flag", fzn_mzn_flags, additionalArgs); + } else { + addFlags("--nl-flag", fzn_mzn_flags, additionalArgs); + } + } + if (sc.needsPathsFile()) { + // Instruct flattener to hold onto paths + int i = 0; + vector args{"--keep-paths"}; + // flt.processOption(i, args); + + // Instruct FznSolverInstance to write a path file + // and pass it to the executable with --paths arg + additionalArgs.push_back("--fzn-needs-paths"); + } + if (!sc.needsSolns2Out()) { + additionalArgs.push_back("--fzn-output-passthrough"); + } + int i = 0; + for (i = 0; i < additionalArgs.size(); ++i) { + bool success = sf->processOption(si_opt, i, additionalArgs); + if (!success) { + log << "Solver backend " << solverId << " does not recognise option " + << additionalArgs[i] << "." << endl; + return OPTION_ERROR; + } + } + } + } + if (!sc.mznlib().empty()) { + if (sc.mznlib().substr(0, 2) == "-G") { + std::vector additionalArgs({sc.mznlib()}); + int i = 0; + // if (!flt.processOption(i, additionalArgs)) { + // log << "Flattener does not recognise option " << sc.mznlib() << endl; + // return OPTION_ERROR; + // } + } else { + std::vector additionalArgs(2); + additionalArgs[0] = "-I"; + if (sc.mznlib_resolved().size()) { + additionalArgs[1] = sc.mznlib_resolved(); + } else { + additionalArgs[1] = sc.mznlib(); + } + int i = 0; + // if (!flt.processOption(i, additionalArgs)) { + // log << "Flattener does not recognise option -I." << endl; + // return OPTION_ERROR; + // } + } + } + if (!sc.defaultFlags().empty()) { + std::vector addedArgs; + addedArgs.push_back(argv[0]); // excutable name + for (auto& df : sc.defaultFlags()) { + addedArgs.push_back(df); + } + for (int i = 1; i < argv.size(); i++) { + addedArgs.push_back(argv[i]); + } + argv = addedArgs; + argc = addedArgs.size(); + } + break; + } + } + + } catch (ConfigException& e) { + log << "Config exception: " << e.msg() << endl; + return OPTION_ERROR; + } + + if (sf == NULL) { + log << "Solver " << solver << " not found." << endl; + return OPTION_ERROR; + } + + for (i = 1; i < argc; ++i) { + if (!ifMzn2Fzn() ? s2out.processOption(i, argv) : false) { + // } else if ((!isMznMzn || is_mzn2fzn) && flt.processOption(i, argv)) { + } else if (sf != NULL && sf->processOption(si_opt, i, argv)) { + } else { + size_t last_dot = argv[i].find_last_of('.'); + if (last_dot != string::npos) { + std::string extension = argv[i].substr(last_dot, string::npos); + if (extension == ".uzn" || extension == ".mza") { + assert(file == ""); + file = argv[i]; + } else if (extension == ".dzn") { + data_files.push_back(argv[i]); + } + } else { + std::string executable_name(argv[0]); + executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); + log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'" + << endl; + return OPTION_ERROR; + } + } + } + return OPTION_OK; + + } else { + for (i = 1; i < argc; ++i) { + if (s2out.processOption(i, argv)) { + } else { + std::string executable_name(argv[0]); + executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); + log << executable_name << ": Unrecognized option or bad format `" << argv[i] << "'" << endl; + return OPTION_ERROR; + } + } + return OPTION_OK; + } +} + +Val MznSolver::eval_val(EnvI& env, Expression* e) { + if (e->type().dim() > 0) { + ArrayLit* al = eval_array_lit(env, e); + std: + vector content(al->size()); + for (size_t i = 0; i < al->size(); ++i) { + content[i] = eval_val(env, (*al)[i]); + } + Val ret = Val(Vec::a(interpreter, interpreter->newIdent(), content)); + + if (al->dims() > 1 || al->min(0) != 1) { + std::vector idxs(al->dims() * 2); + for (size_t i = 0; i < al->dims(); ++i) { + idxs[i * 2] = Val(al->min(i)); + idxs[i * 2 + 1] = Val(al->max(i)); + } + Vec* ranges = Vec::a(interpreter, interpreter->newIdent(), idxs); + ret = Val(Vec::a(interpreter, interpreter->newIdent(), {ret, Val(ranges)}, true)); + } + + return ret; + } + if (e->type().is_set()) { + // TODO: Might not be int + IntSetVal* sl = eval_intset(env, e); + std::vector vranges(sl->size() * 2); + for (size_t i = 0; i < sl->size(); ++i) { + vranges[i * 2] = Val::fromIntVal(sl->min(i)); + vranges[i * 2 + 1] = Val::fromIntVal(sl->max(i)); + } + return Val(Vec::a(interpreter, interpreter->newIdent(), vranges)); + } + if (e->type().isbool()) { + bool b(eval_bool(env, e)); + return Val(b); + } + if (e->type().isfloat()) { + throw InternalError("Unsupported Type"); + } + IntVal iv(eval_int(env, e)); + return Val::fromIntVal(iv); +} + +void MznSolver::flatten(const std::string& filename, const std::string& modelName) { + if (!FileUtils::file_exists(filename)) { + std::cerr << "Error: cannot open assembly file '" << filename << "'." << std::endl; + return; + } + bool verbose = flag_compiler_verbose; + std::ifstream t(filename, std::ifstream::in); + std::string line; + std::string mzn_defs; + while (std::getline(t, line)) { + if (line == "@@@@@@@@@@") { + break; + } + mzn_defs += line + "\n"; + } + std::string assembly; + while (std::getline(t, line)) { + assembly += line + "\n"; + } + if (assembly.empty()) { + std::swap(mzn_defs, assembly); + } + // Parse assembly file + int max_glob; + std::tie(max_glob, bs) = parse_mza(assembly); + if (verbose) { + std::cerr << "Disassembled code:\n"; + int b_count = 0; + for (auto& b : bs) { + for (int i = 0; i < BytecodeProc::MAX_MODE; i++) { + if (b.mode[i].size() > 0) { + std::cerr << ":" << b.name << ":" << BytecodeProc::mode_to_string[i] << " %% " << b_count + << "\n"; + std::cerr << b.mode[i].toString(bs); + } + } + b_count++; + } + std::cerr << "\n"; + } + for (size_t i = 0; i < bs.size(); i++) { + resolve_call.insert({bs[i].name, {i, bs[i].nargs}}); + } + // The main procedure is the last one in the file + BytecodeFrame frame(bs.back().mode[BytecodeProc::ROOT], bs.size() - 1, BytecodeProc::ROOT); + interpreter = new Interpreter(bs, frame, max_glob); + // Parse and add data + if (!mzn_defs.empty()) { + GCLock lock; + std::vector syntaxErrors; + Model* m = parse(in_out_defs, {}, data_files, mzn_defs, file, + {solver_configs.mznlibDir() + "/std"}, false, false, verbose, std::cerr); + if (!m) { + throw Error("Unable to parse MiniZinc Declarations"); + } + assert(!in_out_defs.model()); + in_out_defs.model(m); + long long int idn = 0; + std::vector typeErrors; + typecheck(in_out_defs, in_out_defs.model(), typeErrors, false, true, false); + registerBuiltins(in_out_defs); + if (typeErrors.size() > 0) { + for (unsigned int i = 0; i < typeErrors.size(); i++) { + if (flag_verbose) log << std::endl; + log << typeErrors[i].loc() << ":" << std::endl; + log << typeErrors[i].what() << ": " << typeErrors[i].msg() << std::endl; + } + throw Error("multiple type errors"); + } + if (verbose) { + std::cerr << "Input Data:\n"; + } + for (VarDeclIterator it = in_out_defs.model()->begin_vardecls(); + it != in_out_defs.model()->end_vardecls(); ++it) { + if (it->removed()) { + continue; + } + GCLock lock; + Env& env = in_out_defs; + Model* m = in_out_defs.model(); + + VarDecl* vd = it->e(); + if (vd->type().isann()) { + continue; + } + assert(vd->e()); + Call* global_ann = vd->ann().getCall(constants().ann.global_register); + if (!global_ann) { + throw TypeError(env.envi(), vd->loc(), "Unkown global " + vd->id()->str().str()); + } + IntVal global = eval_int(env.envi(), global_ann->arg(0)); + + if (vd->type().dim() > 0) { + ArrayLit* al = eval_array_lit(env.envi(), vd->e()); + checkIndexSets(env.envi(), vd, al); + } + Val v = eval_val(env.envi(), vd->e()); + + interpreter->globals.assign(interpreter, global.toInt(), v); + if (verbose) { + std::cerr << " - R" << global << "(" << vd->id()->str() << ") = " << v.toString() + << std::endl; + } + } + } + // Start interpreter + Timer tm01; + if (verbose) { + std::cerr << "Run:\n"; + } + bool delayed = true; + interpreter->run(); + while (interpreter->status() == Interpreter::ROGER && delayed) { + delayed = interpreter->runDelayed(); + } + flatten_time = tm01.s(); + // FIXME: Global registers should not be removed, merely hidden + // interpreter->clear_globals(); + if (verbose) { + std::cerr << "Status: " << Interpreter::status_to_string[interpreter->status()] << std::endl; + if (interpreter->status() == Interpreter::ROGER) { + interpreter->dumpState(std::cerr); + } + std::cerr << "----------------" << std::endl; + } + switch (interpreter->status()) { + case Interpreter::ROGER: + interpreter_status = SolverInstance::UNKNOWN; + break; + case Interpreter::ERROR: + case Interpreter::ABORTED: + interpreter_status = SolverInstance::ERROR; + break; + case Interpreter::INCONSISTENT: + interpreter_status = SolverInstance::UNSAT; + break; + } +} + +std::pair MznSolver::solve() { + SolverInstance::Status status = getSI()->solve(); + std::string sol = printSolution(status); + if (si_opt->printStatistics) getSI()->printStatistics(); + /* if (flag_statistics) */ + /* getSI()->getSolns2Out()->printStatistics(log); */ + return {status, sol}; +} + +void MznSolver::printStatistics() { // from flattener too? TODO + if (si) getSI()->printStatistics(); +} + +std::string MznSolver::printSolution(SolverInstance::Status s) { + std::stringstream ss; + switch (s) { + case SolverInstance::SAT: + case SolverInstance::OPT: { + if (output) { + Val vec = output->arg(0); + ss << (output_dict ? '{' : '['); + for (int i = 0; i < vec.size(); ++i) { + Val v = vec[i]; + if (i > 0) { + ss << ", "; + } + if (v.isVar()) { + if (output_dict) { + ss << "\"" << v.timestamp() << "\"" + << ": "; + } + v = Val::follow_alias(v); + if (v.isVar()) { + ss << si->getSolutionValue(v.toVar()).toString(); + } else { + ss << v.toString(); + } + } else { + assert(!output_dict); + ss << v.toString(); + } + } + ss << (output_dict ? '}' : ']') << std::endl; + + // Set output for sol() builtin + interpreter->solutions.clear(); + for (int i = 0; i < vec.size(); ++i) { + Val v = Val::follow_alias(vec[i]); + if (v.isVar()) { + interpreter->solutions.emplace(v.timestamp(), si->getSolutionValue(v.toVar())); + } + } + } else { + interpreter->solutions.clear(); + bool first = true; + ss << "{" << std::endl; + for (Variable* v = interpreter->root()->next(); v != interpreter->root(); v = v->next()) { + Val va = Val::follow_alias(Val(v)); + if (va.isVar()) { + int timestamp = va.timestamp(); + if (timestamp >= 0) { + /// TODO: all timestamps >= 0 ? + if (!first) { + ss << "," << std::endl; + } + ss << " \"" << timestamp << "\"" + << ": "; + Val sv = si->getSolutionValue(va.toVar()); + ss << sv.toString(); + first = false; + // Set output for sol() builtin + interpreter->solutions.emplace(timestamp, sv); + } + } + } + ss << std::endl << "}" << std::endl; + } + } break; + case SolverInstance::UNSAT: + ss << "=====UNSATISFIABLE=====" << std::endl; + break; + case SolverInstance::UNKNOWN: + ss << "=====UNKNOWN=====" << std::endl; + break; + case SolverInstance::ERROR: + default: + ss << "=====ERROR=====" << std::endl; + break; + } + return ss.str(); +} + +std::pair MznSolver::run() { + using namespace std::chrono; + steady_clock::time_point startTime = steady_clock::now(); + + if (!ifMzn2Fzn() && flag_overall_time_limit != 0) { + steady_clock::time_point afterFlattening = steady_clock::now(); + milliseconds passed = duration_cast(afterFlattening - startTime); + milliseconds time_limit(flag_overall_time_limit); + if (passed > time_limit) { + s2out.evalStatus(getFltStatus()); + return {SolverInstance::UNKNOWN, ""}; + } + int time_left = (time_limit - passed).count(); + std::vector timeoutArgs(2); + timeoutArgs[0] = "--solver-time-limit"; + std::ostringstream oss; + oss << time_left; + timeoutArgs[1] = oss.str(); + int i = 0; + sf->processOption(si_opt, i, timeoutArgs); + } + + if (flag_statistics) { + os << "%%%mzn-stat: flatTime=" << flatten_time << endl; + } + + if (getFltStatus() != SolverInstance::UNKNOWN) { + if (ifMzn2Fzn()) { + GCLock lock; + std::ofstream os(file.substr(0, file.size() - 4) + std::string(".fzn")); + Printer p(os, 0, true); + auto c = new Call(Location().introduce(), constants().ids.bool_eq, + {constants().lit_true, constants().lit_false}); + auto ci = new ConstraintI(Location().introduce(), c); + p.print(ci); + p.print(SolveI::sat(Location().introduce())); + } + return {getFltStatus(), printSolution(getFltStatus())}; + } + + if (!si) { // only then + // GCLock lock; // better locally, to enable cleanup after ProcessFlt() + addSolverInterface(); + } + addDefinitions(); + if (ifMzn2Fzn()) { + assert(dynamic_cast(si)); + // Print flatzinc to file + static_cast(si)->printFlatZincToFile(file.substr(0, file.size() - 4) + + std::string(".fzn")); + static_cast(si)->printOutputToFile(file.substr(0, file.size() - 4) + + std::string(".ozn")); + + if (flag_statistics) { + si->printStatistics(); + } + return {SolverInstance::UNKNOWN, ""}; + } + return solve(); +} + +void MznSolver::addDefinitions() { + /// TODO: currently this will always add all variables and constraints + Variable* v_start; + size_t c_start; + std::tie(v_start, c_start) = def_stack.back(); + std::set output_vars; + Variable* root = interpreter->root(); + + auto fzn = dynamic_cast(si); + if (v_start == nullptr) { + v_start = root; + for (Constraint* c : v_start->definitions()) { + if (interpreter->_procs[c->pred()].name == "output_this") { + assert(c->size() == 1); + output = c; + Val arg = c->arg(0); + assert(arg.isVec()); + for (int i = 0; i < arg.size(); ++i) { + Val real = Val::follow_alias(Val(arg[i])); + if (real.isVar()) { + output_vars.insert(real.toVar()->timestamp()); + } + } + break; + } + } + } + + // Add all new variables + for (Variable* v = v_start->next(); v != interpreter->root(); v = v->next()) { + // Only add variables that are not aliased + if (Val(v) == Val::follow_alias(Val(v))) { + si->addVariable(v, + output != nullptr || output_vars.find(v->timestamp()) != output_vars.end()); + } + } + // Add defining constraints + for (Variable* v = v_start->next(); v != interpreter->root(); v = v->next()) { + for (Constraint* c : v->definitions()) { + si->addConstraint(interpreter->_procs, c); + } + } + // Add new root level constraints + for (size_t i = c_start; i < root->definitions().size(); ++i) { + Constraint* c = root->definitions()[i]; + if (interpreter->_procs[c->pred()].name == "output_this") { + if (fzn) { + assert(c->size() == 1); + fzn->outputArray(c->arg(0).toVec()); + } + continue; + } else { + si->addConstraint(interpreter->_procs, c); + } + } + // Force output statement + if (output == nullptr && fzn != nullptr) { + fzn->outputDict(interpreter->root()); + } + // TODO: Domain Changes + def_stack[def_stack.size() - 1] = {root->prev(), root->definitions().size()}; +} + +void MznSolver::pushToSolver() { + assert(interpreter->trail.len() > 0); + + if (auto tsi = dynamic_cast(si)) { + assert(interpreter->trail.len() == tsi->states() + 1); + tsi->restart(); + addDefinitions(); + def_stack.push_back(def_stack.back()); + tsi->pushState(); + } else { + assert(false); + } +} + +void MznSolver::popFromSolver() { + if (auto tsi = dynamic_cast(si)) { + assert(interpreter->trail.len() == tsi->states() - 1); + tsi->restart(); + tsi->popState(); + def_stack.pop_back(); + } else { + assert(false); + delete si; + si = nullptr; + // TODO: do we need to reconstruct the model here or can we trust there is a push before + // solving + } +} diff --git a/lib/solver_config.cpp b/lib/solver_config.cpp new file mode 100644 index 0000000..c2c2b91 --- /dev/null +++ b/lib/solver_config.cpp @@ -0,0 +1,758 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace MiniZinc { + +namespace { +std::string getString(AssignI* ai) { + if (StringLit* sl = ai->e()->dyn_cast()) { + return sl->v().str(); + } + throw ConfigException("invalid configuration item (right hand side must be string)"); +} +bool getBool(AssignI* ai) { + if (BoolLit* bl = ai->e()->dyn_cast()) { + return bl->v(); + } + throw ConfigException("invalid configuration item (right hand side must be bool)"); +} +int getInt(AssignI* ai) { + if (IntLit* il = ai->e()->dyn_cast()) { + return static_cast(il->v().toInt()); + } + throw ConfigException("invalid configuration item (right hand side must be int)"); +} +std::vector getStringList(AssignI* ai) { + if (ArrayLit* al = ai->e()->dyn_cast()) { + std::vector ret; + for (unsigned int i = 0; i < al->size(); i++) { + if (StringLit* sl = (*al)[i]->dyn_cast()) { + ret.push_back(sl->v().str()); + } else { + throw ConfigException( + "invalid configuration item (right hand side must be a list of strings)"); + } + } + return ret; + } + throw ConfigException("invalid configuration item (right hand side must be a list of strings)"); +} +std::vector > getStringPairList(AssignI* ai) { + if (ArrayLit* al = ai->e()->dyn_cast()) { + std::vector > ret; + if (al->dims() != 2 || al->min(1) != 1 || al->max(1) != 2) { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); + } + for (unsigned int i = 0; i < al->size(); i += 2) { + StringLit* sl1 = (*al)[i]->dyn_cast(); + StringLit* sl2 = (*al)[i + 1]->dyn_cast(); + if (sl1 && sl2) { + ret.push_back(std::make_pair(sl1->v().str(), sl2->v().str())); + } else { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); + } + } + return ret; + } + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); +} +std::vector > getDefaultOptionList(AssignI* ai) { + if (ArrayLit* al = ai->e()->dyn_cast()) { + std::vector > ret; + if (al->size() == 0) return ret; + if (al->dims() != 2) { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); + } + int nCols = al->max(1) - al->min(1) + 1; + if (nCols != 3) { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings with 3 " + "columns)"); + } + for (unsigned int i = 0; i < al->size(); i += nCols) { + StringLit* sl0 = (*al)[i]->dyn_cast(); + StringLit* sl1 = (*al)[i + 1]->dyn_cast(); + StringLit* sl2 = (*al)[i + 2]->dyn_cast(); + if (sl0 && sl1 && sl2) { + ret.push_back(std::vector({sl0->v().str(), sl1->v().str(), sl2->v().str()})); + } else { + throw ConfigException( + "invalid configuration item (right hand side must be a list of strings)"); + } + } + return ret; + } + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); +} +std::vector getExtraFlagList(AssignI* ai) { + if (ArrayLit* al = ai->e()->dyn_cast()) { + std::vector ret; + if (al->size() == 0) return ret; + if (al->dims() != 2) { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); + } + int nCols = al->max(1) - al->min(1) + 1; + if (nCols < 2 || nCols > 4) { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); + } + bool haveType = (nCols >= 3); + bool haveDefault = (nCols >= 4); + for (unsigned int i = 0; i < al->size(); i += nCols) { + StringLit* sl1 = (*al)[i]->dyn_cast(); + StringLit* sl2 = (*al)[i + 1]->dyn_cast(); + StringLit* sl3 = haveType ? (*al)[i + 2]->dyn_cast() : NULL; + StringLit* sl4 = haveDefault ? (*al)[i + 3]->dyn_cast() : NULL; + std::string opt_type = sl3 ? sl3->v().str() : "bool"; + std::string opt_def; + if (sl4) { + opt_def = sl4->v().str(); + } else if (opt_type == "bool") { + opt_def = "false"; + } else if (opt_type == "int") { + opt_def = "0"; + } else if (opt_type == "float") { + opt_def = "0.0"; + } + if (sl1 && sl2) { + ret.emplace_back(sl1->v().str(), sl2->v().str(), opt_type, opt_def); + } else { + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); + } + } + return ret; + } + throw ConfigException( + "invalid configuration item (right hand side must be a 2d array of strings)"); +} + +std::string getEnv(const char* v) { + std::string ret; +#ifdef _MSC_VER + size_t len; + getenv_s(&len, NULL, 0, v); + if (len > 0) { + char* p = static_cast(malloc(len * sizeof(char))); + getenv_s(&len, p, len, v); + if (len > 0) { + ret = p; + } + free(p); + } +#else + char* p = getenv(v); + if (p) { + ret = p; + } +#endif + return ret; +} + +char charToLower(char c) { return std::tolower(static_cast(c)); } +std::string stringToLower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), charToLower); + return s; +} +struct SortByLowercase { + bool operator()(const std::string& n1, const std::string& n2) { + for (size_t i = 0; i < n1.size() && i < n2.size(); i++) { + if (std::tolower(n1[i]) != std::tolower(n2[i])) + return std::tolower(n1[i]) < std::tolower(n2[i]); + } + return n1.size() < n2.size(); + } +}; +struct SortByName { + const std::vector& solvers; + SortByLowercase sortByLowercase; + SortByName(const std::vector& solvers0) : solvers(solvers0) {} + bool operator()(int idx1, int idx2) { + return sortByLowercase(solvers[idx1].name(), solvers[idx2].name()); + } +}; + +} // namespace + +SolverConfig SolverConfig::load(string filename) { + SolverConfig sc; + sc._configFile = FileUtils::file_path(filename); + ostringstream errstream; + try { + Env confenv; + Model* m = NULL; + if (JSONParser::fileIsJSON(filename)) { + JSONParser jp(confenv.envi()); + try { + m = new Model; + GCLock lock; + jp.parse(m, filename); + } catch (JSONError& e) { + delete m; + m = NULL; + throw ConfigException(e.msg()); + } + } else { + vector filenames; + filenames.push_back(filename); + m = parse(confenv, filenames, vector(), "", "", vector(), true, false, false, + errstream); + } + if (m) { + bool hadId = false; + bool hadVersion = false; + bool hadName = false; + string basePath = FileUtils::dir_name(sc._configFile); + for (unsigned int i = 0; i < m->size(); i++) { + if (AssignI* ai = (*m)[i]->dyn_cast()) { + if (ai->id() == "id") { + sc._id = getString(ai); + hadId = true; + } else if (ai->id() == "name") { + sc._name = getString(ai); + hadName = true; + } else if (ai->id() == "executable") { + std::string exePath = getString(ai); + sc._executable = exePath; + std::string exe = FileUtils::find_executable(FileUtils::file_path(exePath, basePath)); + int nr_found = (int)(!exe.empty()); + std::string tmp = FileUtils::file_path(FileUtils::find_executable(exePath)); + nr_found += (int)((!tmp.empty()) && tmp != exe); + exe = exe.empty() ? tmp : exe; + if (nr_found > 0) { + sc._executable_resolved = exe; + if (nr_found > 1) { + std::cerr << "Warning: multiple executables '" << exePath + << "' found on the system, using '" << exe << "'" << std::endl; + } + } + } else if (ai->id() == "mznlib") { + std::string libPath = getString(ai); + sc._mznlib = libPath; + if (!libPath.empty()) { + if (libPath[0] == '-') { + sc._mznlib_resolved = libPath; + } else if (libPath.size() > 2 && libPath[0] == '.' && + (libPath[1] == '/' || (libPath[1] == '.' && libPath[2] == '/'))) { + sc._mznlib_resolved = FileUtils::file_path(libPath, basePath); + } else { + sc._mznlib_resolved = FileUtils::file_path(libPath, basePath); + } + } + } else if (ai->id() == "version") { + sc._version = getString(ai); + hadVersion = true; + } else if (ai->id() == "mznlibVersion") { + sc._mznlibVersion = getInt(ai); + } else if (ai->id() == "description") { + sc._description = getString(ai); + } else if (ai->id() == "contact") { + sc._contact = getString(ai); + } else if (ai->id() == "website") { + sc._website = getString(ai); + } else if (ai->id() == "supportsMzn") { + sc._supportsMzn = getBool(ai); + } else if (ai->id() == "supportsFzn") { + sc._supportsFzn = getBool(ai); + } else if (ai->id() == "supportsNL") { + sc._supportsNL = getBool(ai); + } else if (ai->id() == "needsSolns2Out") { + sc._needsSolns2Out = getBool(ai); + } else if (ai->id() == "isGUIApplication") { + sc._isGUIApplication = getBool(ai); + } else if (ai->id() == "needsMznExecutable") { + sc._needsMznExecutable = getBool(ai); + } else if (ai->id() == "needsStdlibDir") { + sc._needsStdlibDir = getBool(ai); + } else if (ai->id() == "needsPathsFile") { + sc._needsPathsFile = getBool(ai); + } else if (ai->id() == "tags") { + sc._tags = getStringList(ai); + } else if (ai->id() == "stdFlags") { + sc._stdFlags = getStringList(ai); + } else if (ai->id() == "requiredFlags") { + sc._requiredFlags = getStringList(ai); + } else if (ai->id() == "extraFlags") { + sc._extraFlags = getExtraFlagList(ai); + } else { + throw ConfigException("invalid configuration item (" + ai->id().str() + ")"); + } + } else { + throw ConfigException("invalid configuration item"); + } + } + if (!hadId) { + throw ConfigException("invalid solver configuration (missing id)"); + } + if (!hadVersion) { + throw ConfigException("invalid solver configuration (missing version)"); + } + if (!hadName) { + throw ConfigException("invalid solver configuration (missing name)"); + } + } else { + throw ConfigException(errstream.str()); + } + } catch (ConfigException&) { + throw; + } catch (Exception& e) { + throw ConfigException(e.what()); + } + + return sc; +} + +class BuiltinSolverConfigs { +public: + std::unordered_map builtinSolvers; +}; + +BuiltinSolverConfigs& builtinSolverConfigs(void) { + static BuiltinSolverConfigs c; + return c; +} + +void SolverConfigs::addConfig(const MiniZinc::SolverConfig& sc) { + int newIdx = static_cast(_solvers.size()); + _solvers.push_back(sc); + std::vector sc_tags = sc.tags(); + std::string id = sc.id(); + id = stringToLower(id); + sc_tags.push_back(id); + std::string name = sc.name(); + name = stringToLower(name); + sc_tags.push_back(name); + for (auto t : sc_tags) { + TagMap::iterator it = _tags.find(t); + if (it == _tags.end()) { + _tags.insert(std::make_pair(t, std::vector({newIdx}))); + } else { + it->second.push_back(newIdx); + } + } +} + +std::vector SolverConfigs::solverConfigsPath(void) const { return _solver_path; } + +SolverConfigs::SolverConfigs(std::ostream& log) { +#ifdef _MSC_VER + const char* PATHSEP = ";"; +#else + const char* PATHSEP = ":"; +#endif + for (auto sc : builtinSolverConfigs().builtinSolvers) { + addConfig(sc.second); + } + std::string mzn_solver_path = getEnv("MZN_SOLVER_PATH"); + while (!mzn_solver_path.empty()) { + size_t next_sep = mzn_solver_path.find(PATHSEP); + string cur_path = mzn_solver_path.substr(0, next_sep); + _solver_path.push_back(cur_path); + if (next_sep != string::npos) + mzn_solver_path = mzn_solver_path.substr(next_sep + 1, string::npos); + else + mzn_solver_path = ""; + } + std::string userConfigDir = FileUtils::user_config_dir(); + if (FileUtils::directory_exists(userConfigDir + "/solvers")) { + _solver_path.push_back(userConfigDir + "/solvers"); + } + std::vector configFiles( + {FileUtils::global_config_file(), FileUtils::user_config_file()}); + + for (auto& cf : configFiles) { + if (!cf.empty() && FileUtils::file_exists(cf)) { + ostringstream errstream; + try { + Env userconfenv; + Model* m = NULL; + if (JSONParser::fileIsJSON(cf)) { + JSONParser jp(userconfenv.envi()); + try { + m = new Model; + GCLock lock; + jp.parse(m, cf); + } catch (JSONError&) { + delete m; + m = NULL; + } + } + if (m) { + for (unsigned int i = 0; i < m->size(); i++) { + if (AssignI* ai = (*m)[i]->dyn_cast()) { + if (ai->id() == "mzn_solver_path") { + std::vector sp = getStringList(ai); + for (auto s : sp) { + _solver_path.push_back(s); + } + } else if (ai->id() == "mzn_lib_dir") { + _mznlibDir = getString(ai); + } else if (ai->id() == "tagDefaults") { + std::vector > tagDefs = getStringPairList(ai); + for (auto& td : tagDefs) { + std::string tag = td.first; + std::string solver_id = td.second; + _tagDefault[tag] = solver_id; + } + } else if (ai->id() == "solverDefaults") { + std::vector > solverDefs = getDefaultOptionList(ai); + for (auto& sd : solverDefs) { + assert(sd.size() == 3); + std::string solver = sd[0]; + SolverDefaultMap::iterator it = _solverDefaultOptions.find(solver); + if (it == _solverDefaultOptions.end()) { + std::vector solverOptions({sd[1], sd[2]}); + _solverDefaultOptions.insert(std::make_pair(solver, solverOptions)); + } else { + std::vector& opts = it->second; + bool found = false; + for (unsigned int i = 0; i < opts.size(); i += 2) { + if (opts[i] == sd[1]) { + // Override existing option value + opts[i + 1] = sd[2]; + found = true; + break; + } + } + if (!found) { + // Option didn't exist, add to end of list + opts.push_back(sd[1]); + opts.push_back(sd[2]); + } + } + } + } else { + throw ConfigException("invalid configuration item"); + } + } else { + throw ConfigException("invalid configuration item"); + } + } + } else { + std::cerr << errstream.str(); + throw ConfigException("internal error"); + } + } catch (ConfigException& e) { + log << "Warning: invalid configuration file: " << e.msg() << "\n"; + } catch (Exception& e) { + log << "Warning: invalid configuration file: " << e.what() << "\n"; + } + } + } + + if (_mznlibDir.empty()) { + _mznlibDir = FileUtils::file_path(FileUtils::share_directory()); + } + if (!_mznlibDir.empty()) { + _solver_path.push_back(_mznlibDir + "/solvers"); + } +#ifndef _MSC_VER + if (_mznlibDir != "/usr/local/share/minizinc" && + FileUtils::directory_exists("/usr/local/share")) { + _solver_path.push_back("/usr/local/share/minizinc/solvers"); + } + if (_mznlibDir != "/usr/share/minizinc" && FileUtils::directory_exists("/usr/share")) { + _solver_path.push_back("/usr/share/minizinc/solvers"); + } +#endif + for (const string& cur_path : _solver_path) { + std::vector configFiles = FileUtils::directory_list(cur_path, "msc"); + for (unsigned int i = 0; i < configFiles.size(); i++) { + try { + SolverConfig sc = SolverConfig::load(cur_path + "/" + configFiles[i]); + addConfig(sc); + } catch (ConfigException& e) { + log << "Warning: error loading solver configuration from file " << cur_path << "/" + << configFiles[i] << "\n"; + log << "Error was:\n" << e.msg() << "\n"; + } + } + } + + // Add default options to loaded solver configurations + for (auto& sc : _solvers) { + SolverDefaultMap::const_iterator it = _solverDefaultOptions.find(sc.id()); + if (it != _solverDefaultOptions.end()) { + std::vector defaultOptions; + for (auto& df : it->second) { + if (!df.empty()) { + defaultOptions.push_back(df); + } + } + sc.defaultFlags(defaultOptions); + } + } +} + +vector SolverConfigs::solvers() const { + // Find default solver, if present + std::string def_id; + DefaultMap::const_iterator def_it = _tagDefault.find(""); + if (def_it != _tagDefault.end()) { + def_id = def_it->second; + } + // Create sorted list of solvers + vector s; + for (auto& sc : _solvers) { + if (std::find(sc.tags().begin(), sc.tags().end(), "__internal__") != sc.tags().end()) continue; + std::ostringstream oss; + oss << sc.name() << " " << sc.version() << " (" << sc.id(); + if (!def_id.empty() && sc.id() == def_id) { + oss << ", default solver"; + } + for (std::string t : sc.tags()) { + oss << ", " << t; + } + oss << ")"; + s.push_back(oss.str()); + } + SortByLowercase sortByLowercase; + std::sort(s.begin(), s.end(), sortByLowercase); + return s; +} + +std::string SolverConfigs::solverConfigsJSON() const { + GCLock lock; + std::ostringstream oss; + + // Find default solver, if present + std::string def_id; + DefaultMap::const_iterator def_it = _tagDefault.find(""); + if (def_it != _tagDefault.end()) { + def_id = def_it->second; + } + + SortByName sortByName(_solvers); + std::vector solversIdx(_solvers.size()); + for (unsigned int i = 0; i < solversIdx.size(); i++) { + solversIdx[i] = i; + } + std::sort(solversIdx.begin(), solversIdx.end(), sortByName); + + bool hadSolver = false; + oss << "[\n"; + for (unsigned int i = 0; i < _solvers.size(); i++) { + const SolverConfig& sc = _solvers[solversIdx[i]]; + if (std::find(sc.tags().begin(), sc.tags().end(), "__internal__") != sc.tags().end()) continue; + if (hadSolver) { + oss << ",\n"; + } + hadSolver = true; + oss << " {\n"; + oss << " \"extraInfo\": {\n"; + if (!def_id.empty() && def_id == sc.id()) { + oss << " \"isDefault\": true,\n"; + } + if (sc.mznlib_resolved().size()) { + oss << " \"mznlib\": \"" << Printer::escapeStringLit(sc.mznlib_resolved()) << "\",\n"; + } + if (sc.executable_resolved().size()) { + oss << " \"executable\": \"" << Printer::escapeStringLit(sc.executable_resolved()) + << "\",\n"; + } + oss << " \"configFile\": \"" << Printer::escapeStringLit(sc.configFile()) << "\""; + if (sc.defaultFlags().size()) { + oss << ",\n \"defaultFlags\": ["; + for (unsigned int j = 0; j < sc.defaultFlags().size(); j++) { + oss << "\"" << Printer::escapeStringLit(sc.defaultFlags()[j]) << "\""; + if (j < sc.defaultFlags().size() - 1) oss << ","; + } + oss << "]"; + } + oss << "\n"; + oss << " },\n"; + oss << " \"id\": \"" << Printer::escapeStringLit(sc.id()) << "\",\n"; + oss << " \"name\": \"" << Printer::escapeStringLit(sc.name()) << "\",\n"; + oss << " \"version\": \"" << Printer::escapeStringLit(sc.version()) << "\",\n"; + if (sc.mznlib().size()) { + oss << " \"mznlib\": \"" << Printer::escapeStringLit(sc.mznlib()) << "\",\n"; + } + if (sc.executable().size()) { + oss << " \"executable\": \"" << Printer::escapeStringLit(sc.executable()) << "\",\n"; + } + oss << " \"mznlibVersion\": " << sc.mznlibVersion() << ",\n"; + if (sc.description().size()) { + oss << " \"description\": \"" << Printer::escapeStringLit(sc.description()) << "\",\n"; + } + if (sc.contact().size()) { + oss << " \"contact\": \"" << Printer::escapeStringLit(sc.contact()) << "\",\n"; + } + if (sc.website().size()) { + oss << " \"website\": \"" << Printer::escapeStringLit(sc.website()) << "\",\n"; + } + if (sc.requiredFlags().size()) { + oss << " \"requiredFlags\": ["; + for (unsigned int j = 0; j < sc.requiredFlags().size(); j++) { + oss << "\"" << sc.requiredFlags()[j] << "\""; + if (j < sc.requiredFlags().size() - 1) oss << ","; + } + oss << "],\n"; + } + if (sc.stdFlags().size()) { + oss << " \"stdFlags\": ["; + for (unsigned int j = 0; j < sc.stdFlags().size(); j++) { + oss << "\"" << sc.stdFlags()[j] << "\""; + if (j < sc.stdFlags().size() - 1) oss << ","; + } + oss << "],\n"; + } + if (sc.extraFlags().size()) { + oss << " \"extraFlags\": ["; + for (unsigned int j = 0; j < sc.extraFlags().size(); j++) { + oss << "[" + << "\"" << sc.extraFlags()[j].flag << "\",\"" << sc.extraFlags()[j].description + << "\",\""; + oss << sc.extraFlags()[j].flag_type << "\",\"" << sc.extraFlags()[j].default_value << "\"]"; + if (j < sc.extraFlags().size() - 1) oss << ","; + } + oss << "],\n"; + } + + if (sc.tags().size()) { + oss << " \"tags\": ["; + for (unsigned int j = 0; j < sc.tags().size(); j++) { + oss << "\"" << Printer::escapeStringLit(sc.tags()[j]) << "\""; + if (j < sc.tags().size() - 1) oss << ","; + } + oss << "],\n"; + } + oss << " \"supportsMzn\": " << (sc.supportsMzn() ? "true" : "false") << ",\n"; + oss << " \"supportsFzn\": " << (sc.supportsFzn() ? "true" : "false") << ",\n"; + oss << " \"supportsNL\": " << (sc.supportsNL() ? "true" : "false") << ",\n"; + oss << " \"needsSolns2Out\": " << (sc.needsSolns2Out() ? "true" : "false") << ",\n"; + oss << " \"needsMznExecutable\": " << (sc.needsMznExecutable() ? "true" : "false") << ",\n"; + oss << " \"needsStdlibDir\": " << (sc.needsStdlibDir() ? "true" : "false") << ",\n"; + oss << " \"needsPathsFile\": " << (sc.needsPathsFile() ? "true" : "false") << ",\n"; + oss << " \"isGUIApplication\": " << (sc.isGUIApplication() ? "true" : "false") << "\n"; + oss << " }"; + } + oss << "\n]\n"; + return oss.str(); +} + +namespace { +std::string getTag(const std::string& t) { return t.substr(0, t.find('@')); } +std::string getVersion(const std::string& t) { + size_t sep = t.find('@'); + return sep == string::npos ? "" : t.substr(sep + 1); +} +} // namespace + +const SolverConfig& SolverConfigs::config(const std::string& _s) { + std::string s; + if (_s.size() > 4 && _s.substr(_s.size() - 4) == ".msc") { + SolverConfig sc = SolverConfig::load(_s); + addConfig(sc); + s = sc.id() + "@" + sc.version(); + } else { + s = _s; + } + std::remove(s.begin(), s.end(), ' '); + s = stringToLower(s); + std::vector tags; + std::istringstream iss(s); + std::string next_s; + while (std::getline(iss, next_s, ',')) { + tags.push_back(next_s); + } + std::set defaultSolvers; + std::set selectedSolvers; + + std::string firstTag; + if (tags.empty()) { + DefaultMap::const_iterator def_it = _tagDefault.find(""); + if (def_it != _tagDefault.end()) { + firstTag = def_it->second; + } else { + throw ConfigException("no solver selected"); + } + } else { + firstTag = tags[0]; + } + TagMap::const_iterator tag_it = _tags.find(getTag(firstTag)); + + if (tag_it == _tags.end()) { + throw ConfigException("no solver with tag " + getTag(firstTag) + " found"); + } + std::string tv = getVersion(firstTag); + for (int sidx : tag_it->second) { + if (tv.empty() || tv == _solvers[sidx].version()) selectedSolvers.insert(sidx); + } + DefaultMap::const_iterator def_it = _tagDefault.find(getTag(firstTag)); + if (def_it != _tagDefault.end()) { + defaultSolvers.insert(def_it->second); + } + for (unsigned int i = 1; i < tags.size(); i++) { + tag_it = _tags.find(getTag(tags[i])); + if (tag_it == _tags.end()) { + throw ConfigException("no solver with tag " + tags[i] + " found"); + } + tv = getVersion(tags[i]); + std::set newSolvers; + for (int sidx : tag_it->second) { + if (tv.empty() || tv == _solvers[sidx].version()) newSolvers.insert(sidx); + } + std::set intersection; + std::set_intersection(selectedSolvers.begin(), selectedSolvers.end(), newSolvers.begin(), + newSolvers.end(), std::inserter(intersection, intersection.begin())); + selectedSolvers = intersection; + if (selectedSolvers.empty()) { + throw ConfigException("no solver with tags " + s + " found"); + } + def_it = _tagDefault.find(getTag(tags[i])); + if (def_it != _tagDefault.end()) { + defaultSolvers.insert(def_it->second); + } + } + int selectedSolver = -1; + if (selectedSolvers.size() > 1) { + // use default information for the tags to select a solver + for (int sc_idx : selectedSolvers) { + if (defaultSolvers.find(_solvers[sc_idx].id()) != defaultSolvers.end()) { + selectedSolver = sc_idx; + break; + } + } + if (selectedSolver == -1) { + selectedSolver = *selectedSolvers.begin(); + } + } else { + selectedSolver = *selectedSolvers.begin(); + } + return _solvers[selectedSolver]; +} + +void SolverConfigs::registerBuiltinSolver(const SolverConfig& sc) { + builtinSolverConfigs().builtinSolvers.insert(make_pair(sc.id(), sc)); +} + +} // namespace MiniZinc diff --git a/lib/solver_instance.cpp b/lib/solver_instance.cpp new file mode 100644 index 0000000..37502de --- /dev/null +++ b/lib/solver_instance.cpp @@ -0,0 +1,14 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +namespace MiniZinc {} diff --git a/lib/solver_instance_base.cpp b/lib/solver_instance_base.cpp new file mode 100644 index 0000000..222399e --- /dev/null +++ b/lib/solver_instance_base.cpp @@ -0,0 +1,184 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#undef ERROR // MICROsoft. +#undef min +#undef max +#endif + +namespace MiniZinc { + +SolverInstanceBase::Status SolverInstanceBase::solve(void) { return SolverInstance__ERROR; } + +void SolverInstanceBase::reset(void) { assert(false); } + +void SolverInstanceBase::resetWithConstraints(Model::iterator begin, Model::iterator end) { + assert(false); +} + +void SolverInstanceBase::processPermanentConstraints(Model::iterator begin, Model::iterator end) { + assert(false); +} + +void Registry::add(const std::string& name, poster p) { _registry.insert(std::make_pair(name, p)); } +void Registry::post(std::string name, Constraint* c) { + std::unordered_map::iterator it = _registry.find(name); + if (it == _registry.end()) { + GCLock lock; + throw InternalError("Error: solver backend cannot handle constraint: " + name + "\n"); + } + it->second(_base, c); +} + +void SolverInstanceBase::printSolution() { + std::ostringstream oss; + + if (_options->printStatistics) printStatistics(1); // Insert stats before sol separator + if (0 == pS2Out) { + getEnv()->evalOutput(std::cout); // deprecated + std::cout << oss.str(); + if (oss.str().size() && '\n' != oss.str().back()) std::cout << '\n'; + std::cout << "----------" << std::endl; + } else + getSolns2Out()->evalOutput(oss.str()); +} + +void SolverInstanceBase2::printSolution() { + GCLock lock; + assignSolutionToOutput(); + SolverInstanceBase::printSolution(); +} + +// void +// SolverInstanceBase::assignSolutionToOutput(void) { +// for (VarDeclIterator it = getEnv()->output()->begin_vardecls(); it != +// getEnv()->output()->end_vardecls(); ++it) { +// if (it->e()->e() == NULL) { +// it->e()->e(getSolutionValue(it->e()->id())); +// } +// } +// } + +void SolverInstanceBase2::assignSolutionToOutput() { + GCLock lock; + + MZN_ASSERT_HARD_MSG( + 0 != pS2Out, "Setup a Solns2Out object to use default solution extraction/reporting procs"); + + if (_varsWithOutput.empty()) { + for (VarDeclIterator it = getEnv()->flat()->begin_vardecls(); + it != getEnv()->flat()->end_vardecls(); ++it) { + if (!it->removed()) { + VarDecl* vd = it->e(); + if (!vd->ann().isEmpty()) { + if (vd->ann().containsCall(constants().ann.output_array.aststr()) || + vd->ann().contains(constants().ann.output_var)) { + _varsWithOutput.push_back(vd); + } + } + } + } + } + + pS2Out->declNewOutput(); // Even for empty output decl + + // iterate over set of ids that have an output annotation && obtain their right hand side from the + // flat model + for (unsigned int i = 0; i < _varsWithOutput.size(); i++) { + VarDecl* vd = _varsWithOutput[i]; + // std::cout << "DEBUG: Looking at var-decl with output-annotation: " << *vd << std::endl; + if (Call* output_array_ann = Expression::dyn_cast( + getAnnotation(vd->ann(), constants().ann.output_array.aststr()))) { + assert(vd->e()); + + if (ArrayLit* al = vd->e()->dyn_cast()) { + std::vector array_elems; + ArrayLit& array = *al; + for (unsigned int j = 0; j < array.size(); j++) { + if (Id* id = array[j]->dyn_cast()) { + // std::cout << "DEBUG: getting solution value from " << *id << " : " << id->v() << + // std::endl; + array_elems.push_back(getSolutionValue(id)); + } else if (FloatLit* floatLit = array[j]->dyn_cast()) { + array_elems.push_back(floatLit); + } else if (IntLit* intLit = array[j]->dyn_cast()) { + array_elems.push_back(intLit); + } else if (BoolLit* boolLit = array[j]->dyn_cast()) { + array_elems.push_back(boolLit); + } else if (SetLit* setLit = array[j]->dyn_cast()) { + array_elems.push_back(setLit); + } else if (StringLit* strLit = array[j]->dyn_cast()) { + array_elems.push_back(strLit); + } else { + std::ostringstream oss; + oss << "Error: array element " << *array[j] << " is not an id nor a literal"; + throw InternalError(oss.str()); + } + } + GCLock lock; + ArrayLit* dims; + Expression* e = output_array_ann->arg(0); + if (ArrayLit* al = e->dyn_cast()) { + dims = al; + } else if (Id* id = e->dyn_cast()) { + dims = id->decl()->e()->cast(); + } else { + throw -1; + } + std::vector > dims_v; + for (int i = 0; i < dims->length(); i++) { + IntSetVal* isv = eval_intset(getEnv()->envi(), (*dims)[i]); + if (isv->size() == 0) { + dims_v.push_back(std::pair(1, 0)); + } else { + dims_v.push_back(std::pair(static_cast(isv->min().toInt()), + static_cast(isv->max().toInt()))); + } + } + ArrayLit* array_solution = new ArrayLit(Location(), array_elems, dims_v); + KeepAlive ka(array_solution); + auto& de = getSolns2Out()->findOutputVar(vd->id()->str().str()); + de.first->e(array_solution); + } + } else if (vd->ann().contains(constants().ann.output_var)) { + Expression* sol = getSolutionValue(vd->id()); + vd->e(sol); + auto& de = getSolns2Out()->findOutputVar(vd->id()->str().str()); + de.first->e(sol); + } + } +} + +void SolverInstanceBase::flattenSearchAnnotations(const Annotation& ann, + std::vector& out) { + for (ExpressionSetIter i = ann.begin(); i != ann.end(); ++i) { + Expression* e = *i; + if (e->isa() && (e->cast()->id().str() == "seq_search" || + e->cast()->id().str() == "warm_start_array")) { + Call* c = e->cast(); + auto* anns = c->arg(0)->cast(); + for (unsigned int i = 0; i < anns->size(); i++) { + Annotation subann; + subann.add((*anns)[i]); + flattenSearchAnnotations(subann, out); + } + } else { + out.push_back(*i); + } + } +} + +} // namespace MiniZinc diff --git a/lib/statistics.cpp b/lib/statistics.cpp new file mode 100644 index 0000000..0a7b2b1 --- /dev/null +++ b/lib/statistics.cpp @@ -0,0 +1,44 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include + +namespace MiniZinc { + +/// TODO all key words should be standard and defined in 1 place +void Statistics::print(std::ostream& os) { + os << "%%%mzn-stat: solveTime=" << _time << std::endl + << "%%%mzn-stat: nodes=" << _nodes << std::endl + << "%%%mzn-stat: failures=" << _failures << std::endl + << "%%%mzn-stat: objective=" << _objective << std::endl; +}; + +void Statistics::time(unsigned long long t) { _time = t; } +void Statistics::nodes(unsigned long long n) { _nodes = n; } +void Statistics::failures(unsigned long long f) { _failures = f; } +void Statistics::objective(double o) { _objective = o; } + +unsigned long long Statistics::time() { return _time; }; +unsigned long long Statistics::nodes() { return _nodes; }; +unsigned long long Statistics::failures() { return _failures; }; +double Statistics::objective() { return _objective; }; + +Statistics& Statistics::operator+=(Statistics& s) { + _time += s.time(); + _nodes += s.nodes(); + _failures += s.failures(); + _objective = s.objective(); + return *this; +} + +} // namespace MiniZinc diff --git a/lib/support/assembly/lexer.lxx b/lib/support/assembly/lexer.lxx new file mode 100644 index 0000000..9fe9a18 --- /dev/null +++ b/lib/support/assembly/lexer.lxx @@ -0,0 +1,122 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%option noyywrap +%option bison-bridge bison-locations + +%option prefix="mza_yy" + +%{ +#include +#include + +using namespace MiniZinc; + +#define YY_USER_ACTION { yylloc->first_line = yylineno; } +%} + +%% + +\n { yylineno++; } +[ \t] { /* ignore white space */ } +%[^\xa]* { /* ignore comments */ } +"-"?[0-9]+ { yylval->iValue = std::atoi(yytext); return MZA_INT; } +[Rr][0-9]+ { yylval->iValue = std::atoi(yytext+1); return MZA_REG; } +":" { return MZA_COLON; } +[dD] { return MZA_DELAY; } + +"GLOBAL" { return MZA_GLOBAL; } + +"ADDI" { yylval->bValue = BytecodeStream::ADDI; return MZA_ADDI; } +"SUBI" { yylval->bValue = BytecodeStream::SUBI; return MZA_SUBI; } +"MULI" { yylval->bValue = BytecodeStream::MULI; return MZA_MULI; } +"DIVI" { yylval->bValue = BytecodeStream::DIVI; return MZA_DIVI; } +"MODI" { yylval->bValue = BytecodeStream::MODI; return MZA_MODI; } +"INCI" { yylval->bValue = BytecodeStream::INCI; return MZA_INCI; } +"DECI" { yylval->bValue = BytecodeStream::DECI; return MZA_DECI; } + +"IMMI" { yylval->bValue = BytecodeStream::IMMI; return MZA_IMMI; } +"CLEAR" { yylval->bValue = BytecodeStream::CLEAR; return MZA_CLEAR; } +"LOAD_GLOBAL" { yylval->bValue = BytecodeStream::LOAD_GLOBAL; return MZA_LOAD_GLOBAL; } +"STORE_GLOBAL" { yylval->bValue = BytecodeStream::STORE_GLOBAL; return MZA_STORE_GLOBAL; } +"MOV" { yylval->bValue = BytecodeStream::MOV; return MZA_MOV; } + +"JMP" { yylval->bValue = BytecodeStream::JMP; return MZA_JMP; } +"JMPIF" { yylval->bValue = BytecodeStream::JMPIF; return MZA_JMPIF; } +"JMPIFNOT" { yylval->bValue = BytecodeStream::JMPIFNOT; return MZA_JMPIFNOT; } + +"EQI" { yylval->bValue = BytecodeStream::EQI; return MZA_EQI; } +"LTI" { yylval->bValue = BytecodeStream::LTI; return MZA_LTI; } +"LEI" { yylval->bValue = BytecodeStream::LEI; return MZA_LEI; } + +"AND" { yylval->bValue = BytecodeStream::AND; return MZA_AND; } +"OR" { yylval->bValue = BytecodeStream::OR; return MZA_OR; } +"NOT" { yylval->bValue = BytecodeStream::NOT; return MZA_NOT; } +"XOR" { yylval->bValue = BytecodeStream::XOR; return MZA_XOR; } + +"ISPAR" { yylval->bValue = BytecodeStream::ISPAR; return MZA_ISPAR; } +"ISEMPTY" { yylval->bValue = BytecodeStream::ISEMPTY; return MZA_ISEMPTY; } +"LENGTH" { yylval->bValue = BytecodeStream::LENGTH; return MZA_LENGTH; } +"GET_VEC" { yylval->bValue = BytecodeStream::GET_VEC; return MZA_GET_VEC; } +"GET_ARRAY" { yylval->bValue = BytecodeStream::GET_ARRAY; return MZA_GET_ARRAY; } + +"LB" { yylval->bValue = BytecodeStream::LB; return MZA_LB; } +"UB" { yylval->bValue = BytecodeStream::UB; return MZA_UB; } +"DOM" { yylval->bValue = BytecodeStream::DOM; return MZA_DOM; } + +"MAKE_SET" { yylval->bValue = BytecodeStream::MAKE_SET; return MZA_MAKE_SET; } +"DIFF" { yylval->bValue = BytecodeStream::DIFF; return MZA_DIFF; } +"INTERSECTION" { yylval->bValue = BytecodeStream::INTERSECTION; return MZA_INTERSECTION; } +"UNION" { yylval->bValue = BytecodeStream::UNION; return MZA_UNION; } + +"INTERSECT_DOMAIN" { yylval->bValue = BytecodeStream::INTERSECT_DOMAIN; return MZA_INTERSECT_DOMAIN; } + +"OPEN_AGGREGATION" { yylval->bValue = BytecodeStream::OPEN_AGGREGATION; return MZA_OPEN_AGGREGATION; } +"CLOSE_AGGREGATION" { yylval->bValue = BytecodeStream::CLOSE_AGGREGATION; return MZA_CLOSE_AGGREGATION; } +"SIMPLIFY_LIN" { yylval->bValue = BytecodeStream::SIMPLIFY_LIN; return MZA_SIMPLIFY_LIN; } + +"PUSH" { yylval->bValue = BytecodeStream::PUSH; return MZA_PUSH; } +"POP" { yylval->bValue = BytecodeStream::POP; return MZA_POP; } +"POST" { yylval->bValue = BytecodeStream::POST; return MZA_POST; } + +"RET" { yylval->bValue = BytecodeStream::RET; return MZA_RET; } +"CALL" { yylval->bValue = BytecodeStream::CALL; return MZA_CALL; } +"BUILTIN" { yylval->bValue = BytecodeStream::BUILTIN; return MZA_BUILTIN; } +"TCALL" { yylval->bValue = BytecodeStream::TCALL; return MZA_TCALL; } + +"ITER_ARRAY" { yylval->bValue = BytecodeStream::ITER_ARRAY; return MZA_ITER_ARRAY; } +"ITER_VEC" { yylval->bValue = BytecodeStream::ITER_VEC; return MZA_ITER_VEC; } +"ITER_RANGE" { yylval->bValue = BytecodeStream::ITER_RANGE; return MZA_ITER_RANGE; } +"ITER_NEXT" { yylval->bValue = BytecodeStream::ITER_NEXT; return MZA_ITER_NEXT; } +"ITER_BREAK" { yylval->bValue = BytecodeStream::ITER_BREAK; return MZA_ITER_BREAK; } +"TRACE" { yylval->bValue = BytecodeStream::TRACE; return MZA_TRACE; } +"ABORT" { yylval->bValue = BytecodeStream::ABORT; return MZA_ABORT; } + +"VEC" { yylval->iValue = AggregationCtx::VCTX_VEC; return MZA_CTX; } +"OTHER" { yylval->iValue = AggregationCtx::VCTX_OTHER; return MZA_CTX; } + +"ROOT" { yylval->iValue = BytecodeProc::ROOT; return MZA_MODE; } +"ROOT_NEG" { yylval->iValue = BytecodeProc::ROOT_NEG; return MZA_MODE; } +"FUN" { yylval->iValue = BytecodeProc::FUN; return MZA_MODE; } +"FUN_NEG" { yylval->iValue = BytecodeProc::FUN_NEG; return MZA_MODE; } +"IMP" { yylval->iValue = BytecodeProc::IMP; return MZA_MODE; } +"IMP_NEG" { yylval->iValue = BytecodeProc::IMP_NEG; return MZA_MODE; } + + +[A-Za-z][A-Za-z0-9_]* { yylval->sValue = strdup(yytext); return MZA_ID; } +"'"[^\\'\xa\xd\x0]*"'" { yylval->sValue = strdup(yytext); return MZA_ID; } + +. { + /* Catch all */ + throw std::runtime_error("Illegal token in regular expression: '" + std::string(yytext) + "'"); +} + +%% diff --git a/lib/support/assembly/parser.yxx b/lib/support/assembly/parser.yxx new file mode 100644 index 0000000..0872123 --- /dev/null +++ b/lib/support/assembly/parser.yxx @@ -0,0 +1,502 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%define api.pure +%define api.header.include {} + +%{ +#include +#include + +#include + +//Anonymous struct for when yyparse is exported +typedef struct MZAContext MZAContext; +#include + +using namespace MiniZinc; + +typedef struct yy_buffer_state *YY_BUFFER_STATE; +YY_BUFFER_STATE mza_yy_scan_string ( const char* yy_str ); + +extern int yylex(YYSTYPE*, YYLTYPE*); +extern FILE* yyin; + +struct ProcPatch { + int code; + BytecodeProc::Mode mode; + std::vector>> patch; + ProcPatch(int code0, BytecodeProc::Mode mode0, std::vector>> patch0) + : code(code0), mode(mode0), patch(std::move(patch0)) {} +}; + +typedef struct MZAContext { + std::vector& procs; + std::unordered_map& proc_map; + int& max_glob; + std::vector to_patch; + BytecodeStream proc_body; + std::unordered_map labels; + std::vector> patch_labels; + std::vector>> patch_procs; +} MZAContext; + +void yyerror(YYLTYPE* location, MZAContext& ctx, const char* s); +%} + +%union { + int iValue; + char* sValue; + MiniZinc::BytecodeStream::Instr bValue; + std::list* liValue; + std::list* sliValue; +} +%parse-param {MZAContext& ctx} +%locations +%define parse.error verbose + +%token MZA_INT +%token MZA_REG +%token MZA_MODE +%token MZA_CTX +%token MZA_ID + +%token MZA_COLON ":" +%token MZA_DELAY "D" + +%token MZA_GLOBAL "GLOBAL" + +%token MZA_ADDI +%token MZA_SUBI +%token MZA_MULI +%token MZA_DIVI +%token MZA_MODI +%token MZA_INCI +%token MZA_DECI + +%token MZA_IMMI +%token MZA_CLEAR +%token MZA_LOAD_GLOBAL +%token MZA_STORE_GLOBAL +%token MZA_MOV + +%token MZA_JMP +%token MZA_JMPIF +%token MZA_JMPIFNOT + +%token MZA_EQI +%token MZA_LTI +%token MZA_LEI + +%token MZA_AND +%token MZA_OR +%token MZA_NOT +%token MZA_XOR + +%token MZA_ISPAR +%token MZA_ISEMPTY +%token MZA_LENGTH +%token MZA_GET_VEC +%token MZA_GET_ARRAY + +%token MZA_LB +%token MZA_UB +%token MZA_DOM + +%token MZA_MAKE_SET +%token MZA_DIFF +%token MZA_INTERSECTION +%token MZA_UNION + +%token MZA_INTERSECT_DOMAIN + +%token MZA_OPEN_AGGREGATION +%token MZA_CLOSE_AGGREGATION +%token MZA_SIMPLIFY_LIN + +%token MZA_PUSH +%token MZA_POP +%token MZA_POST + +%token MZA_RET +%token MZA_CALL +%token MZA_BUILTIN +%token MZA_TCALL + +%token MZA_ITER_ARRAY +%token MZA_ITER_VEC +%token MZA_ITER_RANGE +%token MZA_ITER_NEXT +%token MZA_ITER_BREAK + +%token MZA_TRACE +%token MZA_ABORT + + +%type delay instruction mode +%type instr instrI instrIR instrR instrRR instrRS instrRRR instrRRS instrRRIRRR instrS +%type label +%type registers +%type labels + +%start procedures + +%% + +procedures: + /* empty */ + | procedures procedure + +procedure: + ":" MZA_ID ":" mode MZA_INT delay instructions + { + // Patch jumps with recorded labels + for (auto& cl : ctx.patch_labels) { + if (ctx.labels.find(cl.second) == ctx.labels.end()) { + throw Error("Error: label " + cl.second + " not found\n"); + } + ctx.proc_body.patchAddress(cl.first, ctx.labels[cl.second]); + } + ctx.labels.clear(); + ctx.patch_labels.clear(); + + ctx.proc_body.setNArgs($5); + + // Add ABORT instruction for error management + if (ctx.proc_body.size() > 0) { + ctx.proc_body.addInstr(BytecodeStream::ABORT); + } + // Store procedure in the correct place + auto mode = static_cast($4); + auto it = ctx.proc_map.find($2); + if (it != ctx.proc_map.end()) { + BytecodeProc& bcp = ctx.procs[it->second]; + if (bcp.mode[mode].size() > 0) { + throw Error("Error: procedure " + std::string($2) + " already defined before with the same mode\n"); + } + if (bcp.nargs != $5 ) { + throw Error("Error: procedure " + std::string($2) + " already defined before with different number of arguments\n"); + } + bcp.mode[$4] = ctx.proc_body; + ctx.to_patch.emplace_back(it->second, mode, ctx.patch_procs); + } else { + BytecodeProc bcp; + bcp.name = $2; + bcp.mode[mode] = ctx.proc_body; + bcp.nargs = $5; + bcp.delay = $6; + ctx.proc_map.emplace($2, ctx.procs.size()); + ctx.to_patch.emplace_back(ctx.procs.size(), mode, ctx.patch_procs); + ctx.procs.push_back(bcp); + } + ctx.proc_body = BytecodeStream(); + ctx.patch_procs.clear(); + } + +mode: + /* empty */ { $$ = BytecodeProc::FUN; } + | MZA_MODE + +delay: + /* empty */ { $$ = 0; } + | MZA_DELAY { $$ = 1; } + +instructions: + /* empty */ + | instructions labeled_instr + +labeled_instr: + labels instruction + { + for (auto l : *$1) { + ctx.labels.emplace(l, $2); + } + delete $1; + } + +labels: + /* empty */ { $$ = new std::list(); } + | labels label { $1->push_back($2); $$ = $1; } + +label: + MZA_ID ":" + +instruction: + instr + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + } + | instrI MZA_INT + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addIntVal($2); + } + | instrIR MZA_INT MZA_REG + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addIntVal($2); + ctx.proc_body.addReg($3); + } + | instrR MZA_REG + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + } + | instrRR MZA_REG MZA_REG + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + ctx.proc_body.addReg($3); + } + | instrRS MZA_REG MZA_ID + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + ctx.patch_labels.emplace_back(ctx.proc_body.size(), $3); + ctx.proc_body.addSmallInt(0); + } + | instrRRS MZA_REG MZA_REG MZA_ID + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + ctx.proc_body.addReg($3); + ctx.patch_labels.emplace_back(ctx.proc_body.size(), $4); + ctx.proc_body.addSmallInt(0); + } + | instrRRR MZA_REG MZA_REG MZA_REG + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + ctx.proc_body.addReg($3); + ctx.proc_body.addReg($4); + } + | instrRRIRRR MZA_REG MZA_REG MZA_INT MZA_REG MZA_REG MZA_REG + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + ctx.proc_body.addReg($3); + ctx.proc_body.addIntVal($4); + ctx.proc_body.addReg($5); + ctx.proc_body.addReg($6); + ctx.proc_body.addReg($7); + } + | instrS MZA_ID + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.patch_labels.emplace_back(ctx.proc_body.size(), $2); + ctx.proc_body.addSmallInt(0); + } + | MZA_CALL MZA_MODE MZA_ID MZA_INT registers + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addCharVal($2); + ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair($3, $5->size())); + ctx.proc_body.addSmallInt(0); + ctx.proc_body.addCharVal($4); + for (auto arg : *$5) { + ctx.proc_body.addReg(arg); + } + delete $5; + } + | MZA_BUILTIN MZA_ID registers + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair($2, $3->size())); + ctx.proc_body.addSmallInt(0); + for (auto arg : *$3) { + ctx.proc_body.addReg(arg); + } + delete $3; + } + | MZA_TCALL MZA_MODE MZA_ID MZA_INT + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addCharVal($2); + ctx.patch_procs.emplace_back(ctx.proc_body.size(), std::make_pair($3, -1)); + ctx.proc_body.addSmallInt(0); + ctx.proc_body.addCharVal($4); + } + | MZA_OPEN_AGGREGATION MZA_CTX + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addCharVal($2); + } + // Special cases: Using aggregation context with the same name as instructions + | MZA_OPEN_AGGREGATION MZA_AND + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addCharVal(AggregationCtx::VCTX_AND); + } + | MZA_OPEN_AGGREGATION MZA_OR + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addCharVal(AggregationCtx::VCTX_OR); + } + // Special cases: Read INT, but adds Reg value on the Stream + | MZA_LOAD_GLOBAL MZA_INT MZA_REG + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2, true); + ctx.max_glob = std::max(ctx.max_glob, $2); + ctx.proc_body.addReg($3); + } + | MZA_STORE_GLOBAL MZA_REG MZA_INT + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addReg($2); + ctx.proc_body.addReg($3, true); + ctx.max_glob = std::max(ctx.max_glob, $3); + } + | MZA_GET_ARRAY MZA_INT registers + { + $$ = ctx.proc_body.size(); + ctx.proc_body.addInstr($1); + ctx.proc_body.addIntVal($2); + for (auto arg : *$3) { + ctx.proc_body.addReg(arg); + } + delete $3; + } + +instrIR: + MZA_IMMI + +instrI: + MZA_ITER_BREAK + +instrR: + MZA_INCI + | MZA_DECI + | MZA_PUSH + | MZA_POP + | MZA_POST + | MZA_ITER_NEXT + | MZA_TRACE + +instrRR: + MZA_MOV + | MZA_NOT + | MZA_ISPAR + | MZA_ISEMPTY + | MZA_LENGTH + | MZA_MAKE_SET + | MZA_UB + | MZA_LB + | MZA_DOM + | MZA_CLEAR + +instrRS: + MZA_JMPIF + | MZA_JMPIFNOT + | MZA_ITER_ARRAY + | MZA_ITER_VEC + +instrRRR: + MZA_ADDI + | MZA_SUBI + | MZA_MULI + | MZA_DIVI + | MZA_MODI + | MZA_EQI + | MZA_LTI + | MZA_LEI + | MZA_AND + | MZA_OR + | MZA_XOR + | MZA_GET_VEC + | MZA_DIFF + | MZA_INTERSECTION + | MZA_UNION + | MZA_INTERSECT_DOMAIN + +instrRRS: + MZA_ITER_RANGE + +instrRRIRRR: + MZA_SIMPLIFY_LIN + +instrS: + MZA_JMP + +instr: + MZA_RET + | MZA_CLOSE_AGGREGATION + | MZA_ABORT + + +registers: + /* empty */ { $$ = new std::list(); } + | registers MZA_REG { $1->push_back($2); $$ = $1; } + +%% + +#include +#include + +void yyerror(YYLTYPE* location, MZAContext& ctx, const char* s) { + std::ostringstream oss; + oss << "Cannot parse MiniZinc assembly in line " << location->first_line << ": " << std::string(s); + throw Error(oss.str()); +} + +std::pair> parse_mza(const std::string& assembly_str) { + std::vector procs; + std::unordered_map proc_map; + int max_glob; + // Initialise first slots with + for (PrimitiveMap::Primitive* p : primitiveMap()) { + BytecodeProc bcp; + bcp.name = p->name(); + bcp.nargs = p->n_args(); + bcp.delay = false; + procs.push_back(bcp); + proc_map.emplace(bcp.name, p->ident()); + } + mza_yy_scan_string(assembly_str.c_str()); + MZAContext ctx = MZAContext{procs, proc_map, max_glob}; + int err = yyparse(ctx); + if (err != 0) { + throw std::runtime_error("Cannot parse MiniZinc assembly: " + std::to_string(err)); + } + for (auto& p : ctx.to_patch) { + int code = p.code; + BytecodeProc::Mode mode = p.mode; + for (auto& patch : p.patch) { + int& nargs = patch.second.second; + std::string& name = patch.second.first; + + if (nargs >= 0 && nargs != procs[proc_map[name]].nargs) { + throw Error("Error: number of arguments in call to " + patch.second.first + " at position " + + std::to_string(patch.second.second) + " has an invalid number of arguments.\n"); + } + procs[code].mode[mode].patchAddress(patch.first, proc_map[patch.second.first]); + } + } + return {max_glob, procs}; +} diff --git a/lib/support/regex/lexer.lxx b/lib/support/regex/lexer.lxx new file mode 100644 index 0000000..8e8d70e --- /dev/null +++ b/lib/support/regex/lexer.lxx @@ -0,0 +1,52 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%option noyywrap + +%option prefix="regex_yy" + +%{ +#include + +#define YY_DECL int yylex() + +#include + +%} + +%% + +[ \t\n] { /* ignore white space */ } +[0-9]+ { regex_yylval.iValue = std::atoi(regex_yytext); return R_INTEGER; } +"|" { return R_UNION; } +"+" { return R_PLUS; } +"*" { return R_STAR; } +"(" { return R_GROUP_OPEN; } +")" { return R_GROUP_CLOSE; } +"?" { return R_OPTIONAL; } +"{" { return R_QUANT_OPEN; } +"}" { return R_QUANT_CLOSE; } +"," { return R_COMMA; } +"." { return R_ANY; } +"[" { return R_CLASS_OPEN; } +"]" { return R_CLASS_CLOSE; } +"-" { return R_CLASS_RANGE; } +"^" { return R_CLASS_NEG; } + +[A-Za-z][A-Za-z0-9_]* { regex_yylval.sValue = strdup(yytext); return R_IDENTIFIER; } +"'"[^\\'\xa\xd\x0]*"'" { regex_yylval.sValue = strdup(yytext); return R_IDENTIFIER; } + +. { + /* Catch all */ + throw std::runtime_error("Illegal token in regular expression: '" + std::string(regex_yytext) + "'"); +} + +%% diff --git a/lib/support/regex/parser.yxx b/lib/support/regex/parser.yxx new file mode 100644 index 0000000..97aecad --- /dev/null +++ b/lib/support/regex/parser.yxx @@ -0,0 +1,225 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Jip J. Dekker + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%define api.header.include {} + +%{ +#include +#include +#include +#include +#include + +#include +#include +using namespace Gecode; +using namespace MiniZinc; + +typedef struct yy_buffer_state *YY_BUFFER_STATE; +YY_BUFFER_STATE regex_yy_scan_string ( const char* yy_str ); + +extern int yylex(); +extern FILE* yyin; + +typedef struct REContext{ + REG* expr; + const IntSetVal& dom; + const std::unordered_map& idMap; +} REContext; + +void yyerror(REContext& ctx, const char* s); +%} + +%union { + int iValue; + char* sValue; + std::unordered_set* setValue; + Gecode::REG* rValue; +} +%parse-param {REContext& ctx} + +%token R_INTEGER +%token R_GROUP_OPEN "(" +%token R_GROUP_CLOSE ")" +%token R_STAR "*" +%token R_PLUS "+" +%token R_ANY "." +%token R_UNION "|" +%token R_OPTIONAL "?" +%token R_QUANT_OPEN "{" +%token R_QUANT_CLOSE "}" +%token R_COMMA "," +%token R_CLASS_OPEN "[" +%token R_CLASS_CLOSE "]" +%token R_CLASS_RANGE "-" +%token R_CLASS_NEG "^" +%token R_IDENTIFIER + +%type literal +%type regex expression term factor atom +%type set_item set_items + +%start regex + +%% + +regex: + expression + { + *ctx.expr = (*$1); + delete $1; + } + +expression: + term + | term "|" expression + { + *$1 = *$1 | *$3; + delete $3; + $$ = $1; + } + +term: + factor + | factor term + { + *$1 = *$1 + *$2; + delete $2; + $$ = $1; + } + +factor: + atom + | atom "*" + { + *$1 = *(*$1); + $$ = $1; + } + | atom "+" + { + *$1 = +(*$1); + $$ = $1; + } + | atom "?" + { + *$1 = (*$1)(0, 1); + $$ = $1; + } + | atom "{" R_INTEGER "}" + { + *$1 = (*$1)($3, $3); + $$ = $1; + } + | atom "{" R_INTEGER "," "}" + { + *$1 = (*$1)($3); + $$ = $1; + } + | atom "{" R_INTEGER "," R_INTEGER "}" + { + *$1 = (*$1)($3, $5); + $$ = $1; + } + +atom: + literal + { $$ = new REG($1); } + | "." + { + IntArgs range = IntArgs::create(ctx.dom.max().toInt() - ctx.dom.min().toInt() + 1, ctx.dom.min().toInt()); + $$ = new REG(range); + } + | "[" set_items "]" + { + std::vector v; + v.reserve($2->size()); + for(auto i : *$2) { + v.push_back(i); + } + delete $2; + $$ = new REG(IntArgs(v)); + } + | "[" "^" set_items "]" + { + std::vector diff; + std::unordered_set domain; + for(int i = ctx.dom.min().toInt(); i<=ctx.dom.max().toInt(); ++i) { + domain.insert(i); + } + std::set_difference( + domain.begin(), domain.end(), + $3->begin(), $3->end(), + std::inserter(diff, diff.begin()) + ); + delete $3; + $$ = new REG(IntArgs(diff)); + } + | "(" expression ")" + { $$ = $2; } + +set_items: + set_item + | set_item set_items + { + $$ = $1; + for (auto i : *$2) { + $1->insert(i); + } + delete $2; + } + +set_item: + literal + { + $$ = new std::unordered_set({$1}); + } + | literal "-" literal + { + int from = $1; + int to = $3; + if (to < from) { + std::swap(from,to); + } + $$ = new std::unordered_set; + for(int i = from; i<=to; ++i) { + $$->insert(i); + } + } + +literal: + R_INTEGER + | R_IDENTIFIER + { + std::string s($1); + auto find = ctx.idMap.find(s); + if (find == ctx.idMap.end()) { + throw std::runtime_error("Unknown identifier: " + s); + } + $$ = find->second; + } + +%% + +void yyerror(REContext& ctx, const char* s) { + // TODO: Add error location + throw std::runtime_error("Cannot parse regular expression: " + std::string(s)); +} + +std::unique_ptr regex_from_string(const std::string& regex_str, const IntSetVal& domain, const std::unordered_map& identifiers) { + REG* expr = new REG(); + regex_yy_scan_string(regex_str.c_str()); + REContext rctx = REContext{expr, domain, identifiers}; + int err = yyparse(rctx); + if (err != 0) { + throw std::runtime_error("Cannot parse regular expression, error code " + std::to_string(err)); + } + return std::unique_ptr(expr); +} diff --git a/lib/type.cpp b/lib/type.cpp new file mode 100644 index 0000000..45b367b --- /dev/null +++ b/lib/type.cpp @@ -0,0 +1,133 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +namespace MiniZinc { + +std::string Type::toString(EnvI& env) const { + std::ostringstream oss; + if (_dim > 0) { + oss << "array["; + if (_enumId != 0) { + const std::vector& arrayEnumIds = env.getArrayEnum(_enumId); + for (unsigned int i = 0; i < arrayEnumIds.size() - 1; i++) { + if (i != 0) oss << ","; + unsigned int enumId = arrayEnumIds[i]; + if (enumId == 0) { + oss << "int"; + } else { + oss << *env.getEnum(enumId)->e()->id(); + } + } + } else { + for (int i = 0; i < _dim; i++) oss << (i == 0 ? "" : ",") << "int"; + } + oss << "] of "; + } + if (_dim < 0) oss << "array[$_] of "; + switch (_ti) { + case TI_PAR: + break; + case TI_VAR: + oss << "var "; + break; + } + if (_ot == OT_OPTIONAL) oss << "opt "; + if (_st == ST_SET) oss << "set of "; + switch (_bt) { + case BT_INT: { + unsigned int enumId; + if (_enumId != 0 && _dim > 0) { + const std::vector& arrayEnumIds = env.getArrayEnum(_enumId); + enumId = arrayEnumIds[arrayEnumIds.size() - 1]; + } else { + enumId = _enumId; + } + if (enumId == 0) { + oss << "int"; + } else { + oss << *env.getEnum(enumId)->e()->id(); + } + } break; + case BT_BOOL: + oss << "bool"; + break; + case BT_FLOAT: + oss << "float"; + break; + case BT_STRING: + oss << "string"; + break; + case BT_ANN: + oss << "ann"; + break; + case BT_BOT: + oss << "bot"; + break; + case BT_TOP: + oss << "top"; + break; + case BT_UNKNOWN: + oss << "??? "; + break; + } + return oss.str(); +} + +std::string Type::nonEnumToString(void) const { + std::ostringstream oss; + if (_dim > 0) { + oss << "array[int"; + for (int i = 1; i < _dim; i++) oss << ",int"; + oss << "] of "; + } + if (_dim < 0) oss << "array[$_] of "; + switch (_ti) { + case TI_PAR: + break; + case TI_VAR: + oss << "var "; + break; + } + if (_ot == OT_OPTIONAL) oss << "opt "; + if (_st == ST_SET) oss << "set of "; + switch (_bt) { + case BT_INT: + oss << "int"; + break; + case BT_BOOL: + oss << "bool"; + break; + case BT_FLOAT: + oss << "float"; + break; + case BT_STRING: + oss << "string"; + break; + case BT_ANN: + oss << "ann"; + break; + case BT_BOT: + oss << "bot"; + break; + case BT_TOP: + oss << "top"; + break; + case BT_UNKNOWN: + oss << "??? "; + break; + } + return oss.str(); +} + +} // namespace MiniZinc diff --git a/lib/typecheck.cpp b/lib/typecheck.cpp new file mode 100644 index 0000000..c1c7f5c --- /dev/null +++ b/lib/typecheck.cpp @@ -0,0 +1,2422 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace MiniZinc { + +Scopes::Scopes(void) { + s.push_back(Scope()); + s.back().toplevel = true; +} + +void Scopes::add(EnvI& env, VarDecl* vd) { + if (!s.back().toplevel && vd->ti()->isEnum() && vd->e()) { + throw TypeError(env, vd->loc(), "enums are only allowed at top level"); + } + if (vd->id()->idn() == -1 && vd->id()->v() == "") return; + DeclMap::iterator vdi = s.back().m.find(vd->id()); + if (vdi == s.back().m.end()) { + s.back().m.insert(vd->id(), vd); + } else { + GCLock lock; + throw TypeError(env, vd->loc(), "identifier `" + vd->id()->str().str() + "' already defined"); + } +} + +void Scopes::push(bool toplevel) { + s.push_back(Scope()); + s.back().toplevel = toplevel; +} + +void Scopes::pop(void) { s.pop_back(); } + +VarDecl* Scopes::find(Id* ident) { + int cur = static_cast(s.size()) - 1; + for (;;) { + DeclMap::iterator vdi = s[cur].m.find(ident); + if (vdi == s[cur].m.end()) { + if (s[cur].toplevel) { + if (cur > 0) + cur = 0; + else + return NULL; + } else { + cur--; + } + } else { + return vdi->second; + } + } +} + +struct VarDeclCmp { + std::unordered_map& _pos; + VarDeclCmp(std::unordered_map& pos) : _pos(pos) {} + bool operator()(Expression* e0, Expression* e1) { + if (VarDecl* vd0 = Expression::dyn_cast(e0)) { + if (VarDecl* vd1 = Expression::dyn_cast(e1)) { + return _pos[vd0] < _pos[vd1]; + } else { + return true; + } + } else { + return false; + } + } +}; +struct ItemCmp { + std::unordered_map& _pos; + ItemCmp(std::unordered_map& pos) : _pos(pos) {} + bool operator()(Item* i0, Item* i1) { + if (VarDeclI* vd0 = i0->cast()) { + if (VarDeclI* vd1 = i1->cast()) { + return _pos[vd0->e()] < _pos[vd1->e()]; + } else { + return true; + } + } else { + return false; + } + } +}; + +std::string createEnumToStringName(Id* ident, std::string prefix) { + std::string name = ident->str().str(); + if (name[0] == '\'') { + name = "'" + prefix + name.substr(1); + } else { + name = prefix + name; + } + return name; +} + +AssignI* createEnumMapper(EnvI& env, Model* m, unsigned int enumId, VarDecl* vd, + VarDecl* vd_enumToString, Model* enumItems) { + Id* ident = vd->id(); + SetLit* sl = vd->e()->dyn_cast(); + ArrayLit* enum_init_al = NULL; + AssignI* ret = NULL; + + GCLock lock; + if (ArrayLit* al = vd->e()->dyn_cast()) { + enum_init_al = al; + } else if (Call* c = vd->e()->dyn_cast()) { + if (c->id() != "anon_enum") { + throw TypeError(env, c->loc(), "invalid initialisation for enum `" + ident->v().str() + "'"); + } + if (c->n_args() == 1 && c->arg(0)->isa()) { + enum_init_al = c->arg(0)->cast(); + } + } + if (enum_init_al) { + std::vector enumIds(enum_init_al->size()); + for (unsigned int i = 0; i < enum_init_al->size(); i++) { + if (Id* eid = (*enum_init_al)[i]->dyn_cast()) { + enumIds[i] = eid; + } else { + throw TypeError(env, vd->e()->loc(), + "invalid initialisation for enum `" + ident->v().str() + "'"); + } + } + sl = new SetLit(vd->e()->loc(), enumIds); + } + if (sl) { + for (unsigned int i = 0; i < sl->v().size(); i++) { + if (!sl->v()[i]->isa()) { + throw TypeError(env, sl->v()[i]->loc(), + "invalid initialisation for enum `" + ident->v().str() + "'"); + } + TypeInst* ti_id = new TypeInst(sl->v()[i]->loc(), Type::parenum(enumId)); + + std::vector toEnumArgs(2); + toEnumArgs[0] = vd->id(); + toEnumArgs[1] = IntLit::a(i + 1); + Call* toEnum = new Call(sl->v()[i]->loc(), ASTString("to_enum"), toEnumArgs); + toEnum->decl(env.model->matchFn(env, toEnum, false)); + VarDecl* vd_id = new VarDecl(ti_id->loc(), ti_id, sl->v()[i]->cast()->str(), toEnum); + enumItems->addItem(new VarDeclI(vd_id->loc(), vd_id)); + } + SetLit* nsl = new SetLit(vd->loc(), IntSetVal::a(1, sl->v().size())); + Type tt = nsl->type(); + tt.enumId(vd->type().enumId()); + nsl->type(tt); + vd->e(nsl); + } else if (!vd->e()->isa()) { + throw TypeError(env, vd->e()->loc(), + "invalid initialisation for enum `" + ident->v().str() + "'"); + } + + if (sl) { + std::string name = createEnumToStringName(ident, "_enum_to_string_"); + std::vector al_args(sl->v().size()); + for (unsigned int i = 0; i < sl->v().size(); i++) { + std::string str = sl->v()[i]->cast()->str().str(); + if (str.size() >= 2 && str[0] == '\'' && str[str.size() - 1] == '\'') { + al_args[i] = + new StringLit(Location().introduce(), ASTString(str.substr(1, str.size() - 2))); + } else { + al_args[i] = new StringLit(Location().introduce(), ASTString(str)); + } + env.reverseEnum[str] = i + 1; + } + ArrayLit* al = new ArrayLit(Location().introduce(), al_args); + + if (vd_enumToString) { + ret = new AssignI(Location().introduce(), name, al); + ret->decl(vd_enumToString); + } else { + std::vector ranges(1); + ranges[0] = new TypeInst(Location().introduce(), Type::parint()); + TypeInst* ti = new TypeInst(Location().introduce(), Type::parstring(1)); + ti->setRanges(ranges); + vd_enumToString = new VarDecl(Location().introduce(), ti, name, al); + enumItems->addItem(new VarDeclI(Location().introduce(), vd_enumToString)); + } + + Type tx = Type::parint(); + tx.ot(Type::OT_OPTIONAL); + TypeInst* ti_aa = new TypeInst(Location().introduce(), tx); + VarDecl* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x"); + vd_aa->toplevel(false); + TypeInst* ti_ab = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b"); + vd_ab->toplevel(false); + TypeInst* ti_aj = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json"); + vd_aj->toplevel(false); + TypeInst* ti_fi = new TypeInst(Location().introduce(), Type::parstring()); + std::vector fi_params(3); + fi_params[0] = vd_aa; + fi_params[1] = vd_ab; + fi_params[2] = vd_aj; + + std::vector deopt_args(1); + deopt_args[0] = vd_aa->id(); + Call* deopt = new Call(Location().introduce(), "deopt", deopt_args); + Call* occurs = new Call(Location().introduce(), "occurs", deopt_args); + std::vector aa_args(1); + aa_args[0] = deopt; + ArrayAccess* aa = new ArrayAccess(Location().introduce(), vd_enumToString->id(), aa_args); + + StringLit* sl_absent = new StringLit(Location().introduce(), "<>"); + + ITE* if_absent = + new ITE(Location().introduce(), + {vd_aj->id(), new StringLit(Location().introduce(), ASTString("null"))}, sl_absent); + + StringLit* json_e_quote = new StringLit(Location().introduce(), ASTString("{\"e\":\"")); + StringLit* json_e_quote_end = new StringLit(Location().introduce(), ASTString("\"}")); + BinOp* quote_aa = new BinOp(Location().introduce(), json_e_quote, BOT_PLUSPLUS, aa); + BinOp* quote_aa2 = new BinOp(Location().introduce(), quote_aa, BOT_PLUSPLUS, json_e_quote_end); + + Call* quote_dzn = new Call(Location().introduce(), ASTString("showDznId"), {aa}); + + std::vector ite_ifelse(2); + ite_ifelse[0] = occurs; + ite_ifelse[1] = + new ITE(Location().introduce(), {vd_ab->id(), quote_dzn, vd_aj->id(), quote_aa2}, aa); + + ITE* ite = new ITE(Location().introduce(), ite_ifelse, if_absent); + + FunctionI* fi = new FunctionI( + Location().introduce(), createEnumToStringName(ident, "_toString_"), ti_fi, fi_params, ite); + enumItems->addItem(fi); + } else { + if (vd_enumToString) { + /// TODO: find a better solution (don't introduce the vd_enumToString until we + /// know it's a non-anonymous enum) + vd_enumToString->e(new ArrayLit(Location().introduce(), std::vector())); + } + { + Type tx = Type::parint(); + tx.ot(Type::OT_OPTIONAL); + TypeInst* ti_aa = new TypeInst(Location().introduce(), tx); + VarDecl* vd_aa = new VarDecl(Location().introduce(), ti_aa, "x"); + vd_aa->toplevel(false); + + TypeInst* ti_ab = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_ab = new VarDecl(Location().introduce(), ti_ab, "b"); + vd_ab->toplevel(false); + + TypeInst* ti_aj = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_aj = new VarDecl(Location().introduce(), ti_aj, "json"); + vd_aj->toplevel(false); + + std::vector deopt_args(1); + deopt_args[0] = vd_aa->id(); + Call* deopt = new Call(Location().introduce(), "deopt", deopt_args); + Call* if_absent = new Call(Location().introduce(), "absent", deopt_args); + StringLit* sl_absent_dzn = new StringLit(Location().introduce(), "<>"); + ITE* sl_absent = new ITE( + Location().introduce(), + {vd_aj->id(), new StringLit(Location().introduce(), ASTString("null"))}, sl_absent_dzn); + + StringLit* sl_dzn = + new StringLit(Location().introduce(), ASTString("to_enum(" + ident->str().str() + ",")); + std::vector showIntArgs(1); + showIntArgs[0] = deopt; + Call* showInt = new Call(Location().introduce(), constants().ids.show, showIntArgs); + BinOp* construct_string_dzn = + new BinOp(Location().introduce(), sl_dzn, BOT_PLUSPLUS, showInt); + StringLit* closing_bracket = new StringLit(Location().introduce(), ASTString(")")); + BinOp* construct_string_dzn_2 = + new BinOp(Location().introduce(), construct_string_dzn, BOT_PLUSPLUS, closing_bracket); + + StringLit* sl = new StringLit(Location().introduce(), ASTString(ident->str().str() + "_")); + BinOp* construct_string = new BinOp(Location().introduce(), sl, BOT_PLUSPLUS, showInt); + + StringLit* json_e_quote = new StringLit(Location().introduce(), ASTString("{\"e\":\"")); + StringLit* json_e_quote_end = new StringLit(Location().introduce(), ASTString("\"}")); + BinOp* construct_string_json = + new BinOp(Location().introduce(), json_e_quote, BOT_PLUSPLUS, construct_string); + BinOp* construct_string_json_2 = + new BinOp(Location().introduce(), construct_string_json, BOT_PLUSPLUS, json_e_quote_end); + + std::vector if_then(6); + if_then[0] = if_absent; + if_then[1] = sl_absent; + if_then[2] = vd_ab->id(); + if_then[3] = construct_string_dzn_2; + if_then[4] = vd_aj->id(); + if_then[5] = construct_string_json_2; + ITE* ite = new ITE(Location().introduce(), if_then, construct_string); + + TypeInst* ti_fi = new TypeInst(Location().introduce(), Type::parstring()); + std::vector fi_params(3); + fi_params[0] = vd_aa; + fi_params[1] = vd_ab; + fi_params[2] = vd_aj; + FunctionI* fi = + new FunctionI(Location().introduce(), createEnumToStringName(ident, "_toString_"), ti_fi, + fi_params, ite); + enumItems->addItem(fi); + } + } + + { + /* + + function _toString_ENUM(array[$U] of opt Foo: x, bool: b, bool: json) = + let { + array[int] of opt ENUM: xx = array1d(x) + } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b,json) | i in index_set(xx) ]) ++ "]"; + + */ + + TIId* tiid = new TIId(Location().introduce(), "U"); + TypeInst* ti_range = new TypeInst(Location().introduce(), Type::parint(), tiid); + std::vector ranges(1); + ranges[0] = ti_range; + + Type tx = Type::parint(-1); + tx.ot(Type::OT_OPTIONAL); + TypeInst* x_ti = new TypeInst(Location().introduce(), tx, ranges, ident); + VarDecl* vd_x = new VarDecl(Location().introduce(), x_ti, "x"); + vd_x->toplevel(false); + + TypeInst* b_ti = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_b = new VarDecl(Location().introduce(), b_ti, "b"); + vd_b->toplevel(false); + + TypeInst* j_ti = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_j = new VarDecl(Location().introduce(), j_ti, "json"); + vd_j->toplevel(false); + + TypeInst* xx_range = new TypeInst(Location().introduce(), Type::parint(), NULL); + std::vector xx_ranges(1); + xx_ranges[0] = xx_range; + TypeInst* xx_ti = new TypeInst(Location().introduce(), tx, xx_ranges, ident); + + std::vector array1dArgs(1); + array1dArgs[0] = vd_x->id(); + Call* array1dCall = new Call(Location().introduce(), "array1d", array1dArgs); + + VarDecl* vd_xx = new VarDecl(Location().introduce(), xx_ti, "xx", array1dCall); + vd_xx->toplevel(false); + + TypeInst* idx_i_ti = new TypeInst(Location().introduce(), Type::parint()); + VarDecl* idx_i = new VarDecl(Location().introduce(), idx_i_ti, "i"); + idx_i->toplevel(false); + + std::vector aa_xxi_idx(1); + aa_xxi_idx[0] = idx_i->id(); + ArrayAccess* aa_xxi = new ArrayAccess(Location().introduce(), vd_xx->id(), aa_xxi_idx); + + std::vector _toString_ENUMArgs(3); + _toString_ENUMArgs[0] = aa_xxi; + _toString_ENUMArgs[1] = vd_b->id(); + _toString_ENUMArgs[2] = vd_j->id(); + Call* _toString_ENUM = new Call( + Location().introduce(), createEnumToStringName(ident, "_toString_"), _toString_ENUMArgs); + + std::vector index_set_xx_args(1); + index_set_xx_args[0] = vd_xx->id(); + Call* index_set_xx = new Call(Location().introduce(), "index_set", index_set_xx_args); + std::vector gen_exps(1); + gen_exps[0] = idx_i; + Generator gen(gen_exps, index_set_xx, NULL); + + Generators generators; + generators._g.push_back(gen); + Comprehension* comp = + new Comprehension(Location().introduce(), _toString_ENUM, generators, false); + + std::vector join_args(2); + join_args[0] = new StringLit(Location().introduce(), ", "); + join_args[1] = comp; + Call* join = new Call(Location().introduce(), "join", join_args); + + StringLit* sl_open = new StringLit(Location().introduce(), "["); + BinOp* bopp0 = new BinOp(Location().introduce(), sl_open, BOT_PLUSPLUS, join); + StringLit* sl_close = new StringLit(Location().introduce(), "]"); + BinOp* bopp1 = new BinOp(Location().introduce(), bopp0, BOT_PLUSPLUS, sl_close); + + std::vector let_args(1); + let_args[0] = vd_xx; + Let* let = new Let(Location().introduce(), let_args, bopp1); + + TypeInst* ti_fi = new TypeInst(Location().introduce(), Type::parstring()); + std::vector fi_params(3); + fi_params[0] = vd_x; + fi_params[1] = vd_b; + fi_params[2] = vd_j; + FunctionI* fi = new FunctionI( + Location().introduce(), createEnumToStringName(ident, "_toString_"), ti_fi, fi_params, let); + enumItems->addItem(fi); + } + + { + /* + + function _toString_ENUM(opt set of ENUM: x, bool: b, bool: json) = + if absent(x) then "<>" else "{" ++ join(", ", [ _toString_ENUM(i,b,json) | i in x ]) ++ "}" + endif; + + */ + + Type argType = Type::parsetenum(ident->type().enumId()); + argType.ot(Type::OT_OPTIONAL); + TypeInst* x_ti = new TypeInst(Location().introduce(), argType, ident); + VarDecl* vd_x = new VarDecl(Location().introduce(), x_ti, "x"); + vd_x->toplevel(false); + + TypeInst* b_ti = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_b = new VarDecl(Location().introduce(), b_ti, "b"); + vd_b->toplevel(false); + + TypeInst* j_ti = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_j = new VarDecl(Location().introduce(), j_ti, "json"); + vd_j->toplevel(false); + + TypeInst* idx_i_ti = new TypeInst(Location().introduce(), Type::parint()); + VarDecl* idx_i = new VarDecl(Location().introduce(), idx_i_ti, "i"); + idx_i->toplevel(false); + + std::vector _toString_ENUMArgs(3); + _toString_ENUMArgs[0] = idx_i->id(); + _toString_ENUMArgs[1] = vd_b->id(); + _toString_ENUMArgs[2] = vd_j->id(); + Call* _toString_ENUM = new Call( + Location().introduce(), createEnumToStringName(ident, "_toString_"), _toString_ENUMArgs); + + std::vector deopt_args(1); + deopt_args[0] = vd_x->id(); + Call* deopt = new Call(Location().introduce(), "deopt", deopt_args); + Call* if_absent = new Call(Location().introduce(), "absent", deopt_args); + StringLit* sl_absent_dzn = new StringLit(Location().introduce(), "<>"); + ITE* sl_absent = new ITE(Location().introduce(), + {vd_j->id(), new StringLit(Location().introduce(), ASTString("null"))}, + sl_absent_dzn); + + std::vector gen_exps(1); + gen_exps[0] = idx_i; + Generator gen(gen_exps, deopt, NULL); + + Generators generators; + generators._g.push_back(gen); + Comprehension* comp = + new Comprehension(Location().introduce(), _toString_ENUM, generators, false); + + std::vector join_args(2); + join_args[0] = new StringLit(Location().introduce(), ", "); + join_args[1] = comp; + Call* join = new Call(Location().introduce(), "join", join_args); + + ITE* json_set = + new ITE(Location().introduce(), + {vd_j->id(), new StringLit(Location().introduce(), ASTString("\"set\":["))}, + new StringLit(Location().introduce(), ASTString(""))); + ITE* json_set_close = new ITE( + Location().introduce(), {vd_j->id(), new StringLit(Location().introduce(), ASTString("]"))}, + new StringLit(Location().introduce(), ASTString(""))); + + StringLit* sl_open = new StringLit(Location().introduce(), "{"); + BinOp* bopp0 = new BinOp(Location().introduce(), sl_open, BOT_PLUSPLUS, json_set); + BinOp* bopp1 = new BinOp(Location().introduce(), bopp0, BOT_PLUSPLUS, join); + BinOp* bopp2 = new BinOp(Location().introduce(), bopp1, BOT_PLUSPLUS, json_set_close); + StringLit* sl_close = new StringLit(Location().introduce(), "}"); + BinOp* bopp3 = new BinOp(Location().introduce(), bopp2, BOT_PLUSPLUS, sl_close); + + std::vector if_then(2); + if_then[0] = if_absent; + if_then[1] = sl_absent; + ITE* ite = new ITE(Location().introduce(), if_then, bopp3); + + TypeInst* ti_fi = new TypeInst(Location().introduce(), Type::parstring()); + std::vector fi_params(3); + fi_params[0] = vd_x; + fi_params[1] = vd_b; + fi_params[2] = vd_j; + FunctionI* fi = new FunctionI( + Location().introduce(), createEnumToStringName(ident, "_toString_"), ti_fi, fi_params, ite); + enumItems->addItem(fi); + } + + { + /* + + function _toString_ENUM(array[$U] of opt set of ENUM: x, bool: b, bool: json) = + let { + array[int] of opt set of ENUM: xx = array1d(x) + } in "[" ++ join(", ", [ _toString_ENUM(xx[i],b,json) | i in index_set(xx) ]) ++ "]"; + + */ + + TIId* tiid = new TIId(Location().introduce(), "U"); + TypeInst* ti_range = new TypeInst(Location().introduce(), Type::parint(), tiid); + std::vector ranges(1); + ranges[0] = ti_range; + + Type tx = Type::parsetint(-1); + tx.ot(Type::OT_OPTIONAL); + TypeInst* x_ti = new TypeInst(Location().introduce(), tx, ranges, ident); + VarDecl* vd_x = new VarDecl(Location().introduce(), x_ti, "x"); + vd_x->toplevel(false); + + TypeInst* b_ti = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_b = new VarDecl(Location().introduce(), b_ti, "b"); + vd_b->toplevel(false); + + TypeInst* j_ti = new TypeInst(Location().introduce(), Type::parbool()); + VarDecl* vd_j = new VarDecl(Location().introduce(), j_ti, "json"); + vd_j->toplevel(false); + + TypeInst* xx_range = new TypeInst(Location().introduce(), Type::parint(), NULL); + std::vector xx_ranges(1); + xx_ranges[0] = xx_range; + TypeInst* xx_ti = new TypeInst(Location().introduce(), tx, xx_ranges, ident); + + std::vector array1dArgs(1); + array1dArgs[0] = vd_x->id(); + Call* array1dCall = new Call(Location().introduce(), "array1d", array1dArgs); + + VarDecl* vd_xx = new VarDecl(Location().introduce(), xx_ti, "xx", array1dCall); + vd_xx->toplevel(false); + + TypeInst* idx_i_ti = new TypeInst(Location().introduce(), Type::parint()); + VarDecl* idx_i = new VarDecl(Location().introduce(), idx_i_ti, "i"); + idx_i->toplevel(false); + + std::vector aa_xxi_idx(1); + aa_xxi_idx[0] = idx_i->id(); + ArrayAccess* aa_xxi = new ArrayAccess(Location().introduce(), vd_xx->id(), aa_xxi_idx); + + std::vector _toString_ENUMArgs(3); + _toString_ENUMArgs[0] = aa_xxi; + _toString_ENUMArgs[1] = vd_b->id(); + _toString_ENUMArgs[2] = vd_j->id(); + Call* _toString_ENUM = new Call( + Location().introduce(), createEnumToStringName(ident, "_toString_"), _toString_ENUMArgs); + + std::vector index_set_xx_args(1); + index_set_xx_args[0] = vd_xx->id(); + Call* index_set_xx = new Call(Location().introduce(), "index_set", index_set_xx_args); + std::vector gen_exps(1); + gen_exps[0] = idx_i; + Generator gen(gen_exps, index_set_xx, NULL); + + Generators generators; + generators._g.push_back(gen); + Comprehension* comp = + new Comprehension(Location().introduce(), _toString_ENUM, generators, false); + + std::vector join_args(2); + join_args[0] = new StringLit(Location().introduce(), ", "); + join_args[1] = comp; + Call* join = new Call(Location().introduce(), "join", join_args); + + StringLit* sl_open = new StringLit(Location().introduce(), "["); + BinOp* bopp0 = new BinOp(Location().introduce(), sl_open, BOT_PLUSPLUS, join); + StringLit* sl_close = new StringLit(Location().introduce(), "]"); + BinOp* bopp1 = new BinOp(Location().introduce(), bopp0, BOT_PLUSPLUS, sl_close); + + std::vector let_args(1); + let_args[0] = vd_xx; + Let* let = new Let(Location().introduce(), let_args, bopp1); + + TypeInst* ti_fi = new TypeInst(Location().introduce(), Type::parstring()); + std::vector fi_params(3); + fi_params[0] = vd_x; + fi_params[1] = vd_b; + fi_params[2] = vd_j; + FunctionI* fi = new FunctionI( + Location().introduce(), createEnumToStringName(ident, "_toString_"), ti_fi, fi_params, let); + enumItems->addItem(fi); + } + return ret; +} + +void TopoSorter::add(EnvI& env, VarDeclI* vdi, bool handleEnums, Model* enumItems) { + VarDecl* vd = vdi->e(); + if (handleEnums && vd->ti()->isEnum()) { + unsigned int enumId = env.registerEnum(vdi); + Type vdt = vd->type(); + vdt.enumId(enumId); + vd->ti()->type(vdt); + vd->type(vdt); + + if (vd->e()) { + (void)createEnumMapper(env, model, enumId, vd, NULL, enumItems); + } else { + GCLock lock; + std::string name = createEnumToStringName(vd->id(), "_enum_to_string_"); + std::vector ranges(1); + ranges[0] = new TypeInst(Location().introduce(), Type::parint()); + TypeInst* ti = new TypeInst(Location().introduce(), Type::parstring(1)); + ti->setRanges(ranges); + VarDecl* vd_enumToString = new VarDecl(Location().introduce(), ti, name, NULL); + enumItems->addItem(new VarDeclI(Location().introduce(), vd_enumToString)); + } + } + scopes.add(env, vd); +} + +VarDecl* TopoSorter::get(EnvI& env, const ASTString& id_v, const Location& loc) { + GCLock lock; + Id* id = new Id(Location(), id_v, NULL); + VarDecl* decl = scopes.find(id); + if (decl == NULL) { + throw TypeError(env, loc, "undefined identifier `" + id->str().str() + "'"); + } + return decl; +} + +VarDecl* TopoSorter::checkId(EnvI& env, Id* ident, const Location& loc) { + VarDecl* decl = scopes.find(ident); + if (decl == NULL) { + GCLock lock; + throw TypeError(env, loc, "undefined identifier `" + ident->str().str() + "'"); + } + PosMap::iterator pi = pos.find(decl); + if (pi == pos.end()) { + // new id + scopes.push(true); + run(env, decl); + scopes.pop(); + } else { + // previously seen, check if circular + if (pi->second == -1) { + GCLock lock; + throw TypeError(env, loc, "circular definition of `" + ident->str().str() + "'"); + } + } + return decl; +} + +VarDecl* TopoSorter::checkId(EnvI& env, const ASTString& id_v, const Location& loc) { + GCLock lock; + Id* id = new Id(loc, id_v, NULL); + return checkId(env, id, loc); +} + +void TopoSorter::run(EnvI& env, Expression* e) { + if (e == NULL) return; + switch (e->eid()) { + case Expression::E_INTLIT: + case Expression::E_FLOATLIT: + case Expression::E_BOOLLIT: + case Expression::E_STRINGLIT: + case Expression::E_ANON: + break; + case Expression::E_SETLIT: { + SetLit* sl = e->cast(); + if (sl->isv() == NULL && sl->fsv() == NULL) + for (unsigned int i = 0; i < sl->v().size(); i++) run(env, sl->v()[i]); + } break; + case Expression::E_ID: { + if (e != constants().absent) { + VarDecl* vd = checkId(env, e->cast(), e->loc()); + e->cast()->decl(vd); + } + } break; + case Expression::E_ARRAYLIT: { + ArrayLit* al = e->cast(); + for (unsigned int i = 0; i < al->size(); i++) run(env, (*al)[i]); + } break; + case Expression::E_ARRAYACCESS: { + ArrayAccess* ae = e->cast(); + run(env, ae->v()); + for (unsigned int i = 0; i < ae->idx().size(); i++) run(env, ae->idx()[i]); + } break; + case Expression::E_COMP: { + Comprehension* ce = e->cast(); + scopes.push(false); + for (int i = 0; i < ce->n_generators(); i++) { + run(env, ce->in(i)); + for (int j = 0; j < ce->n_decls(i); j++) { + run(env, ce->decl(i, j)); + scopes.add(env, ce->decl(i, j)); + } + if (ce->where(i)) run(env, ce->where(i)); + } + run(env, ce->e()); + scopes.pop(); + } break; + case Expression::E_ITE: { + ITE* ite = e->cast(); + for (int i = 0; i < ite->size(); i++) { + run(env, ite->e_if(i)); + run(env, ite->e_then(i)); + } + run(env, ite->e_else()); + } break; + case Expression::E_BINOP: { + BinOp* be = e->cast(); + std::vector todo; + todo.push_back(be->lhs()); + todo.push_back(be->rhs()); + while (!todo.empty()) { + Expression* e = todo.back(); + todo.pop_back(); + if (BinOp* e_bo = e->dyn_cast()) { + todo.push_back(e_bo->lhs()); + todo.push_back(e_bo->rhs()); + for (ExpressionSetIter it = e_bo->ann().begin(); it != e_bo->ann().end(); ++it) + run(env, *it); + } else { + run(env, e); + } + } + } break; + case Expression::E_UNOP: { + UnOp* ue = e->cast(); + run(env, ue->e()); + } break; + case Expression::E_CALL: { + Call* ce = e->cast(); + for (unsigned int i = 0; i < ce->n_args(); i++) run(env, ce->arg(i)); + } break; + case Expression::E_VARDECL: { + VarDecl* ve = e->cast(); + PosMap::iterator pi = pos.find(ve); + if (pi == pos.end()) { + pos.insert(std::pair(ve, -1)); + run(env, ve->ti()); + run(env, ve->e()); + ve->payload(static_cast(decls.size())); + decls.push_back(ve); + pi = pos.find(ve); + pi->second = static_cast(decls.size()) - 1; + } else { + assert(pi->second != -1); + } + } break; + case Expression::E_TI: { + TypeInst* ti = e->cast(); + for (unsigned int i = 0; i < ti->ranges().size(); i++) run(env, ti->ranges()[i]); + run(env, ti->domain()); + } break; + case Expression::E_TIID: + break; + case Expression::E_LET: { + Let* let = e->cast(); + scopes.push(false); + for (unsigned int i = 0; i < let->let().size(); i++) { + run(env, let->let()[i]); + if (VarDecl* vd = let->let()[i]->dyn_cast()) { + scopes.add(env, vd); + } + } + run(env, let->in()); + VarDeclCmp poscmp(pos); + std::stable_sort(let->let().begin(), let->let().end(), poscmp); + for (unsigned int i = 0; i < let->let().size(); i++) { + if (VarDecl* vd = let->let()[i]->dyn_cast()) { + let->let_orig()[i] = vd->e(); + } else { + let->let_orig()[i] = NULL; + } + } + scopes.pop(); + } break; + } + if (env.ignoreUnknownIds) { + std::vector toDelete; + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) { + try { + run(env, *it); + } catch (TypeError&) { + toDelete.push_back(*it); + } + for (Expression* de : toDelete) e->ann().remove(de); + } + } else { + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) { + run(env, *it); + } + } +} + +KeepAlive addCoercion(EnvI& env, Model* m, Expression* e, const Type& funarg_t) { + if (e->isa() && e->type().dim() > 0) { + ArrayAccess* aa = e->cast(); + // Turn ArrayAccess into a slicing operation + std::vector slice; + slice.reserve(aa->idx().size()); + std::vector ndims; + GCLock lock; + for (unsigned int i = 0; i < aa->idx().size(); i++) { + if (aa->idx()[i]->type().is_set()) { + bool needIdxSet = true; + bool needInter = true; + if (SetLit* sl = aa->idx()[i]->dyn_cast()) { + if (sl->isv() && sl->isv()->size() == 1) { + if (sl->isv()->min().isFinite() && sl->isv()->max().isFinite()) { + ndims.push_back(sl); + needIdxSet = false; + } else if (sl->isv()->min() == -IntVal::infinity() && + sl->isv()->max() == IntVal::infinity()) { + needInter = false; + } + } + } + if (needIdxSet) { + std::ostringstream oss; + oss << "index_set"; + if (aa->idx().size() > 1) { + oss << "_" << (i + 1) << "of" << aa->idx().size(); + } + std::vector origIdxsetArgs(1); + origIdxsetArgs[0] = aa->v(); + Call* origIdxset = new Call(aa->v()->loc(), ASTString(oss.str()), origIdxsetArgs); + FunctionI* fi = m->matchFn(env, origIdxset, false); + if (!fi) throw TypeError(env, e->loc(), "missing builtin " + oss.str()); + origIdxset->type(fi->rtype(env, origIdxsetArgs, false)); + origIdxset->decl(fi); + if (needInter) { + BinOp* inter = new BinOp(aa->idx()[i]->loc(), aa->idx()[i], BOT_INTERSECT, origIdxset); + inter->type(Type::parsetint()); + ndims.push_back(inter); + } else { + ndims.push_back(origIdxset); + } + } + slice.push_back(aa->idx()[i]); + } else { + BinOp* bo = new BinOp(aa->idx()[i]->loc(), aa->idx()[i], BOT_DOTDOT, aa->idx()[i]); + bo->type(Type::parsetint()); + slice.push_back(bo); + } + } + ArrayLit* a_slice = new ArrayLit(e->loc().introduce(), slice); + a_slice->type(Type::parsetint(1)); + ArrayLit* a_ndims = new ArrayLit(e->loc().introduce(), ndims); + a_ndims->type(Type::parsetint(1)); + std::vector args = {aa->v(), a_slice, a_ndims}; + Call* c = new Call(e->loc(), ASTString("slice_Xd"), args); + FunctionI* fi = m->matchFn(env, c, false); + if (!fi) throw TypeError(env, e->loc(), "missing builtin slice_Xd"); + c->type(fi->rtype(env, args, false)); + c->decl(fi); + e = c; + } + if (e->type().dim() == funarg_t.dim() && + (funarg_t.bt() == Type::BT_BOT || funarg_t.bt() == Type::BT_TOP || + e->type().bt() == funarg_t.bt() || e->type().bt() == Type::BT_BOT)) + return e; + std::vector args(1); + args[0] = e; + GCLock lock; + Call* c = NULL; + if (e->type().dim() == 0 && funarg_t.dim() != 0) { + if (e->type().isvar()) { + throw TypeError(env, e->loc(), "cannot coerce var set into array"); + } + if (e->type().isopt()) { + throw TypeError(env, e->loc(), "cannot coerce opt set into array"); + } + std::vector set2a_args(1); + set2a_args[0] = e; + Call* set2a = new Call(e->loc(), ASTString("set2array"), set2a_args); + FunctionI* fi = m->matchFn(env, set2a, false); + if (fi) { + set2a->type(fi->rtype(env, args, false)); + set2a->decl(fi); + e = set2a; + } + } + if (funarg_t.bt() == Type::BT_TOP || e->type().bt() == funarg_t.bt() || + e->type().bt() == Type::BT_BOT) { + KeepAlive ka(e); + return ka; + } + if (e->type().bt() == Type::BT_BOOL) { + if (funarg_t.bt() == Type::BT_INT) { + c = new Call(e->loc(), constants().ids.bool2int, args); + } else if (funarg_t.bt() == Type::BT_FLOAT) { + c = new Call(e->loc(), constants().ids.bool2float, args); + } + } else if (e->type().bt() == Type::BT_INT) { + if (funarg_t.bt() == Type::BT_FLOAT) { + c = new Call(e->loc(), constants().ids.int2float, args); + } + } + if (c) { + FunctionI* fi = m->matchFn(env, c, false); + assert(fi); + c->type(fi->rtype(env, args, false)); + c->decl(fi); + KeepAlive ka(c); + return ka; + } + throw TypeError(env, e->loc(), + "cannot determine coercion from type " + e->type().toString(env) + " to type " + + funarg_t.toString(env)); +} +KeepAlive addCoercion(EnvI& env, Model* m, Expression* e, Expression* funarg) { + return addCoercion(env, m, e, funarg->type()); +} + +template +class Typer { +public: + EnvI& _env; + Model* _model; + std::vector& _typeErrors; + bool _ignoreUndefined; + Typer(EnvI& env, Model* model, std::vector& typeErrors, bool ignoreUndefined) + : _env(env), _model(model), _typeErrors(typeErrors), _ignoreUndefined(ignoreUndefined) {} + /// Check annotations when expression is finished + void exit(Expression* e) { + for (ExpressionSetIter it = e->ann().begin(); it != e->ann().end(); ++it) + if (!(*it)->type().isann()) + throw TypeError(_env, (*it)->loc(), + "expected annotation, got `" + (*it)->type().toString(_env) + "'"); + } + bool enter(Expression*) { return true; } + /// Visit integer literal + void vIntLit(const IntLit&) {} + /// Visit floating point literal + void vFloatLit(const FloatLit&) {} + /// Visit Boolean literal + void vBoolLit(const BoolLit&) {} + /// Visit set literal + void vSetLit(SetLit& sl) { + Type ty; + ty.st(Type::ST_SET); + if (sl.isv()) { + ty.bt(Type::BT_INT); + ty.enumId(sl.type().enumId()); + sl.type(ty); + return; + } + unsigned int enumId = sl.v().size() > 0 ? sl.v()[0]->type().enumId() : 0; + for (unsigned int i = 0; i < sl.v().size(); i++) { + if (sl.v()[i]->type().dim() > 0) + throw TypeError(_env, sl.v()[i]->loc(), "set literals cannot contain arrays"); + if (sl.v()[i]->type().isvar()) ty.ti(Type::TI_VAR); + if (sl.v()[i]->type().isopt()) + throw TypeError(_env, sl.v()[i]->loc(), "set literals cannot contain option type values"); + if (sl.v()[i]->type().cv()) ty.cv(true); + if (enumId != sl.v()[i]->type().enumId()) enumId = 0; + if (!Type::bt_subtype(sl.v()[i]->type(), ty, true)) { + if (ty.bt() == Type::BT_UNKNOWN || Type::bt_subtype(ty, sl.v()[i]->type(), true)) { + ty.bt(sl.v()[i]->type().bt()); + } else { + throw TypeError(_env, sl.loc(), "non-uniform set literal"); + } + } + } + ty.enumId(enumId); + if (ty.bt() == Type::BT_UNKNOWN) { + ty.bt(Type::BT_BOT); + } else { + if (ty.isvar() && ty.bt() != Type::BT_INT) { + if (ty.bt() == Type::BT_BOOL) + ty.bt(Type::BT_INT); + else + throw TypeError(_env, sl.loc(), "cannot coerce set literal element to var int"); + } + for (unsigned int i = 0; i < sl.v().size(); i++) { + sl.v()[i] = addCoercion(_env, _model, sl.v()[i], ty)(); + } + } + sl.type(ty); + } + /// Visit string literal + void vStringLit(const StringLit&) {} + /// Visit identifier + void vId(Id& id) { + if (&id != constants().absent) { + assert(!id.decl()->type().isunknown()); + id.type(id.decl()->type()); + } + } + /// Visit anonymous variable + void vAnonVar(const AnonVar&) {} + /// Visit array literal + void vArrayLit(ArrayLit& al) { + Type ty; + ty.dim(al.dims()); + std::vector anons; + bool haveAbsents = false; + bool haveInferredType = false; + for (unsigned int i = 0; i < al.size(); i++) { + Expression* vi = al[i]; + if (vi->type().dim() > 0) + throw TypeError(_env, vi->loc(), "arrays cannot be elements of arrays"); + if (vi == constants().absent) haveAbsents = true; + AnonVar* av = vi->dyn_cast(); + if (av) { + ty.ti(Type::TI_VAR); + anons.push_back(av); + } else if (vi->type().isvar()) { + ty.ti(Type::TI_VAR); + } + if (vi->type().cv()) ty.cv(true); + if (vi->type().isopt()) { + ty.ot(Type::OT_OPTIONAL); + } + + if (ty.bt() == Type::BT_UNKNOWN) { + if (av == NULL) { + if (haveInferredType) { + if (ty.st() != vi->type().st() && vi->type().ot() != Type::OT_OPTIONAL) { + throw TypeError(_env, al.loc(), "non-uniform array literal"); + } + } else { + haveInferredType = true; + ty.st(vi->type().st()); + } + if (vi->type().bt() != Type::BT_BOT) { + ty.bt(vi->type().bt()); + ty.enumId(vi->type().enumId()); + } + } + } else { + if (av == NULL) { + if (vi->type().bt() == Type::BT_BOT) { + if (vi->type().st() != ty.st() && vi->type().ot() != Type::OT_OPTIONAL) { + throw TypeError(_env, al.loc(), "non-uniform array literal"); + } + if (vi->type().enumId() != 0 && ty.enumId() != vi->type().enumId()) { + ty.enumId(0); + } + } else { + unsigned int tyEnumId = ty.enumId(); + ty.enumId(vi->type().enumId()); + if (Type::bt_subtype(ty, vi->type(), true)) { + ty.bt(vi->type().bt()); + } + if (tyEnumId != vi->type().enumId()) ty.enumId(0); + if (!Type::bt_subtype(vi->type(), ty, true) || ty.st() != vi->type().st()) { + throw TypeError(_env, al.loc(), "non-uniform array literal"); + } + } + } + } + } + if (ty.bt() == Type::BT_UNKNOWN) { + ty.bt(Type::BT_BOT); + if (!anons.empty()) + throw TypeError(_env, al.loc(), + "array literal must contain at least one non-anonymous variable"); + if (haveAbsents) + throw TypeError(_env, al.loc(), "array literal must contain at least one non-absent value"); + } else { + Type at = ty; + at.dim(0); + if (at.ti() == Type::TI_VAR && at.st() == Type::ST_SET && at.bt() != Type::BT_INT) { + if (at.bt() == Type::BT_BOOL) { + ty.bt(Type::BT_INT); + at.bt(Type::BT_INT); + } else { + throw TypeError(_env, al.loc(), "cannot coerce array element to var set of int"); + } + } + for (unsigned int i = 0; i < anons.size(); i++) { + anons[i]->type(at); + } + for (unsigned int i = 0; i < al.size(); i++) { + al.set(i, addCoercion(_env, _model, al[i], at)()); + } + } + if (ty.enumId() != 0) { + std::vector enumIds(ty.dim() + 1); + for (int i = 0; i < ty.dim(); i++) enumIds[i] = 0; + enumIds[ty.dim()] = ty.enumId(); + ty.enumId(_env.registerArrayEnum(enumIds)); + } + al.type(ty); + } + /// Visit array access + void vArrayAccess(ArrayAccess& aa) { + if (aa.v()->type().dim() == 0) { + if (aa.v()->type().st() == Type::ST_SET) { + Type tv = aa.v()->type(); + tv.st(Type::ST_PLAIN); + tv.dim(1); + aa.v(addCoercion(_env, _model, aa.v(), tv)()); + } else { + std::ostringstream oss; + oss << "array access attempted on expression of type `" << aa.v()->type().toString(_env) + << "'"; + throw TypeError(_env, aa.v()->loc(), oss.str()); + } + } else if (aa.v()->isa()) { + aa.v(addCoercion(_env, _model, aa.v(), aa.v()->type())()); + } + if (aa.v()->type().dim() != aa.idx().size()) { + std::ostringstream oss; + oss << aa.v()->type().dim() << "-dimensional array accessed with " << aa.idx().size() + << (aa.idx().size() == 1 ? " expression" : " expressions"); + throw TypeError(_env, aa.v()->loc(), oss.str()); + } + Type tt = aa.v()->type(); + if (tt.enumId() != 0) { + const std::vector& arrayEnumIds = _env.getArrayEnum(tt.enumId()); + + for (unsigned int i = 0; i < arrayEnumIds.size() - 1; i++) { + Expression* aai = aa.idx()[i]; + // Check if index is slice operator, and convert to correct enum type + if (SetLit* aai_sl = aai->dyn_cast()) { + if (IntSetVal* aai_isv = aai_sl->isv()) { + if (aai_isv->min() == -IntVal::infinity() && aai_isv->max() == IntVal::infinity()) { + Type aai_sl_t = aai_sl->type(); + aai_sl_t.enumId(arrayEnumIds[i]); + aai_sl->type(aai_sl_t); + } + } + } else if (BinOp* aai_bo = aai->dyn_cast()) { + if (aai_bo->op() == BOT_DOTDOT) { + Type aai_bo_t = aai_bo->type(); + if (IntLit* il = aai_bo->lhs()->dyn_cast()) { + if (il->v() == -IntVal::infinity()) { + // Expression is ..X, so result gets enum type of X + aai_bo_t.enumId(aai_bo->rhs()->type().enumId()); + } + } else if (IntLit* il = aai_bo->rhs()->dyn_cast()) { + if (il->v() == IntVal::infinity()) { + // Expression is X.., so result gets enum type of X + aai_bo_t.enumId(aai_bo->lhs()->type().enumId()); + } + } + aai_bo->type(aai_bo_t); + } + } + + if (arrayEnumIds[i] != 0) { + if (aa.idx()[i]->type().enumId() != arrayEnumIds[i]) { + std::ostringstream oss; + oss << "array index "; + if (aa.idx().size() > 1) { + oss << (i + 1) << " "; + } + oss << "must be `" << _env.getEnum(arrayEnumIds[i])->e()->id()->str().str() + << "', but is `" << aa.idx()[i]->type().toString(_env) << "'"; + throw TypeError(_env, aa.loc(), oss.str()); + } + } + } + tt.enumId(arrayEnumIds[arrayEnumIds.size() - 1]); + } + int n_dimensions = 0; + bool isVarAccess = false; + bool isSlice = false; + for (unsigned int i = 0; i < aa.idx().size(); i++) { + Expression* aai = aa.idx()[i]; + if (aai->isa()) { + aai->type(Type::varint()); + } + if ((aai->type().bt() != Type::BT_INT && aai->type().bt() != Type::BT_BOOL) || + aai->type().dim() != 0) { + throw TypeError(_env, aa.loc(), + "array index must be `int' or `set of int', but is `" + + aai->type().toString(_env) + "'"); + } + if (aai->type().is_set()) { + if (isVarAccess || aai->type().isvar()) { + throw TypeError(_env, aa.loc(), + "array slicing with variable range or index not supported"); + } + isSlice = true; + aa.idx()[i] = addCoercion(_env, _model, aai, Type::varsetint())(); + n_dimensions++; + } else { + aa.idx()[i] = addCoercion(_env, _model, aai, Type::varint())(); + } + + if (aai->type().isopt()) { + tt.ot(Type::OT_OPTIONAL); + } + if (aai->type().isvar()) { + isVarAccess = true; + if (isSlice) { + throw TypeError(_env, aa.loc(), + "array slicing with variable range or index not supported"); + } + tt.ti(Type::TI_VAR); + if (tt.bt() == Type::BT_ANN || tt.bt() == Type::BT_STRING) { + throw TypeError(_env, aai->loc(), + std::string("array access using a variable not supported for array of ") + + (tt.bt() == Type::BT_ANN ? "ann" : "string")); + } + } + tt.dim(n_dimensions); + if (aai->type().cv()) tt.cv(true); + } + aa.type(tt); + } + /// Visit array comprehension + void vComprehension(Comprehension& c) { + Type tt = c.e()->type(); + typedef std::unordered_map > genMap_t; + typedef std::unordered_map > whereMap_t; + genMap_t generatorMap; + whereMap_t whereMap; + int declCount = 0; + bool didMoveWheres = false; + for (int i = 0; i < c.n_generators(); i++) { + for (int j = 0; j < c.n_decls(i); j++) { + generatorMap[c.decl(i, j)] = std::pair(i, declCount++); + whereMap[c.decl(i, j)] = std::vector(); + } + Expression* g_in = c.in(i); + if (g_in) { + const Type& ty_in = g_in->type(); + if (ty_in == Type::varsetint()) { + tt.ot(Type::OT_OPTIONAL); + tt.ti(Type::TI_VAR); + tt.cv(true); + } + if (ty_in.cv()) tt.cv(true); + if (c.where(i)) { + if (c.where(i)->type() == Type::varbool()) { + tt.ot(Type::OT_OPTIONAL); + tt.ti(Type::TI_VAR); + tt.cv(true); + } else if (c.where(i)->type() != Type::parbool()) { + throw TypeError( + _env, c.where(i)->loc(), + "where clause must be bool, but is `" + c.where(i)->type().toString(_env) + "'"); + } + if (c.where(i)->type().cv()) tt.cv(true); + + // Try to move parts of the where clause to earlier generators + std::vector wherePartsStack; + std::vector whereParts; + wherePartsStack.push_back(c.where(i)); + while (!wherePartsStack.empty()) { + Expression* e = wherePartsStack.back(); + wherePartsStack.pop_back(); + if (BinOp* bo = e->dyn_cast()) { + if (bo->op() == BOT_AND) { + wherePartsStack.push_back(bo->rhs()); + wherePartsStack.push_back(bo->lhs()); + } else { + whereParts.push_back(e); + } + } else { + whereParts.push_back(e); + } + } + + for (unsigned int wpi = 0; wpi < whereParts.size(); wpi++) { + Expression* wp = whereParts[wpi]; + class FindLatestGen : public EVisitor { + public: + int decl_idx; + VarDecl* decl; + const genMap_t& generatorMap; + Comprehension* comp; + FindLatestGen(const genMap_t& generatorMap0, Comprehension* comp0) + : decl_idx(-1), + decl(comp0->decl(0, 0)), + generatorMap(generatorMap0), + comp(comp0) {} + void vId(const Id& ident) { + genMap_t::const_iterator it = generatorMap.find(ident.decl()); + if (it != generatorMap.end() && it->second.second > decl_idx) { + decl_idx = it->second.second; + decl = ident.decl(); + int gen = it->second.first; + while (comp->in(gen) == NULL && gen < comp->n_generators() - 1) { + decl_idx++; + gen++; + decl = comp->decl(gen, 0); + } + } + } + } flg(generatorMap, &c); + topDown(flg, wp); + whereMap[flg.decl].push_back(wp); + + if (flg.decl_idx < declCount - 1) didMoveWheres = true; + } + } + } else { + assert(c.where(i) != NULL); + whereMap[c.decl(i, 0)].push_back(c.where(i)); + } + } + + if (didMoveWheres) { + Generators generators; + for (int i = 0; i < c.n_generators(); i++) { + std::vector decls; + for (int j = 0; j < c.n_decls(i); j++) { + decls.push_back(c.decl(i, j)); + if (whereMap[c.decl(i, j)].size() != 0) { + // need a generator for all the decls up to this point + Expression* whereExpr = whereMap[c.decl(i, j)][0]; + for (unsigned int k = 1; k < whereMap[c.decl(i, j)].size(); k++) { + GCLock lock; + BinOp* bo = + new BinOp(Location().introduce(), whereExpr, BOT_AND, whereMap[c.decl(i, j)][k]); + Type bo_t = whereMap[c.decl(i, j)][k]->type().ispar() && whereExpr->type().ispar() + ? Type::parbool() + : Type::varbool(); + bo->type(bo_t); + whereExpr = bo; + } + generators._g.push_back(Generator(decls, c.in(i), whereExpr)); + decls.clear(); + } else if (j == c.n_decls(i) - 1) { + generators._g.push_back(Generator(decls, c.in(i), NULL)); + decls.clear(); + } + } + } + GCLock lock; + c.init(c.e(), generators); + } + + if (c.set()) { + if (c.e()->type().dim() != 0 || c.e()->type().st() == Type::ST_SET) + throw TypeError(_env, c.e()->loc(), + "set comprehension expression must be scalar, but is `" + + c.e()->type().toString(_env) + "'"); + tt.st(Type::ST_SET); + if (tt.isvar()) { + c.e(addCoercion(_env, _model, c.e(), Type::varint())()); + tt.bt(Type::BT_INT); + } + } else { + if (c.e()->type().dim() != 0) + throw TypeError(_env, c.e()->loc(), "array comprehension expression cannot be an array"); + tt.dim(1); + if (tt.enumId() != 0) { + std::vector enumIds(2); + enumIds[0] = 0; + enumIds[1] = tt.enumId(); + tt.enumId(_env.registerArrayEnum(enumIds)); + } + } + c.type(tt); + } + /// Visit array comprehension generator + void vComprehensionGenerator(Comprehension& c, int gen_i) { + Expression* g_in = c.in(gen_i); + if (g_in == NULL) { + // This is an "assignment generator" (i = expr) + assert(c.where(gen_i) != NULL); + assert(c.n_decls(gen_i) == 1); + const Type& ty_where = c.where(gen_i)->type(); + c.decl(gen_i, 0)->type(ty_where); + c.decl(gen_i, 0)->ti()->type(ty_where); + } else { + const Type& ty_in = g_in->type(); + if (ty_in != Type::varsetint() && ty_in != Type::parsetint() && ty_in.dim() != 1) { + throw TypeError(_env, g_in->loc(), + "generator expression must be (par or var) set of int or one-dimensional " + "array, but is `" + + ty_in.toString(_env) + "'"); + } + Type ty_id; + bool needIntLit = false; + if (ty_in.dim() == 0) { + ty_id = Type::parint(); + ty_id.enumId(ty_in.enumId()); + needIntLit = true; + } else { + ty_id = ty_in; + if (ty_in.enumId() != 0) { + const std::vector& enumIds = _env.getArrayEnum(ty_in.enumId()); + ty_id.enumId(enumIds.back()); + } + ty_id.dim(0); + } + for (int j = 0; j < c.n_decls(gen_i); j++) { + if (needIntLit) { + GCLock lock; + c.decl(gen_i, j)->e(IntLit::aEnum(0, ty_id.enumId())); + } + c.decl(gen_i, j)->type(ty_id); + c.decl(gen_i, j)->ti()->type(ty_id); + } + } + } + /// Visit if-then-else + void vITE(ITE& ite) { + bool mustBeBool = false; + if (ite.e_else() == NULL) { + // this is an "if then endif" so the must be bool + ite.e_else(constants().boollit(true)); + mustBeBool = true; + } + Type tret = ite.e_else()->type(); + std::vector anons; + bool allpar = !(tret.isvar()); + if (tret.isunknown()) { + if (AnonVar* av = ite.e_else()->dyn_cast()) { + allpar = false; + anons.push_back(av); + } else { + throw TypeError(_env, ite.e_else()->loc(), + "cannot infer type of expression in `else' branch of conditional"); + } + } + bool allpresent = !(tret.isopt()); + bool varcond = false; + for (int i = 0; i < ite.size(); i++) { + Expression* eif = ite.e_if(i); + Expression* ethen = ite.e_then(i); + varcond = varcond || (eif->type() == Type::varbool()); + if (eif->type() != Type::parbool() && eif->type() != Type::varbool()) + throw TypeError( + _env, eif->loc(), + "expected bool conditional expression, got `" + eif->type().toString(_env) + "'"); + if (eif->type().cv()) tret.cv(true); + if (ethen->type().isunknown()) { + if (AnonVar* av = ethen->dyn_cast()) { + allpar = false; + anons.push_back(av); + } else { + throw TypeError(_env, ethen->loc(), + "cannot infer type of expression in `then' branch of conditional"); + } + } else { + if (tret.isbot() || tret.isunknown()) tret.bt(ethen->type().bt()); + if (mustBeBool && + (ethen->type().bt() != Type::BT_BOOL || ethen->type().dim() > 0 || + ethen->type().st() != Type::ST_PLAIN || ethen->type().ot() != Type::OT_PRESENT)) { + throw TypeError(_env, ite.loc(), + std::string("conditional without `else' branch must have bool type, ") + + "but `then' branch has type `" + ethen->type().toString(_env) + "'"); + } + if ((!ethen->type().isbot() && !Type::bt_subtype(ethen->type(), tret, true) && + !Type::bt_subtype(tret, ethen->type(), true)) || + ethen->type().st() != tret.st() || ethen->type().dim() != tret.dim()) { + throw TypeError(_env, ethen->loc(), + "type mismatch in branches of conditional. `then' branch has type `" + + ethen->type().toString(_env) + "', but `else' branch has type `" + + tret.toString(_env) + "'"); + } + if (Type::bt_subtype(tret, ethen->type(), true)) { + tret.bt(ethen->type().bt()); + } + if (ethen->type().isvar()) allpar = false; + if (ethen->type().isopt()) allpresent = false; + if (ethen->type().cv()) tret.cv(true); + } + } + Type tret_var(tret); + tret_var.ti(Type::TI_VAR); + for (unsigned int i = 0; i < anons.size(); i++) { + anons[i]->type(tret_var); + } + for (int i = 0; i < ite.size(); i++) { + ite.e_then(i, addCoercion(_env, _model, ite.e_then(i), tret)()); + } + ite.e_else(addCoercion(_env, _model, ite.e_else(), tret)()); + /// TODO: perhaps extend flattener to array types, but for now throw an error + if (varcond && tret.dim() > 0) + throw TypeError(_env, ite.loc(), "conditional with var condition cannot have array type"); + if (varcond || !allpar) tret.ti(Type::TI_VAR); + if (!allpresent) tret.ot(Type::OT_OPTIONAL); + ite.type(tret); + } + /// Visit binary operator + void vBinOp(BinOp& bop) { + std::vector args(2); + args[0] = bop.lhs(); + args[1] = bop.rhs(); + if (FunctionI* fi = _model->matchFn(_env, bop.opToString(), args, true)) { + bop.lhs(addCoercion(_env, _model, bop.lhs(), fi->argtype(_env, args, 0))()); + bop.rhs(addCoercion(_env, _model, bop.rhs(), fi->argtype(_env, args, 1))()); + args[0] = bop.lhs(); + args[1] = bop.rhs(); + Type ty = fi->rtype(_env, args, true); + ty.cv(bop.lhs()->type().cv() || bop.rhs()->type().cv()); + bop.type(ty); + + if (fi->e()) + bop.decl(fi); + else + bop.decl(NULL); + } else { + throw TypeError(_env, bop.loc(), + std::string("type error in operator application for `") + + bop.opToString().str() + + "'. No matching operator found with left-hand side type `" + + bop.lhs()->type().toString(_env) + "' and right-hand side type `" + + bop.rhs()->type().toString(_env) + "'"); + } + } + /// Visit unary operator + void vUnOp(UnOp& uop) { + std::vector args(1); + args[0] = uop.e(); + if (FunctionI* fi = _model->matchFn(_env, uop.opToString(), args, true)) { + uop.e(addCoercion(_env, _model, uop.e(), fi->argtype(_env, args, 0))()); + args[0] = uop.e(); + Type ty = fi->rtype(_env, args, true); + ty.cv(uop.e()->type().cv()); + uop.type(ty); + if (fi->e()) uop.decl(fi); + } else { + throw TypeError(_env, uop.loc(), + std::string("type error in operator application for `") + + uop.opToString().str() + "'. No matching operator found with type `" + + uop.e()->type().toString(_env) + "'"); + } + } + static std::string createEnumToStringName(Id* ident, std::string prefix) { + std::string name = ident->str().str(); + if (name[0] == '\'') { + name = "'" + prefix + name.substr(1); + } else { + name = prefix + name; + } + return name; + } + + /// Visit call + void vCall(Call& call) { + std::vector args(call.n_args()); + for (unsigned int i = static_cast(args.size()); i--;) args[i] = call.arg(i); + if (FunctionI* fi = _model->matchFn(_env, call.id(), args, true)) { + bool cv = false; + for (unsigned int i = 0; i < args.size(); i++) { + if (Comprehension* c = call.arg(i)->dyn_cast()) { + Type t_before = c->e()->type(); + Type t = fi->argtype(_env, args, i); + t.dim(0); + c->e(addCoercion(_env, _model, c->e(), t)()); + Type t_after = c->e()->type(); + if (t_before != t_after) { + Type ct = c->type(); + ct.bt(t_after.bt()); + c->type(ct); + } + } else { + args[i] = addCoercion(_env, _model, call.arg(i), fi->argtype(_env, args, i))(); + call.arg(i, args[i]); + } + cv = cv || args[i]->type().cv(); + } + // Replace par enums with their string versions + if (call.id() == "format" || call.id() == "show" || call.id() == "showDzn" || + call.id() == "showJSON") { + if (call.arg(call.n_args() - 1)->type().ispar()) { + int enumId = call.arg(call.n_args() - 1)->type().enumId(); + if (enumId != 0 && call.arg(call.n_args() - 1)->type().dim() != 0) { + const std::vector& enumIds = _env.getArrayEnum(enumId); + enumId = enumIds[enumIds.size() - 1]; + } + if (enumId > 0) { + VarDecl* enumDecl = _env.getEnum(enumId)->e(); + if (enumDecl->e()) { + Id* ti_id = _env.getEnum(enumId)->e()->id(); + GCLock lock; + std::vector args(3); + args[0] = call.arg(call.n_args() - 1); + if (args[0]->type().dim() > 1) { + std::vector a1dargs(1); + a1dargs[0] = args[0]; + Call* array1d = new Call(Location().introduce(), ASTString("array1d"), a1dargs); + Type array1dt = args[0]->type(); + array1dt.dim(1); + array1d->type(array1dt); + args[0] = array1d; + } + args[1] = constants().boollit(call.id() == "showDzn"); + args[2] = constants().boollit(call.id() == "showJSON"); + ASTString enumName(createEnumToStringName(ti_id, "_toString_")); + call.id(enumName); + call.args(args); + if (call.id() == "showDzn") { + call.id(constants().ids.show); + } + fi = _model->matchFn(_env, &call, false); + if (fi == NULL) { + std::ostringstream oss; + oss << "no function or predicate with this signature found: `"; + oss << call.id() << "("; + for (unsigned int i = 0; i < call.n_args(); i++) { + oss << call.arg(i)->type().toString(_env); + if (i < call.n_args() - 1) oss << ","; + } + oss << ")'"; + throw TypeError(_env, call.loc(), oss.str()); + } + } + } + } + } + + // Set type and decl + Type ty = fi->rtype(_env, args, true); + ty.cv(cv); + call.type(ty); + call.decl(fi); + } else { + std::ostringstream oss; + oss << "no function or predicate with this signature found: `"; + oss << call.id() << "("; + for (unsigned int i = 0; i < call.n_args(); i++) { + oss << call.arg(i)->type().toString(_env); + if (i < call.n_args() - 1) oss << ","; + } + oss << ")'"; + throw TypeError(_env, call.loc(), oss.str()); + } + } + /// Visit let + void vLet(Let& let) { + bool cv = false; + bool isVar = false; + for (unsigned int i = 0; i < let.let().size(); i++) { + Expression* li = let.let()[i]; + cv = cv || li->type().cv(); + if (VarDecl* vdi = li->dyn_cast()) { + if (vdi->e() == NULL && vdi->type().is_set() && vdi->type().isvar() && + vdi->ti()->domain() == NULL) { + _typeErrors.push_back( + TypeError(_env, vdi->loc(), + "set element type for `" + vdi->id()->str().str() + "' is not finite")); + } + if (vdi->type().ispar() && vdi->e() == NULL) + throw TypeError(_env, vdi->loc(), + "let variable `" + vdi->id()->v().str() + "' must be initialised"); + if (vdi->ti()->hasTiVariable()) { + _typeErrors.push_back( + TypeError(_env, vdi->loc(), + "type-inst variables not allowed in type-inst for let variable `" + + vdi->id()->str().str() + "'")); + } + let.let_orig()[i] = vdi->e(); + } + isVar |= li->type().isvar(); + } + Type ty = let.in()->type(); + ty.cv(cv); + if (isVar && ty.bt() == Type::BT_BOOL && ty.dim() == 0) ty.ti(Type::TI_VAR); + let.type(ty); + } + /// Visit variable declaration + void vVarDecl(VarDecl& vd) { + if (ignoreVarDecl) { + if (vd.e()) { + Type vdt = vd.ti()->type(); + Type vet = vd.e()->type(); + if (vdt.enumId() != 0 && vdt.dim() > 0 && + (vd.e()->isa() || vd.e()->isa() || + (vd.e()->isa() && vd.e()->cast()->op() == BOT_PLUSPLUS))) { + // Special case: index sets of array literals and comprehensions automatically + // coerce to any enum index set + const std::vector& enumIds = _env.getArrayEnum(vdt.enumId()); + if (enumIds[enumIds.size() - 1] == 0) { + vdt.enumId(0); + } else { + std::vector nEnumIds(enumIds.size()); + for (unsigned int i = 0; i < nEnumIds.size() - 1; i++) nEnumIds[i] = 0; + nEnumIds[nEnumIds.size() - 1] = enumIds[enumIds.size() - 1]; + vdt.enumId(_env.registerArrayEnum(nEnumIds)); + } + } else if (vd.ti()->isEnum() && vd.e()->isa()) { + if (vd.e()->cast()->id() == "anon_enum") { + vet.enumId(vdt.enumId()); + } + } + + if (vd.type().isunknown()) { + vd.ti()->type(vet); + vd.type(vet); + } else if (!_env.isSubtype(vet, vdt, true)) { + if (vet == Type::bot(1) && vd.e()->isa() && + vd.e()->cast()->size() == 0 && vdt.dim() != 0) { + // this is okay: assigning an empty array (one-dimensional) to an array variable + } else { + _typeErrors.push_back(TypeError(_env, vd.e()->loc(), + "initialisation value for `" + vd.id()->str().str() + + "' has invalid type-inst: expected `" + + vd.ti()->type().toString(_env) + "', actual `" + + vd.e()->type().toString(_env) + "'")); + } + } else { + vd.e(addCoercion(_env, _model, vd.e(), vd.ti()->type())()); + } + } else { + assert(!vd.type().isunknown()); + } + } else { + vd.type(vd.ti()->type()); + vd.id()->type(vd.type()); + } + } + /// Visit type inst + void vTypeInst(TypeInst& ti) { + Type tt = ti.type(); + bool foundEnum = ti.ranges().size() > 0 && ti.domain() && ti.domain()->type().enumId() != 0; + if (ti.ranges().size() > 0) { + bool foundTIId = false; + for (unsigned int i = 0; i < ti.ranges().size(); i++) { + TypeInst* ri = ti.ranges()[i]; + assert(ri != NULL); + if (ri->type().cv()) tt.cv(true); + if (ri->type().enumId() != 0) { + foundEnum = true; + } + if (ri->type() == Type::top()) { + // if (foundTIId) { + // throw TypeError(_env,ri->loc(), + // "only one type-inst variable allowed in array index"); + // } else { + foundTIId = true; + // } + } else if (ri->type() != Type::parint()) { + assert(ri->isa()); + TypeInst* riti = ri->cast(); + if (riti->domain()) { + throw TypeError(_env, ri->loc(), + "array index set expression has invalid type, expected `set of int', " + "actual `set of " + + ri->type().toString(_env) + "'"); + } else { + throw TypeError(_env, ri->loc(), + "cannot use `" + ri->type().toString(_env) + + "' as array index set (did you mean `int'?)"); + } + } + } + tt.dim(foundTIId ? -1 : ti.ranges().size()); + } + if (ti.domain() && ti.domain()->type().cv()) tt.cv(true); + if (ti.domain()) { + if (TIId* tiid = ti.domain()->dyn_cast()) { + if (tiid->isEnum()) { + tt.bt(Type::BT_INT); + } + } else { + if (ti.domain()->type().ti() != Type::TI_PAR || ti.domain()->type().st() != Type::ST_SET) + throw TypeError( + _env, ti.domain()->loc(), + "type-inst must be par set but is `" + ti.domain()->type().toString(_env) + "'"); + if (ti.domain()->type().dim() != 0) + throw TypeError(_env, ti.domain()->loc(), "type-inst cannot be an array"); + } + } + if (tt.isunknown() && ti.domain()) { + assert(ti.domain()); + switch (ti.domain()->type().bt()) { + case Type::BT_INT: + case Type::BT_FLOAT: + break; + case Type::BT_BOT: { + Type tidt = ti.domain()->type(); + tidt.bt(Type::BT_INT); + ti.domain()->type(tidt); + } break; + default: + throw TypeError(_env, ti.domain()->loc(), "type-inst must be int or float"); + } + tt.bt(ti.domain()->type().bt()); + tt.enumId(ti.domain()->type().enumId()); + } else { + // assert(ti.domain()==NULL || ti.domain()->isa()); + } + if (foundEnum) { + std::vector enumIds(ti.ranges().size() + 1); + for (unsigned int i = 0; i < ti.ranges().size(); i++) { + enumIds[i] = ti.ranges()[i]->type().enumId(); + } + enumIds[ti.ranges().size()] = ti.domain() ? ti.domain()->type().enumId() : 0; + int arrayEnumId = _env.registerArrayEnum(enumIds); + tt.enumId(arrayEnumId); + } + + if (tt.st() == Type::ST_SET && tt.ti() == Type::TI_VAR && tt.bt() != Type::BT_INT && + tt.bt() != Type::BT_TOP) + throw TypeError(_env, ti.loc(), "var set element types other than `int' not allowed"); + ti.type(tt); + } + void vTIId(TIId& id) {} +}; + +void typecheck(Env& env, Model* origModel, std::vector& typeErrors, + bool ignoreUndefinedParameters, bool allowMultiAssignment, bool isFlatZinc) { + Model* m; + if (!isFlatZinc && origModel == env.model()) { + // Combine all items into single model + Model* combinedModel = new Model; + class Combiner : public ItemVisitor { + public: + Model* m; + Combiner(Model* m0) : m(m0) {} + bool enter(Item* i) { + if (!i->isa()) m->addItem(i); + return true; + } + } _combiner(combinedModel); + iterItems(_combiner, origModel); + env.envi().orig_model = origModel; + env.envi().model = combinedModel; + m = combinedModel; + } else { + m = origModel; + } + + // Topological sorting + TopoSorter ts(m); + + std::vector functionItems; + std::vector assignItems; + Model* enumItems = new Model; + + class TSVFuns : public ItemVisitor { + public: + EnvI& env; + Model* model; + std::vector& fis; + TSVFuns(EnvI& env0, Model* model0, std::vector& fis0) + : env(env0), model(model0), fis(fis0) {} + void vFunctionI(FunctionI* i) { + model->registerFn(env, i); + fis.push_back(i); + } + } _tsvf(env.envi(), m, functionItems); + iterItems(_tsvf, m); + + class TSV0 : public ItemVisitor { + public: + EnvI& env; + TopoSorter& ts; + Model* model; + bool hadSolveItem; + std::vector& ais; + VarDeclI* objective; + Model* enumis; + TSV0(EnvI& env0, TopoSorter& ts0, Model* model0, std::vector& ais0, Model* enumis0) + : env(env0), + ts(ts0), + model(model0), + hadSolveItem(false), + ais(ais0), + objective(NULL), + enumis(enumis0) {} + void vAssignI(AssignI* i) { ais.push_back(i); } + void vVarDeclI(VarDeclI* i) { + ts.add(env, i, true, enumis); + // initialise new identifier counter to be larger than existing identifier + if (i->e()->id()->idn() >= 0) { + env.minId(i->e()->id()->idn()); + } else if (i->e()->id()->v().beginsWith("X_INTRODUCED_") && i->e()->id()->v().endsWith("_")) { + std::string numId = i->e()->id()->v().str().substr(std::string("X_INTRODUCED_").size()); + if (numId.size() > 0) { + numId = numId.substr(0, numId.size() - 1); + if (numId.size() > 0) { + int vId = -1; + try { + vId = std::stoi(numId); + } catch (std::exception&) { + } + if (vId >= 0) env.minId(vId); + } + } + } + } + void vSolveI(SolveI* si) { + if (hadSolveItem) throw TypeError(env, si->loc(), "Only one solve item allowed"); + hadSolveItem = true; + if (si->e()) { + GCLock lock; + TypeInst* ti = new TypeInst(Location().introduce(), Type()); + VarDecl* obj = new VarDecl(Location().introduce(), ti, "_objective", si->e()); + si->e(obj->id()); + objective = new VarDeclI(Location().introduce(), obj); + } + } + } _tsv0(env.envi(), ts, m, assignItems, enumItems); + iterItems(_tsv0, m); + if (_tsv0.objective) { + m->addItem(_tsv0.objective); + ts.add(env.envi(), _tsv0.objective, true, enumItems); + } + + for (unsigned int i = 0; i < enumItems->size(); i++) { + if (AssignI* ai = (*enumItems)[i]->dyn_cast()) { + assignItems.push_back(ai); + } else if (VarDeclI* vdi = (*enumItems)[i]->dyn_cast()) { + m->addItem(vdi); + ts.add(env.envi(), vdi, false, enumItems); + } else { + FunctionI* fi = (*enumItems)[i]->dyn_cast(); + m->addItem(fi); + m->registerFn(env.envi(), fi); + functionItems.push_back(fi); + } + } + + Model* enumItems2 = new Model; + + for (unsigned int i = 0; i < assignItems.size(); i++) { + AssignI* ai = assignItems[i]; + VarDecl* vd = NULL; + if (env.envi().ignoreUnknownIds) { + try { + vd = ts.get(env.envi(), ai->id(), ai->loc()); + } catch (TypeError&) { + } + } else { + vd = ts.get(env.envi(), ai->id(), ai->loc()); + } + if (vd) { + if (vd->e()) { + if (allowMultiAssignment) { + GCLock lock; + m->addItem(new ConstraintI( + ai->loc(), + new BinOp(ai->loc(), new Id(Location().introduce(), ai->id(), vd), BOT_EQ, ai->e()))); + } else { + throw TypeError(env.envi(), ai->loc(), "multiple assignment to the same variable"); + } + } else { + vd->e(ai->e()); + vd->ann().add(constants().ann.rhs_from_assignment); + if (vd->ti()->isEnum()) { + GCLock lock; + ASTString name(createEnumToStringName(vd->id(), "_enum_to_string_")); + VarDecl* vd_enum = ts.get(env.envi(), name, vd->loc()); + if (vd_enum->e()) + throw TypeError(env.envi(), ai->loc(), "multiple definition of the same enum"); + AssignI* ai_enum = + createEnumMapper(env.envi(), m, vd->ti()->type().enumId(), vd, vd_enum, enumItems2); + if (ai_enum) { + vd_enum->e(ai_enum->e()); + ai_enum->remove(); + } + } + } + } + ai->remove(); + } + + for (unsigned int i = 0; i < enumItems2->size(); i++) { + if (VarDeclI* vdi = (*enumItems2)[i]->dyn_cast()) { + m->addItem(vdi); + ts.add(env.envi(), vdi, false, enumItems); + } else { + FunctionI* fi = (*enumItems2)[i]->cast(); + m->addItem(fi); + m->registerFn(env.envi(), fi); + functionItems.push_back(fi); + } + } + + delete enumItems; + delete enumItems2; + + class TSV1 : public ItemVisitor { + public: + EnvI& env; + TopoSorter& ts; + TSV1(EnvI& env0, TopoSorter& ts0) : env(env0), ts(ts0) {} + void vVarDeclI(VarDeclI* i) { ts.run(env, i->e()); } + void vAssignI(AssignI* i) {} + void vConstraintI(ConstraintI* i) { ts.run(env, i->e()); } + void vSolveI(SolveI* i) { + for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) ts.run(env, *it); + ts.run(env, i->e()); + } + void vOutputI(OutputI* i) { ts.run(env, i->e()); } + void vFunctionI(FunctionI* fi) { + ts.run(env, fi->ti()); + for (unsigned int i = 0; i < fi->params().size(); i++) ts.run(env, fi->params()[i]); + for (ExpressionSetIter it = fi->ann().begin(); it != fi->ann().end(); ++it) ts.run(env, *it); + ts.scopes.push(false); + for (unsigned int i = 0; i < fi->params().size(); i++) ts.scopes.add(env, fi->params()[i]); + ts.run(env, fi->e()); + ts.scopes.pop(); + } + } _tsv1(env.envi(), ts); + iterItems(_tsv1, m); + + m->sortFn(); + + { + struct SortByPayload { + bool operator()(Item* i0, Item* i1) { + if (i0->isa()) return !i1->isa(); + if (VarDeclI* vdi0 = i0->dyn_cast()) { + if (VarDeclI* vdi1 = i1->dyn_cast()) { + return vdi0->e()->payload() < vdi1->e()->payload(); + } else { + return !i1->isa(); + } + } + return false; + } + } _sbp; + + std::stable_sort(m->begin(), m->end(), _sbp); + } + + { + Typer ty(env.envi(), m, typeErrors, ignoreUndefinedParameters); + BottomUpIterator > bu_ty(ty); + for (unsigned int i = 0; i < ts.decls.size(); i++) { + ts.decls[i]->payload(0); + bu_ty.run(ts.decls[i]->ti()); + ty.vVarDecl(*ts.decls[i]); + } + for (unsigned int i = 0; i < functionItems.size(); i++) { + bu_ty.run(functionItems[i]->ti()); + for (unsigned int j = 0; j < functionItems[i]->params().size(); j++) + bu_ty.run(functionItems[i]->params()[j]); + } + } + + m->fixFnMap(); + + { + Typer ty(env.envi(), m, typeErrors, ignoreUndefinedParameters); + BottomUpIterator > bu_ty(ty); + + class TSV2 : public ItemVisitor { + public: + EnvI& env; + Model* m; + BottomUpIterator >& bu_ty; + std::vector& _typeErrors; + TSV2(EnvI& env0, Model* m0, BottomUpIterator >& b, + std::vector& typeErrors) + : env(env0), m(m0), bu_ty(b), _typeErrors(typeErrors) {} + void vVarDeclI(VarDeclI* i) { + bu_ty.run(i->e()); + if (i->e()->ti()->hasTiVariable()) { + _typeErrors.push_back(TypeError(env, i->e()->loc(), + "type-inst variables not allowed in type-inst for `" + + i->e()->id()->str().str() + "'")); + } + VarDecl* vdi = i->e(); + if (vdi->e() == NULL && vdi->type().is_set() && vdi->type().isvar() && + vdi->ti()->domain() == NULL) { + _typeErrors.push_back( + TypeError(env, vdi->loc(), + "set element type for `" + vdi->id()->str().str() + "' is not finite")); + } + if (i->e()->ann().contains(constants().ann.output_only) && vdi->e()->type().isvar()) { + _typeErrors.push_back( + TypeError(env, vdi->loc(), "variables annotated with ::output_only must be par")); + } + } + void vAssignI(AssignI* i) { + bu_ty.run(i->e()); + if (!env.isSubtype(i->e()->type(), i->decl()->ti()->type(), true)) { + _typeErrors.push_back(TypeError(env, i->loc(), + "assignment value for `" + i->decl()->id()->str().str() + + "' has invalid type-inst: expected `" + + i->decl()->ti()->type().toString(env) + + "', actual `" + i->e()->type().toString(env) + "'")); + // Assign to "true" constant to avoid generating further errors that the parameter + // is undefined + i->decl()->e(constants().lit_true); + } + } + void vConstraintI(ConstraintI* i) { + bu_ty.run(i->e()); + if (!env.isSubtype(i->e()->type(), Type::varbool(), true)) + throw TypeError(env, i->loc(), + "invalid type of constraint, expected `" + Type::varbool().toString(env) + + "', actual `" + i->e()->type().toString(env) + "'"); + } + void vSolveI(SolveI* i) { + for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) { + bu_ty.run(*it); + if (!(*it)->type().isann()) + throw TypeError(env, (*it)->loc(), + "expected annotation, got `" + (*it)->type().toString(env) + "'"); + } + bu_ty.run(i->e()); + if (i->e()) { + Type et = i->e()->type(); + + bool needOptCoercion = et.isopt() && et.isint(); + if (needOptCoercion) { + et.ot(Type::OT_PRESENT); + } + + if (!(env.isSubtype(et, Type::varint(), true) || + env.isSubtype(et, Type::varfloat(), true))) + throw TypeError(env, i->e()->loc(), + "objective has invalid type, expected int or float, actual `" + + et.toString(env) + "'"); + + if (needOptCoercion) { + GCLock lock; + std::vector args(2); + args[0] = i->e(); + args[1] = constants().boollit(i->st() == SolveI::ST_MAX); + Call* c = new Call(Location().introduce(), ASTString("objective_deopt_"), args); + c->decl(env.model->matchFn(env, c, false)); + assert(c->decl()); + c->type(et); + i->e(c); + } + } + } + void vOutputI(OutputI* i) { + bu_ty.run(i->e()); + if (i->e()->type() != Type::parstring(1) && i->e()->type() != Type::bot(1)) + throw TypeError(env, i->e()->loc(), + "invalid type in output item, expected `" + + Type::parstring(1).toString(env) + "', actual `" + + i->e()->type().toString(env) + "'"); + } + void vFunctionI(FunctionI* i) { + for (ExpressionSetIter it = i->ann().begin(); it != i->ann().end(); ++it) { + bu_ty.run(*it); + if (!(*it)->type().isann()) + throw TypeError(env, (*it)->loc(), + "expected annotation, got `" + (*it)->type().toString(env) + "'"); + } + bu_ty.run(i->ti()); + bu_ty.run(i->e()); + if (i->e() && !env.isSubtype(i->e()->type(), i->ti()->type(), true)) + throw TypeError(env, i->e()->loc(), + "return type of function does not match body, declared type is `" + + i->ti()->type().toString(env) + "', body type is `" + + i->e()->type().toString(env) + "'"); + if (i->e() && i->e()->type().ispar() && i->ti()->type().isvar()) { + // this is a par function declared as var, so change declared return type + Type i_t = i->ti()->type(); + i_t.ti(Type::TI_PAR); + i->ti()->type(i_t); + } + if (i->e()) i->e(addCoercion(env, m, i->e(), i->ti()->type())()); + } + } _tsv2(env.envi(), m, bu_ty, typeErrors); + iterItems(_tsv2, m); + } + + class TSV3 : public ItemVisitor { + public: + EnvI& env; + Model* m; + OutputI* outputItem; + TSV3(EnvI& env0, Model* m0) : env(env0), m(m0), outputItem(NULL) {} + void vAssignI(AssignI* i) { i->decl()->e(addCoercion(env, m, i->e(), i->decl()->type())()); } + void vOutputI(OutputI* oi) { + if (outputItem == NULL) { + outputItem = oi; + } else { + GCLock lock; + BinOp* bo = new BinOp(Location().introduce(), outputItem->e(), BOT_PLUSPLUS, oi->e()); + bo->type(Type::parstring(1)); + outputItem->e(bo); + oi->remove(); + m->setOutputItem(outputItem); + } + } + } _tsv3(env.envi(), m); + if (typeErrors.empty()) { + iterItems(_tsv3, m); + } + + try { + m->checkFnOverloading(env.envi()); + } catch (TypeError& e) { + typeErrors.push_back(e); + } + + for (unsigned int i = 0; i < ts.decls.size(); i++) { + if (ts.decls[i]->toplevel() && ts.decls[i]->type().ispar() && !ts.decls[i]->type().isann() && + ts.decls[i]->e() == NULL) { + if (ts.decls[i]->type().isopt() && ts.decls[i]->type().dim() == 0) { + ts.decls[i]->e(constants().absent); + } else if (!ignoreUndefinedParameters) { + typeErrors.push_back( + TypeError(env.envi(), ts.decls[i]->loc(), + " symbol error: variable `" + ts.decls[i]->id()->str().str() + + "' must be defined (did you forget to specify a data file?)")); + } + } + if (ts.decls[i]->ti()->isEnum()) { + ts.decls[i]->ti()->setIsEnum(false); + Type vdt = ts.decls[i]->ti()->type(); + vdt.enumId(0); + ts.decls[i]->ti()->type(vdt); + } + } + + for (auto vd_k : env.envi().checkVars) { + try { + VarDecl* vd = + ts.get(env.envi(), vd_k()->cast()->id()->str(), vd_k()->cast()->loc()); + vd->ann().add(constants().ann.mzn_check_var); + if (vd->type().enumId() != 0) { + GCLock lock; + int enumId = vd->type().enumId(); + if (vd->type().dim() > 0) { + const std::vector& arrayEnumIds = + env.envi().getArrayEnum(vd->type().enumId()); + enumId = arrayEnumIds[arrayEnumIds.size() - 1]; + } + if (enumId > 0) { + std::vector args({env.envi().getEnum(enumId)->e()->id()}); + Call* checkEnum = + new Call(Location().introduce(), constants().ann.mzn_check_enum_var, args); + checkEnum->type(Type::ann()); + checkEnum->decl(env.envi().model->matchFn(env.envi(), checkEnum, false)); + vd->ann().add(checkEnum); + } + } + Type vdktype = vd_k()->type(); + vdktype.ti(Type::TI_VAR); + if (!vd_k()->type().isSubtypeOf(vd->type(), false)) { + GCLock lock; + + typeErrors.push_back(TypeError(env.envi(), vd->loc(), + "Solution checker requires `" + vd->id()->str().str() + + "' to be of type `" + vdktype.toString(env.envi()) + + "'")); + } + } catch (TypeError& e) { + typeErrors.push_back( + TypeError(env.envi(), e.loc(), e.msg() + " (required by solution checker model)")); + } + } +} + +void typecheck(Env& env, Model* m, AssignI* ai) { + std::vector typeErrors; + Typer ty(env.envi(), m, typeErrors, false); + BottomUpIterator > bu_ty(ty); + bu_ty.run(ai->e()); + if (!typeErrors.empty()) { + throw typeErrors[0]; + } + if (!env.envi().isSubtype(ai->e()->type(), ai->decl()->ti()->type(), true)) { + throw TypeError(env.envi(), ai->e()->loc(), + "assignment value for `" + ai->decl()->id()->str().str() + + "' has invalid type-inst: expected `" + + ai->decl()->ti()->type().toString(env.envi()) + "', actual `" + + ai->e()->type().toString(env.envi()) + "'"); + } +} + +void output_var_desc_json(Env& env, VarDecl* vd, std::ostream& os, bool extra = false) { + os << " \"" << *vd->id() << "\" : {"; + os << "\"type\" : "; + switch (vd->type().bt()) { + case Type::BT_INT: + os << "\"int\""; + break; + case Type::BT_BOOL: + os << "\"bool\""; + break; + case Type::BT_FLOAT: + os << "\"float\""; + break; + case Type::BT_STRING: + os << "\"string\""; + break; + case Type::BT_ANN: + os << "\"ann\""; + break; + default: + os << "\"?\""; + break; + } + if (vd->type().ot() == Type::OT_OPTIONAL) { + os << ", \"optional\" : true"; + } + if (vd->type().st() == Type::ST_SET) { + os << ", \"set\" : true"; + } + if (vd->type().dim() > 0) { + os << ", \"dim\" : " << vd->type().dim(); + + if (extra) { + os << ", \"dims\" : ["; + bool had_dim = false; + ASTExprVec ranges = vd->ti()->ranges(); + for (int i = 0; i < static_cast(ranges.size()); i++) { + if (ranges[i]->type().enumId() > 0) { + os << (had_dim ? "," : "") << "\"" + << *env.envi().getEnum(ranges[i]->type().enumId())->e()->id() << "\""; + } else { + os << (had_dim ? "," : "") << "\"int\""; + } + had_dim = true; + } + os << "]"; + + if (vd->type().enumId() > 0) { + const std::vector& enumIds = env.envi().getArrayEnum(vd->type().enumId()); + if (enumIds.back() > 0) { + os << ", \"enum_type\" : \"" << *env.envi().getEnum(enumIds.back())->e()->id() << "\""; + } + } + } + + } else { + if (extra) { + if (vd->type().enumId() > 0) { + os << ", \"enum_type\" : \"" << *env.envi().getEnum(vd->type().enumId())->e()->id() << "\""; + } + } + } + os << "}"; +} + +void output_model_variable_types(Env& env, Model* m, std::ostream& os) { + class VInfVisitor : public ItemVisitor { + public: + Env& env; + bool had_var; + bool had_enum; + std::ostringstream oss_vars; + std::ostringstream oss_enums; + VInfVisitor(Env& env0) : env(env0), had_var(false), had_enum(false) {} + bool enter(Item* i) { + if (IncludeI* ii = i->dyn_cast()) { + std::string prefix = + ii->m()->filepath().str().substr(0, ii->m()->filepath().size() - ii->f().size()); + return (prefix.empty() || prefix == "./"); + } + return true; + } + void vVarDeclI(VarDeclI* vdi) { + if (!vdi->e()->type().isann() && !vdi->e()->ti()->isEnum()) { + if (had_var) oss_vars << ",\n"; + output_var_desc_json(env, vdi->e(), oss_vars, true); + had_var = true; + } else if (vdi->e()->type().st() == Type::ST_SET && vdi->e()->type().enumId() != 0 && + !vdi->e()->type().isann()) { + if (had_enum) oss_enums << ", "; + oss_enums << "\"" << *env.envi().getEnum(vdi->e()->type().enumId())->e()->id() << "\""; + had_enum = true; + } + } + } _vinf(env); + iterItems(_vinf, m); + os << "{\"var_types\": {"; + os << "\n \"vars\": {\n" << _vinf.oss_vars.str() << "\n },"; + os << "\n \"enums\": [" << _vinf.oss_enums.str() << "]\n"; + os << "}}\n"; +} + +void output_model_interface(Env& env, Model* m, std::ostream& os) { + class IfcVisitor : public ItemVisitor { + public: + Env& env; + bool had_input; + bool had_output; + bool had_add_to_output = false; + std::ostringstream oss_input; + std::ostringstream oss_output; + std::string method; + IfcVisitor(Env& env0) : env(env0), had_input(false), had_output(false), method("sat") {} + bool enter(Item* i) { + if (IncludeI* ii = i->dyn_cast()) { + std::string prefix = + ii->m()->filepath().str().substr(0, ii->m()->filepath().size() - ii->f().size()); + return (prefix.empty() || prefix == "./"); + } + return true; + } + void vVarDeclI(VarDeclI* vdi) { + VarDecl* vd = vdi->e(); + if (vd->type().ispar() && !vd->type().isann() && + (vd->e() == NULL || vd->e() == constants().absent)) { + if (had_input) oss_input << ",\n"; + output_var_desc_json(env, vd, oss_input); + had_input = true; + } else { + bool process_var = false; + if (vd->ann().contains(constants().ann.add_to_output)) { + if (!had_add_to_output) { + oss_output.str(""); + had_output = false; + } + had_add_to_output = true; + process_var = true; + } else if (!had_add_to_output) { + process_var = + vd->type().isvar() && + (vd->e() == NULL || vd->ann().contains(constants().ann.rhs_from_assignment)); + } + if (process_var) { + if (had_output) { + oss_output << ",\n"; + } + output_var_desc_json(env, vd, oss_output); + had_output = true; + } + } + } + void vSolveI(SolveI* si) { + switch (si->st()) { + case SolveI::ST_MIN: + method = "min"; + break; + case SolveI::ST_MAX: + method = "max"; + break; + case SolveI::ST_SAT: + method = "sat"; + break; + } + } + } _ifc(env); + iterItems(_ifc, m); + os << "{\n \"input\" : {\n" + << _ifc.oss_input.str() << "\n },\n \"output\" : {\n" + << _ifc.oss_output.str() << "\n }"; + os << ",\n \"method\": \""; + os << _ifc.method; + os << "\""; + os << "\n}\n"; +} + +} // namespace MiniZinc diff --git a/lib/utils_savestream.cpp b/lib/utils_savestream.cpp new file mode 100644 index 0000000..518d28b --- /dev/null +++ b/lib/utils_savestream.cpp @@ -0,0 +1,45 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was ! distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#include +#else +#include +#endif + +#include + +#include + +using namespace std; +using namespace MiniZinc; + +// StreamRedir::StreamRedir(FILE *s0) : d_s0(s0) { } + +StreamRedir::StreamRedir(FILE* s0, FILE* s1, bool fFlush) : d_s0(s0) { replaceStream(s1, fFlush); } +StreamRedir::~StreamRedir() { restore(); } + +void StreamRedir::replaceStream(FILE* s1, bool fFlush) { + if (fFlush) fflush(d_s0); + fgetpos(d_s0, &(d_si.pos)); + d_si.fd = dup(fileno(d_s0)); + dup2(fileno(s1), fileno(d_s0)); +} + +void StreamRedir::restore(bool fFLush) { + if (fFLush) fflush(d_s0); + dup2(d_si.fd, fileno(d_s0)); + close(d_si.fd); + clearerr(d_s0); + fsetpos(d_s0, &(d_si.pos)); +} diff --git a/lib/values.cpp b/lib/values.cpp new file mode 100644 index 0000000..6134831 --- /dev/null +++ b/lib/values.cpp @@ -0,0 +1,34 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include + +namespace MiniZinc { + +const IntVal IntVal::minint(void) { return IntVal(std::numeric_limits::min()); } +const IntVal IntVal::maxint(void) { return IntVal(std::numeric_limits::max()); } +const IntVal IntVal::infinity(void) { return IntVal(1, true); } + +IntSetVal::IntSetVal(IntVal m, IntVal n) : ASTChunk(sizeof(Range)) { + get(0).min = m; + get(0).max = n; +} + +FloatSetVal::FloatSetVal(FloatVal m, FloatVal n) : ASTChunk(sizeof(Range)) { + get(0).min = m; + get(0).max = n; +} + +const FloatVal FloatVal::infinity(void) { return FloatVal(1.0, true); } + +} // namespace MiniZinc diff --git a/minizinc.cpp b/minizinc.cpp new file mode 100644 index 0000000..23afe43 --- /dev/null +++ b/minizinc.cpp @@ -0,0 +1,70 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This (main) file coordinates flattening and solving. + * The corresponding modules are flexibly plugged in + * as derived classes, prospectively from DLLs. + * A flattening module should provide MinZinc::GetFlattener() + * A solving module should provide an object of a class derived from SolverFactory. + * Need to get more flexible for multi-pass & multi-solving stuff TODO + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MiniZinc; + +int main(int argc, const char** argv) { + Timer starttime; + bool fSuccess = false; + + try { + MznSolver slv(std::cout, std::cerr); + try { + std::vector args(argc - 1); + for (int i = 1; i < argc; i++) args[i - 1] = argv[i]; + fSuccess = (slv.run(args, "", argv[0]) != SolverInstance::ERROR); + } catch (const LocationException& e) { + if (slv.get_flag_verbose()) std::cerr << std::endl; + std::cerr << e.loc() << ":" << std::endl; + std::cerr << e.what() << ": " << e.msg() << std::endl; + } catch (const Exception& e) { + if (slv.get_flag_verbose()) std::cerr << std::endl; + std::string what = e.what(); + std::cerr << what << (what.empty() ? "" : ": ") << e.msg() << std::endl; + } catch (const exception& e) { + if (slv.get_flag_verbose()) std::cerr << std::endl; + std::cerr << e.what() << std::endl; + } catch (...) { + if (slv.get_flag_verbose()) std::cerr << std::endl; + std::cerr << " UNKNOWN EXCEPTION." << std::endl; + } + + if (slv.get_flag_verbose()) { + std::cerr << " Done ("; + cerr << "overall time " << starttime.stoptime() << ")." << std::endl; + } + return !fSuccess; + } catch (const Exception& e) { + std::string what = e.what(); + std::cerr << what << (what.empty() ? "" : ": ") << e.msg() << std::endl; + std::exit(EXIT_FAILURE); + } +} // int main() diff --git a/mza.py b/mza.py new file mode 100644 index 0000000..515c944 --- /dev/null +++ b/mza.py @@ -0,0 +1,101 @@ +import contextlib +import json +import sys +from ctypes.util import find_library + +import cffi + +DEBUG = False + + +def debugprint(*args): + if DEBUG: + print(*args, file=sys.stderr, flush=True) + + +ffi = cffi.FFI() +ffi.cdef( + """ + struct _MZNInstance; + typedef struct _MZNInstance* MZNInstance; + + void set_rnd_seed(int seed); + + MZNInstance minizinc_instance_init(const char* mza_file, const char* data_file, const char* solver); + void minizinc_instance_destroy(MZNInstance); + + void minizinc_add_call(MZNInstance, const char* call, ...); + void minizinc_set_solution(MZNInstance, int def, int sol); + void minizinc_output_dict(MZNInstance, bool); + void minizinc_print_hedge(MZNInstance); + void minizinc_set_limit(MZNInstance, int limit); + + void minizinc_push_state(MZNInstance); + void minizinc_pop_state(MZNInstance); + + const char* minizinc_solve(MZNInstance); + """ +) +# Set LD_LIBRARY_PATH (DYLD_LIBRARY_PATH on macOS) to the folder containing the mza library. +lib = ffi.dlopen("mza") + + +def set_rnd_seed(seed: int): + debugprint(f"set_rnd_seed({seed});") + lib.set_rnd_seed(seed) + + +class Instance: + def __init__(self, mza_file, data_file, solver): + debugprint( + f'MZNInstance inst = minizinc_instance_init("{mza_file}", "{data_file}", "{solver}");' + ) + self._ptr = lib.minizinc_instance_init( + mza_file.encode(), data_file.encode(), solver.encode() + ) + + def __del__(self): + debugprint(f"minizinc_instance_destroy(inst);") + lib.minizinc_instance_destroy(self._ptr) + self._ptr = None + + def output_dict(self, b: bool): + debugprint(f"minizinc_output_dict(inst, {int(b)});") + lib.minizinc_output_dict(self._ptr, b) + + def set_incumbent(self, sol): + for k, v in sol.items(): + debugprint(f"minizinc_set_solution(inst, {k}, {v});") + lib.minizinc_set_solution(self._ptr, int(k), v) + + def set_limit(self, limit): + debugprint(f"minizinc_set_limit(inst, {limit});") + lib.minizinc_set_limit(self._ptr, limit) + + def print(self): + debugprint(f"minizinc_print_hedge(inst);") + lib.minizinc_print_hedge(self._ptr) + + @contextlib.contextmanager + def branch(self): + try: + debugprint(f"minizinc_push_state(inst);") + lib.minizinc_push_state(self._ptr) + yield self + finally: + debugprint(f"minizinc_pop_state(inst);") + lib.minizinc_pop_state(self._ptr) + + def add_call(self, call: str, *args): + debugprint( + f"minizinc_add_call(inst, \"{call}\"{''.join([', '+ str(i) for i in args])});" + ) + lib.minizinc_add_call( + self._ptr, call.encode(), *[ffi.cast("int", i) for i in args] + ) + + def solve(self): + debugprint(f"minizinc_solve(inst);") + res = ffi.string(lib.minizinc_solve(self._ptr)) + tmp = json.loads(res) + return tmp["status"], tmp["solution"] diff --git a/mzn2doc.cpp b/mzn2doc.cpp new file mode 100644 index 0000000..d632b32 --- /dev/null +++ b/mzn2doc.cpp @@ -0,0 +1,301 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace MiniZinc; +using namespace std; + +bool beginswith(string s, string t) { return s.compare(0, t.length(), t) == 0; } + +int main(int argc, char** argv) { + string filename; + vector includePaths; + bool flag_ignoreStdlib = false; + bool flag_verbose = false; + bool flag_include_stdlib = false; + bool flag_index = true; + bool flag_rst = false; + int toplevel_groups = 0; + string output_base; + string header_file; + string footer_file; + + string std_lib_dir; + if (char* MZNSTDLIBDIR = getenv("MZN_STDLIB_DIR")) { + std_lib_dir = string(MZNSTDLIBDIR); + } + string globals_dir; + + if (argc < 2) goto error; + + for (int i = 1; i < argc; i++) { + if (string(argv[i]) == string("-h") || string(argv[i]) == string("--help")) goto error; + if (string(argv[i]) == string("--version")) { + std::cout << "MiniZinc documentation generator, version " << MZN_VERSION_MAJOR << "." + << MZN_VERSION_MINOR << "." << MZN_VERSION_PATCH << std::endl; + std::cout << "Copyright (C) 2014-2017 Monash University, NICTA, Data61" << std::endl; + std::exit(EXIT_SUCCESS); + } + if (beginswith(string(argv[i]), "-I")) { + string include(argv[i]); + if (include.length() > 2) { + includePaths.push_back(include.substr(2) + string("/")); + } else { + i++; + if (i == argc) { + goto error; + } + includePaths.push_back(argv[i] + string("/")); + } + } else if (string(argv[i]) == string("--ignore-stdlib")) { + flag_ignoreStdlib = true; + } else if (string(argv[i]) == string("-v") || string(argv[i]) == string("--verbose")) { + flag_verbose = true; + } else if (string(argv[i]) == "--stdlib-dir") { + i++; + if (i == argc) goto error; + std_lib_dir = argv[i]; + } else if (beginswith(string(argv[i]), "-G")) { + string filename(argv[i]); + if (filename.length() > 2) { + globals_dir = filename.substr(2); + } else { + i++; + if (i == argc) { + goto error; + } + globals_dir = argv[i]; + } + } else if (string(argv[i]) == "--toplevel-groups") { + i++; + if (i == argc) goto error; + toplevel_groups = atoi(argv[i]); + } else if (string(argv[i]) == "--html-header" || string(argv[i]) == "--rst-header") { + i++; + if (i == argc) goto error; + header_file = string(argv[i]); + } else if (string(argv[i]) == "--html-footer" || string(argv[i]) == "--rst-footer") { + i++; + if (i == argc) goto error; + footer_file = string(argv[i]); + } else if (string(argv[i]) == "--include-stdlib") { + flag_include_stdlib = true; + } else if (string(argv[i]) == "--no-index") { + flag_index = false; + } else if (string(argv[i]) == "--globals-dir" || string(argv[i]) == "--mzn-globals-dir") { + i++; + if (i == argc) goto error; + globals_dir = argv[i]; + } else if (string(argv[i]) == "--output-base") { + i++; + if (i == argc) goto error; + output_base = argv[i]; + } else if (string(argv[i]) == "--rst-output") { + flag_rst = true; + toplevel_groups = 0; + } else { + std::string input_file(argv[i]); + if (input_file.length() <= 4) { + std::cerr << "Error: cannot handle file " << input_file << "." << std::endl; + goto error; + } + size_t last_dot = input_file.find_last_of('.'); + std::string extension; + if (last_dot != string::npos) extension = input_file.substr(last_dot, string::npos); + if (extension == ".mzn") { + if (filename == "") { + filename = input_file; + } else { + std::cerr << "Error: Multiple .mzn files given." << std::endl; + goto error; + } + } else if (extension == ".dzn" || extension == ".json") { + std::cerr << "Error: cannot generate documentation for data files." << std::endl; + } else { + std::cerr << "Error: cannot handle file extension " << extension << "." << std::endl; + goto error; + } + } + } + + if (filename == "") { + std::cerr << "Error: no model file given." << std::endl; + goto error; + } + + if (std_lib_dir.empty()) { + SolverConfigs solver_configs(std::cerr); + std_lib_dir = solver_configs.mznlibDir(); + } + + if (std_lib_dir.empty()) { + std::cerr << "Error: unknown minizinc standard library directory.\n" + << "Specify --stdlib-dir on the command line or set the\n" + << "MZN_STDLIB_DIR environment variable.\n"; + std::exit(EXIT_FAILURE); + } + + if (globals_dir != "") { + includePaths.push_back(std_lib_dir + "/" + globals_dir + "/"); + } + includePaths.push_back(std_lib_dir + "/std/"); + + for (unsigned int i = 0; i < includePaths.size(); i++) { + if (!FileUtils::directory_exists(includePaths[i])) { + std::cerr << "Cannot access include directory " << includePaths[i] << "\n"; + std::exit(EXIT_FAILURE); + } + } + + if (output_base == "") { + output_base = filename.substr(0, filename.length() - 4); + } + + { + string header; + size_t header_title = std::string::npos; + size_t title_size = std::string("@TITLE").size(); + if (!header_file.empty()) { + std::ifstream hs(header_file); + if (!hs.good()) { + std::cerr << "Cannot open header file " << header_file << "\n"; + std::exit(EXIT_FAILURE); + } + std::string str((std::istreambuf_iterator(hs)), std::istreambuf_iterator()); + header = str; + header_title = str.find("@TITLE"); + } + string footer; + if (!footer_file.empty()) { + std::ifstream hs(footer_file); + if (!hs.good()) { + std::cerr << "Cannot open footer file " << footer_file << "\n"; + std::exit(EXIT_FAILURE); + } + std::string str((std::istreambuf_iterator(hs)), std::istreambuf_iterator()); + footer = str; + } + + std::stringstream errstream; + if (flag_verbose) std::cerr << "Parsing '" << filename << "'" << std::endl; + std::vector filenames; + filenames.push_back(filename); + Env env; + if (Model* m = parse(env, filenames, vector(), "", "", includePaths, flag_ignoreStdlib, + true, flag_verbose, errstream)) { + try { + env.model(m); + if (flag_verbose) std::cerr << "Done parsing." << std::endl; + if (flag_verbose) std::cerr << "Typechecking ..."; + vector typeErrors; + MiniZinc::typecheck(env, m, typeErrors, true, false); + if (typeErrors.size() > 0) { + for (unsigned int i = 0; i < typeErrors.size(); i++) { + if (flag_verbose) std::cerr << std::endl; + std::cerr << typeErrors[i].loc() << ":" << std::endl; + std::cerr << typeErrors[i].what() << ": " << typeErrors[i].msg() << std::endl; + } + exit(EXIT_FAILURE); + } + if (flag_verbose) std::cerr << " done" << std::endl; + std::string basename = output_base; + std::string basedir; + size_t lastSlash = output_base.find_last_of("/"); + if (lastSlash != std::string::npos) { + basedir = basename.substr(0, lastSlash) + "/"; + basename = basename.substr(lastSlash + 1, std::string::npos); + } + std::vector docs; + if (flag_rst) { + docs = RSTPrinter::printRST(env.envi(), m, basename, toplevel_groups, flag_include_stdlib, + flag_index); + } else { + docs = HtmlPrinter::printHtml(env.envi(), m, basename, toplevel_groups, + flag_include_stdlib, flag_index); + } + + for (unsigned int i = 0; i < docs.size(); i++) { + std::ofstream os(basedir + docs[i].filename() + (flag_rst ? ".rst" : ".html")); + std::string header_replace = header; + if (header_title != std::string::npos) { + header_replace = header_replace.replace(header_title, title_size, docs[i].title()); + } + os << header_replace; + os << docs[i].document(); + os << footer; + os.close(); + } + } catch (LocationException& e) { + if (flag_verbose) std::cerr << std::endl; + std::cerr << e.loc() << ":" << std::endl; + std::cerr << e.what() << ": " << e.msg() << std::endl; + exit(EXIT_FAILURE); + } catch (Exception& e) { + if (flag_verbose) std::cerr << std::endl; + std::cerr << e.what() << ": " << e.msg() << std::endl; + exit(EXIT_FAILURE); + } + } else { + if (flag_verbose) std::cerr << std::endl; + std::copy(istreambuf_iterator(errstream), istreambuf_iterator(), + ostreambuf_iterator(std::cerr)); + exit(EXIT_FAILURE); + } + } + + if (flag_verbose) std::cerr << "Done." << std::endl; + return 0; + +error: + std::string executable_name(argv[0]); + executable_name = executable_name.substr(executable_name.find_last_of("/\\") + 1); + std::cerr + << "Usage: " << executable_name + << " [] [-I ] .mzn [.dzn ...]" << std::endl + << std::endl + << "Options:" << std::endl + << " --help, -h\n Print this help message" << std::endl + << " --version\n Print version information" << std::endl + << " --ignore-stdlib\n Ignore the standard libraries stdlib.mzn and builtins.mzn" + << std::endl + << " -v, --verbose\n Print progress statements" << std::endl + << " --stdlib-dir \n Path to MiniZinc standard library directory" << std::endl + << " -G --globals-dir --mzn-globals-dir\n Search for included files in /." + << std::endl + << " --single-page\n Print entire documentation on a single HTML page." << std::endl + << " --no-index\n Do not generate an index of all symbols." << std::endl + << " --rst-output\n Generate ReStructuredText rather than HTML." << std::endl + << " --html-header, --html-footer, --rst-header, --rst-footer\n Header/footer files " + "to include in output." + << std::endl + << std::endl + << "Output options:" << std::endl + << std::endl + << " --output-base \n Base name for output files" << std::endl; + + exit(EXIT_FAILURE); +} diff --git a/mznasm.cpp b/mznasm.cpp new file mode 100644 index 0000000..c569134 --- /dev/null +++ b/mznasm.cpp @@ -0,0 +1,47 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This (main) file coordinates flattening and solving. + * The corresponding modules are flexibly plugged in + * as derived classes, prospectively from DLLs. + * A flattening module should provide MinZinc::GetFlattener() + * A solving module should provide an object of a class derived from SolverFactory. + * Need to get more flexible for multi-pass & multi-solving stuff TODO + */ + +#include + +#include + +using namespace std; +using namespace MiniZinc; + +int main(int argc, const char** argv) { + std::vector args; + for (int i = 1; i < argc; ++i) { + args.emplace_back(argv[i]); + } + + try { + MznSolver slv(args); + auto result = slv.run(); + std::cout << result.second; + } catch (const LocationException& e) { + std::cerr << std::endl; + std::cerr << e.loc() << ":" << std::endl; + std::cerr << e.what() << ": " << e.msg() << std::endl; + } catch (const Exception& e) { + std::cerr << std::endl; + std::string what = e.what(); + std::cerr << what << (what.empty() ? "" : ": ") << e.msg() << std::endl; + } + return 0; +} diff --git a/mzncc.cpp b/mzncc.cpp new file mode 100644 index 0000000..3094b5b --- /dev/null +++ b/mzncc.cpp @@ -0,0 +1,100 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This (main) file coordinates flattening and solving. + * The corresponding modules are flexibly plugged in + * as derived classes, prospectively from DLLs. + * A flattening module should provide MinZinc::GetFlattener() + * A solving module should provide an object of a class derived from SolverFactory. + * Need to get more flexible for multi-pass & multi-solving stuff TODO + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MiniZinc; + +int main(int argc, const char** argv) { + Timer starttime; + GCLock lock; + try { + MznReader r(std::cout, std::cerr); + Model* m(nullptr); + try { + /* + std::vector args(argc-1); + for (int i=1; i args(argc - 1); + for (int i = 1; i < argc; i++) args[i - 1] = argv[i]; + switch (r.processOptions(args)) { + case MznReader::OPTION_FINISH: + return 0; + case MznReader::OPTION_ERROR: + return 1; + default: + break; + } + + Model* m(r.read()); + + Env env(m, std::cout, std::cerr); + std::vector typeErrors; + typecheck(env, m, typeErrors, true /* ignoreUndefinedParameters */, + false /* allowMultiAssignment */, false /* isFlatZinc */); + // debugprint(m); + m = env.envi().model; + + CodeGen cg; + CG::run(cg, m); + } catch (const LocationException& e) { + if (r.get_flag_verbose()) std::cerr << std::endl; + std::cerr << e.loc() << ":" << std::endl; + std::cerr << e.what() << ": " << e.msg() << std::endl; + } catch (const Exception& e) { + if (r.get_flag_verbose()) std::cerr << std::endl; + std::string what = e.what(); + std::cerr << what << (what.empty() ? "" : ": ") << e.msg() << std::endl; + } catch (const exception& e) { + if (r.get_flag_verbose()) std::cerr << std::endl; + std::cerr << e.what() << std::endl; + } catch (...) { + if (r.get_flag_verbose()) std::cerr << std::endl; + std::cerr << " UNKNOWN EXCEPTION." << std::endl; + } + + if (r.get_flag_verbose()) { + std::cerr << " Done ("; + cerr << "overall time " << starttime.stoptime() << ")." << std::endl; + } + } catch (const Exception& e) { + std::string what = e.what(); + std::cerr << what << (what.empty() ? "" : ": ") << e.msg() << std::endl; + std::exit(EXIT_FAILURE); + } + return 0; +} // int main() diff --git a/share/minizinc/g12_fd/all_different_int.mzn b/share/minizinc/g12_fd/all_different_int.mzn new file mode 100644 index 0000000..b79f1fd --- /dev/null +++ b/share/minizinc/g12_fd/all_different_int.mzn @@ -0,0 +1,21 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate all_different_int(array[int] of var int: x) = + g12fd_int_all_different(x); + +%-----------------------------------------------------------------------------% + +% The implementation of the all_different constraint in the G12/FD solver. +% This should not be called directly, instead the definition above should +% be used. +predicate g12fd_int_all_different(array[int] of var int: x); + +% G12/FD doesn't provide a reified all_different so we use the following +% definition if g12fd_int_all_different is called from a reified context. +% +predicate g12fd_int_all_different_reif(array[int] of var int: x, var bool: r) = + r <-> forall(i,j in index_set(x) where i < j) ( x[i] != x[j] ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/g12_fd/cumulative.mzn b/share/minizinc/g12_fd/cumulative.mzn new file mode 100644 index 0000000..44ac2c2 --- /dev/null +++ b/share/minizinc/g12_fd/cumulative.mzn @@ -0,0 +1,39 @@ +%-----------------------------------------------------------------------------% +% Requires that a set of tasks given by start times 's', durations 'd', and +% resource requirements 'r', never require more than a global resource bound +% 'b' at any one time. +% Assumptions: +% - forall i, d[i] >= 0 and r[i] >= 0 +%-----------------------------------------------------------------------------% + +predicate cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + g12fd_cumulative(s, d, r, b))); + + +%-----------------------------------------------------------------------------% + +% Optional parameters that can be used with the built-in G12/FD cumulative +% constraint. +% +annotation energy_feasibility_check; +annotation edge_finding_filtering; +annotation ext_edge_finding_filtering; +annotation histogram_filtering; +annotation idempotent; + +%-----------------------------------------------------------------------------% + +% The implementation of the cumulative constraint in the G12/FD solver. +% This should not be called directly, instead the definition above should +% be used. +predicate g12fd_cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/g12_fd/global_cardinality_low_up.mzn b/share/minizinc/g12_fd/global_cardinality_low_up.mzn new file mode 100644 index 0000000..84e5c72 --- /dev/null +++ b/share/minizinc/g12_fd/global_cardinality_low_up.mzn @@ -0,0 +1,23 @@ +%-----------------------------------------------------------------------------% +% Requires that for all 'i', the value 'cover[i]' appears at least 'lbound[i]' +% and at most 'ubound[i]' times in the array 'x'. +%-----------------------------------------------------------------------------% + +predicate global_cardinality_low_up(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound) = + g12fd_global_cardinality_open(x, cover, lbound, ubound); + +%-----------------------------------------------------------------------------% + +% The implementation in the G12/FD solver. This should not be called directly; +% instead the definition above should be used. + +predicate g12fd_global_cardinality_open(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/g12_lazyfd/all_different_int.mzn b/share/minizinc/g12_lazyfd/all_different_int.mzn new file mode 100644 index 0000000..9bba796 --- /dev/null +++ b/share/minizinc/g12_lazyfd/all_different_int.mzn @@ -0,0 +1,21 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate all_different_int(array[int] of var int: x) = + g12lazy_int_all_different(x); + +%-----------------------------------------------------------------------------% + +% The implementation of the all_different constraint in the G12/LazyFD solver. +% This should not be called directly, instead the definition above should +% be used. +predicate g12lazy_int_all_different(array[int] of var int: x); + +% G12/LazyFD doesn't provide a reified all_different so we use the following +% definition if g12lazy_int_all_different is called from a reified context. +% +predicate g12lazy_int_all_different_reif(array[int] of var int: x, var bool: r) = + r <-> forall(i,j in index_set(x) where i < j) ( x[i] != x[j] ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/g12_lazyfd/redefinitions.mzn b/share/minizinc/g12_lazyfd/redefinitions.mzn new file mode 100644 index 0000000..a57b3ca --- /dev/null +++ b/share/minizinc/g12_lazyfd/redefinitions.mzn @@ -0,0 +1,51 @@ +% FlatZinc built-in redefinitions for G12/LazyFD. + +predicate array_bool_element(var int : idx, array[int] of bool : arr, + var bool : v) = + let { int : N = length(arr), + array[1..N] of var bool : tmp = [idx = I | I in 1..N] + } + in + exists(I in 1..N)(tmp[I]) + /\ forall(I in 1..N) (tmp[I] -> v = arr[I]); + +predicate array_var_bool_element(var int : idx, array[int] of var bool : arr, + var bool : v) = + let { int : N = length(arr), + array[1..N] of var bool : tmp = [idx = I | I in 1..N] + } + in + exists(I in 1..N)(tmp[I]) + /\ forall(I in 1..N) (tmp[I] -> v = arr[I]); + +predicate array_bool_xor(array[int] of var bool: bs) = + let { int: bs_lower = min(index_set(bs)), + int: bs_upper = max(index_set(bs)), + int: n = length(bs) + } in + if n == 1 then bs[bs_lower] else + if n == 2 then bs[bs_lower] xor bs[bs_upper] else + if n == 3 then bs[bs_lower] = (bs[bs_lower + 1] = bs[bs_upper]) + else + let { int: cs_lower = bs_lower + 1, + int: cs_upper = bs_upper - 1, + array [cs_lower..cs_upper] of var bool: cs + } in + forall(i in cs_lower..cs_upper-1)( + cs[i+1] = bs[i+1] xor cs[i] + ) + /\ (cs[cs_lower] = bs[bs_lower] xor bs[bs_lower + 1]) + /\ (bs[bs_upper] xor cs[cs_upper]) + endif endif endif; + +predicate int_abs(var int: a, var int: b) = + b >= 0 /\ b >= a /\ b >= -a /\ (b <= a \/ b <= - a); + +predicate int_mod(var int: a, var int: b, var int: c) = + let { + int : bnd = max([lb(a), ub(a), -lb(a), -ub(a)]), + var -bnd .. bnd : z + } in + ( b >= 0 -> c >= 0 /\ c < b) /\ + ( b <= 0 -> c <= 0 /\ c > b) /\ + b * z + c = a; diff --git a/share/minizinc/geas/fzn_all_different_int.mzn b/share/minizinc/geas/fzn_all_different_int.mzn new file mode 100644 index 0000000..e0a9f7c --- /dev/null +++ b/share/minizinc/geas/fzn_all_different_int.mzn @@ -0,0 +1,2 @@ +predicate fzn_all_different_int(array [int] of var int: x) = geas_all_different_int(x); +predicate geas_all_different_int(array [int] of var int: x); diff --git a/share/minizinc/geas/fzn_alldifferent_except_0.mzn b/share/minizinc/geas/fzn_alldifferent_except_0.mzn new file mode 100644 index 0000000..30c4b5c --- /dev/null +++ b/share/minizinc/geas/fzn_alldifferent_except_0.mzn @@ -0,0 +1,2 @@ +predicate fzn_alldifferent_except_0(array [int] of var int: x) = geas_all_different_except_0(x); +predicate geas_all_different_except_0(array [int] of var int: x); diff --git a/share/minizinc/geas/fzn_cumulative.mzn b/share/minizinc/geas/fzn_cumulative.mzn new file mode 100644 index 0000000..098b278 --- /dev/null +++ b/share/minizinc/geas/fzn_cumulative.mzn @@ -0,0 +1,23 @@ +predicate geas_cumulative_var(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b); +predicate geas_cumulative(array[int] of var int: s, + array[int] of int: d, + array[int] of int: r, + int: b); + + +predicate fzn_cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b) = + geas_cumulative_var(s, d, r, b); + +%% TODO: once disjunctive propagator is fixed, add +%% special case for b = 1. +predicate fzn_cumulative(array[int] of var int: s, + array[int] of int: d, + array[int] of int: r, + int: b) = + geas_cumulative(s, d, r, b); diff --git a/share/minizinc/geas/fzn_disjunctive.mzn b/share/minizinc/geas/fzn_disjunctive.mzn new file mode 100644 index 0000000..12bb9d9 --- /dev/null +++ b/share/minizinc/geas/fzn_disjunctive.mzn @@ -0,0 +1,9 @@ +%predicate geas_disjunctive_var(array[int] of var int: s, +% array[int] of var int: d); +predicate geas_disjunctive(array[int] of var int: s, + array[int] of int: d); + +%predicate disjunctive(array[int] of var int: s, array[int] of var int: d) = +% geas_disjunctive_var(s, d); +predicate fzn_disjunctive(array[int] of var int: s, array[int] of int: d) = + geas_disjunctive(s, d); diff --git a/share/minizinc/geas/fzn_global_cardinality.mzn b/share/minizinc/geas/fzn_global_cardinality.mzn new file mode 100644 index 0000000..37f3579 --- /dev/null +++ b/share/minizinc/geas/fzn_global_cardinality.mzn @@ -0,0 +1,15 @@ +predicate fzn_global_cardinality(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: count) = + geas_global_cardinality(x, cover, count); + +predicate fzn_global_cardinality(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: count) = + forall (i in index_set(cover)) ( + count[i] = sum (j in index_set(x)) (bool2int(x[j] = cover[i])) + ); + +predicate geas_global_cardinality(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: count); diff --git a/share/minizinc/geas/fzn_inverse.mzn b/share/minizinc/geas/fzn_inverse.mzn new file mode 100644 index 0000000..2333810 --- /dev/null +++ b/share/minizinc/geas/fzn_inverse.mzn @@ -0,0 +1,5 @@ +include "all_different.mzn"; + +predicate fzn_inverse(array [int] of var int: x, array [int] of var int: y) = + alldifferent(x) /\ alldifferent(y) /\ + forall (i in index_set(x), j in index_set(y)) (x[i] = j <-> y[j] = i); diff --git a/share/minizinc/geas/fzn_table_int.mzn b/share/minizinc/geas/fzn_table_int.mzn new file mode 100644 index 0000000..c58256a --- /dev/null +++ b/share/minizinc/geas/fzn_table_int.mzn @@ -0,0 +1,8 @@ +predicate geas_table_int(array [int] of var int: x, array [int] of int: t); + +predicate fzn_table_int(array [int] of var int: x, array [int, int] of int: t) = + assert (index_set_2of2(t) == index_set(x), + "The second dimension of the table must equal the number of variables " + ++ "in the first argument", + geas_table_int(x, [ t[i,j] | i in index_set_1of2(t), j in index_set_2of2(t) ]) + ); diff --git a/share/minizinc/geas/fzn_value_precede_int.mzn b/share/minizinc/geas/fzn_value_precede_int.mzn new file mode 100644 index 0000000..7140c67 --- /dev/null +++ b/share/minizinc/geas/fzn_value_precede_int.mzn @@ -0,0 +1,9 @@ +predicate fzn_value_precede_int(int: s, int: t, array[int] of var int: x) = + let { + var index_set(x): pos_s; + constraint forall (i in index_set(x)) ( + (pos_s <= i) -> (x[i] = s) \/ (pos_s < i) + ); + } in forall(i in index_set(x)) ( + (x[i] = t) -> (pos_s < i) + ); diff --git a/share/minizinc/geas/redefinitions-2.0.mzn b/share/minizinc/geas/redefinitions-2.0.mzn new file mode 100644 index 0000000..339f053 --- /dev/null +++ b/share/minizinc/geas/redefinitions-2.0.mzn @@ -0,0 +1,29 @@ +% This file contains redefinitions of standard builtins that can be overridden +% by solvers. + +predicate bool_clause_reif(array[int] of var bool: as, + array[int] of var bool: bs, + var bool: b) = + clause(as,bs++[b]) /\ + forall (i in index_set(as)) (as[i] -> b) /\ + forall (i in index_set(bs)) (bs[i] \/ b); + +predicate array_float_maximum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +predicate array_float_minimum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); diff --git a/share/minizinc/geas/redefinitions.mzn b/share/minizinc/geas/redefinitions.mzn new file mode 100644 index 0000000..111bea7 --- /dev/null +++ b/share/minizinc/geas/redefinitions.mzn @@ -0,0 +1,84 @@ +%% New annotations +annotation assume(array[int] of var bool: b); +annotation int_priority(array[int] of var int: xs, + array[int] of ann: br, ann: sel); +annotation bool_priority(array[int] of var bool: xs, + array[int] of ann: br, ann: sel); + +%% Half Reifications +predicate int_eq_imp(var int: a, var int: b, var bool: r); +predicate int_ne_imp(var int: a, var int: b, var bool: r); +predicate int_le_imp(var int: a, var int: b, var bool: r); +predicate int_lt_imp(var int: a, var int: b, var bool: r); + +predicate int_lin_eq_imp(array [int] of int: as, array [int] of var int: bs, + int: c, var bool: r); +predicate int_lin_ne_imp(array [int] of int: as, array [int] of var int: bs, + int: c, var bool: r); +predicate int_lin_le_imp(array [int] of int: as, array [int] of var int: bs, + int: c, var bool: r); + +predicate bool_eq_imp(var bool: a, var bool: b, var bool: r); +predicate bool_ne_imp(var bool: a, var bool: b, var bool: r); +predicate bool_le_imp(var bool: a, var bool: b, var bool: r); +predicate bool_lt_imp(var bool: a, var bool: b, var bool: r); + +predicate bool_or_imp(var bool: a, var bool: b, var bool: r); +predicate bool_and_imp(var bool: a, var bool: b, var bool: r); +predicate bool_xor_imp(var bool: a, var bool: b, var bool: r); + +predicate bool_clause_imp(array [int] of var bool: as, array [int] of var bool: bs, + var bool: b); +predicate array_bool_or_imp(array [int] of var bool: as, var bool: r); +predicate array_bool_and_imp(array [int] of var bool: as, var bool: r); + +predicate bool_lin_eq_imp(array [int] of int: as, array [int] of var bool: bs, + var int: c, var bool: r); +predicate bool_lin_le_imp(array [int] of int: as, array [int] of var bool: bs, + var int: c, var bool: r); +predicate bool_lin_lt_imp(array [int] of int: as, array [int] of var bool: bs, + var int: c, var bool: r); +predicate bool_lin_ne_imp(array [int] of int: as, array [int] of var bool: bs, + var int: c, var bool: r); + +%% Special cases for binary-ish x, y. +predicate int_lin_eq_reif( + array [int] of int: cs, array [int] of var int: xs, int: k, var bool: r) = + let { var bool: a; var bool: b } in + (r <-> (a /\ b)) /\ int_lin_le_reif(cs, xs, k, a) + /\ int_lin_le_reif([-c | c in cs], xs, -k, b); + +predicate bool_xor(var bool: x, var bool: y, var bool: r) = + bool_clause([x,y],[r]) + /\ bool_clause([],[x,y,r]) + /\ bool_clause([x, r], [y]) + /\ bool_clause([y, r], [x]); + +predicate set_in_reif(var int: x, set of int: s, var bool: r) = + if card(s) = max(s) - min(s) + 1 then + r <-> (x >= min(s) /\ x <= max(s)) + else + bool_clause([x = k | k in s], [r]) /\ forall (k in s) (x = k -> r) + endif; + +predicate int_pow(var int: x, var int: y, var int: z) = + let { + array [dom(x), dom(y)] of int: A = array2d( + dom(x), + dom(y), + [ pow(a, b) | a in dom(x), b in dom(y) ] + ); + } in z = A[x, y]; + +predicate int_pow(var int: x, int: y, var int: z) = + if y = 0 then z = 1 + elseif y = 1 then z = x + else + let { var int: zp ::is_defined_var ; + constraint int_pow(x, y div 2, zp) :: defines_var(zp); } in + if y mod 2 = 0 then + z = zp * zp + else + z = x * zp * zp + endif + endif; diff --git a/share/minizinc/gecode_presolver/fzn_all_different_int.mzn b/share/minizinc/gecode_presolver/fzn_all_different_int.mzn new file mode 100644 index 0000000..d6d623f --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_all_different_int.mzn @@ -0,0 +1,48 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_all_different_int(array[int] of var int: x); +%predicate all_different_int(array[int] of var int: x) = gecode_all_different_int(x); +predicate fzn_all_different_int(array[int] of var int: xs) = + let { + set of int: cs = {fix(x) | x in xs where is_fixed(x)}; + array[int] of var int: ys = [x | x in xs where not is_fixed(x)]; + constraint forall(y in ys, c in cs) (y != c); + } in + if length(ys) <= 1 then true + elseif length(ys) == 2 then ys[1] != ys[2] + else gecode_all_different_int(ys) + endif; diff --git a/share/minizinc/gecode_presolver/fzn_all_equal_int.mzn b/share/minizinc/gecode_presolver/fzn_all_equal_int.mzn new file mode 100644 index 0000000..ebb2ec5 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_all_equal_int.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2010-03-31 19:18:34 +1100 (Wed, 31 Mar 2010) $ by $Author: tack $ +% $Revision: 10618 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_all_equal_int(array[int] of var int: x); diff --git a/share/minizinc/gecode_presolver/fzn_among.mzn b/share/minizinc/gecode_presolver/fzn_among.mzn new file mode 100644 index 0000000..13bc3c9 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_among.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-08-22 05:22:06 +1000 (Mon, 22 Aug 2011) $ by $Author: tack $ +% $Revision: 12325 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_among(var int: n, array[int] of var int: x, set of int: v); diff --git a/share/minizinc/gecode_presolver/fzn_arg_max_int.mzn b/share/minizinc/gecode_presolver/fzn_arg_max_int.mzn new file mode 100644 index 0000000..3d551ac --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_arg_max_int.mzn @@ -0,0 +1,4 @@ +predicate gecode_maximum_arg_int(array[int] of var int: x, var int: i); + +predicate fzn_maximum_arg_int(array[int] of var int: x, var int: i) = + gecode_maximum_arg_int(x,(i-min(index_set(x)))::domain); diff --git a/share/minizinc/gecode_presolver/fzn_arg_min_int.mzn b/share/minizinc/gecode_presolver/fzn_arg_min_int.mzn new file mode 100644 index 0000000..6df1c66 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_arg_min_int.mzn @@ -0,0 +1,4 @@ +predicate gecode_minimum_arg_int(array[int] of var int: x, var int: i); + +predicate fzn_minimum_arg_int(array[int] of var int: x, var int: i) = + gecode_minimum_arg_int(x,(i-min(index_set(x)))::domain); diff --git a/share/minizinc/gecode_presolver/fzn_at_least_int.mzn b/share/minizinc/gecode_presolver/fzn_at_least_int.mzn new file mode 100644 index 0000000..c75cb6d --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_at_least_int.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_at_least_int(int: n, array[int] of var int: x, int: v); diff --git a/share/minizinc/gecode_presolver/fzn_at_least_set.mzn b/share/minizinc/gecode_presolver/fzn_at_least_set.mzn new file mode 100644 index 0000000..4705b7c --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_at_least_set.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_at_least_set(int: n, array[int] of var set of int: x, set of int: v) = + bool_sum_ge([x[i] == v | i in index_set(x)], n); diff --git a/share/minizinc/gecode_presolver/fzn_at_most_int.mzn b/share/minizinc/gecode_presolver/fzn_at_most_int.mzn new file mode 100644 index 0000000..93ad60c --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_at_most_int.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_at_most_int(int: n, array[int] of var int: x, int: v); diff --git a/share/minizinc/gecode_presolver/fzn_at_most_set.mzn b/share/minizinc/gecode_presolver/fzn_at_most_set.mzn new file mode 100644 index 0000000..f77c554 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_at_most_set.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_at_most_set(int: n, array[int] of var set of int: x, set of int: v) = + bool_sum_le([x[i] == v | i in index_set(x)], n); diff --git a/share/minizinc/gecode_presolver/fzn_bin_packing.mzn b/share/minizinc/gecode_presolver/fzn_bin_packing.mzn new file mode 100644 index 0000000..3a8ca42 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_bin_packing.mzn @@ -0,0 +1,46 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2010 +% +% Last modified: +% $Date: 2013-07-19 10:50:31 +1000 (Fri, 19 Jul 2013) $ by $Author: tack $ +% $Revision: 13902 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +include "bin_packing_capa.mzn"; + +predicate fzn_bin_packing(int: c, + array[int] of var int: bin, + array[int] of int: w) = + let { + set of int: idx = lb_array(bin)..ub_array(bin), + array[idx] of int: capa = array1d(idx, [c|i in idx]) + } in + bin_packing_capa(capa,bin,w); diff --git a/share/minizinc/gecode_presolver/fzn_bin_packing_capa.mzn b/share/minizinc/gecode_presolver/fzn_bin_packing_capa.mzn new file mode 100644 index 0000000..c8b6104 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_bin_packing_capa.mzn @@ -0,0 +1,47 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2010 +% +% Last modified: +% $Date: 2013-07-19 10:50:31 +1000 (Fri, 19 Jul 2013) $ by $Author: tack $ +% $Revision: 13902 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +include "bin_packing_load.mzn"; + +predicate fzn_bin_packing_capa(array[int] of int: c, + array[int] of var int: bin, + array[int] of int: w) = + let { + array[min(index_set(c))..max(index_set(c))] of var 0..ub_array(c): l + } in ( + forall( i in index_set(l) ) ( l[i] <= c[i] ) + /\ bin_packing_load(l,bin,w) + ); diff --git a/share/minizinc/gecode_presolver/fzn_bin_packing_load.mzn b/share/minizinc/gecode_presolver/fzn_bin_packing_load.mzn new file mode 100644 index 0000000..31d10ff --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_bin_packing_load.mzn @@ -0,0 +1,45 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2010 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_bin_packing_load(array[int] of var int: l, + array[int] of var int: bin, + array[int] of int: w, + int: minIndex); + +predicate fzn_bin_packing_load(array[int] of var int: l, + array[int] of var int: bin, + array[int] of int: w) = + gecode_bin_packing_load(l,bin,w,min(index_set(l))); diff --git a/share/minizinc/gecode_presolver/fzn_circuit.mzn b/share/minizinc/gecode_presolver/fzn_circuit.mzn new file mode 100644 index 0000000..18736a6 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_circuit.mzn @@ -0,0 +1,44 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-10-22 09:04:10 +1100 (Mon, 22 Oct 2012) $ by $Author: tack $ +% $Revision: 13159 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_circuit(int: offset, array[int] of var int: x); + +predicate fzn_circuit(array[int] of var int: x) = + if min(index_set(x)) >= 0 then + gecode_circuit(min(index_set(x)),x) + else + gecode_circuit(0,[x[i]-min(index_set(x)) | i in index_set(x)]) + endif; diff --git a/share/minizinc/gecode_presolver/fzn_count_eq.mzn b/share/minizinc/gecode_presolver/fzn_count_eq.mzn new file mode 100644 index 0000000..b8296f9 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_count_eq.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-08-14 10:15:14 +1000 (Tue, 14 Aug 2012) $ by $Author: tack $ +% $Revision: 12979 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_count(array[int] of var int: x, var int: y, var int: c); + +predicate fzn_count_eq(array[int] of var int: x, var int: y, var int: c) = + gecode_count(x, y, c); diff --git a/share/minizinc/gecode_presolver/fzn_count_eq_reif.mzn b/share/minizinc/gecode_presolver/fzn_count_eq_reif.mzn new file mode 100644 index 0000000..240e7f4 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_count_eq_reif.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-08-14 10:15:14 +1000 (Tue, 14 Aug 2012) $ by $Author: tack $ +% $Revision: 12979 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_count_reif(array[int] of var int: x, var int: y, var int: c, + var bool: b); + +predicate fzn_count_eq_reif(array[int] of var int: x, var int: y, var int: c, + var bool: b) = + gecode_count_reif(x, y, c, b); diff --git a/share/minizinc/gecode_presolver/fzn_cumulative.mzn b/share/minizinc/gecode_presolver/fzn_cumulative.mzn new file mode 100644 index 0000000..9ba26e7 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_cumulative.mzn @@ -0,0 +1,53 @@ +% +% Main authors: +% Guido Tack +% Mikael lagerkvist +% +% Copyright: +% Guido Tack, 2007 +% Mikael Lagerkvist, 2009 +% +% Last modified: +% $Date: 2011-03-08 19:54:48 +1100 (Tue, 08 Mar 2011) $ by $Author: tack $ +% $Revision: 11787 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + cumulatives(s, d, r, b) + ) + ); + +% Specialized cumulatives propagator is only for fixed bounds +predicate cumulatives(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b); diff --git a/share/minizinc/gecode_presolver/fzn_decreasing_bool.mzn b/share/minizinc/gecode_presolver/fzn_decreasing_bool.mzn new file mode 100644 index 0000000..8e9e161 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_decreasing_bool.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-05-19 04:08:39 +1000 (Thu, 19 May 2011) $ by $Author: tack $ +% $Revision: 12007 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_decreasing_bool(array[int] of var bool: x); diff --git a/share/minizinc/gecode_presolver/fzn_decreasing_int.mzn b/share/minizinc/gecode_presolver/fzn_decreasing_int.mzn new file mode 100644 index 0000000..8282f1c --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_decreasing_int.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2010-03-31 19:18:34 +1100 (Wed, 31 Mar 2010) $ by $Author: tack $ +% $Revision: 10618 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_decreasing_int(array[int] of var int: x); diff --git a/share/minizinc/gecode_presolver/fzn_diffn.mzn b/share/minizinc/gecode_presolver/fzn_diffn.mzn new file mode 100644 index 0000000..01da5de --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_diffn.mzn @@ -0,0 +1,44 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-06-04 01:27:50 +1000 (Sat, 04 Jun 2011) $ by $Author: tack $ +% $Revision: 12041 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_nooverlap(array[int] of var int: x, array[int] of var int: w, + array[int] of var int: y, array[int] of var int: h); + +predicate fzn_diffn(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy) = + gecode_nooverlap(x,dx,y,dy); diff --git a/share/minizinc/gecode_presolver/fzn_disjoint.mzn b/share/minizinc/gecode_presolver/fzn_disjoint.mzn new file mode 100644 index 0000000..1541247 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_disjoint.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_disjoint(var set of int: s1, var set of int: s2); diff --git a/share/minizinc/gecode_presolver/fzn_distribute.mzn b/share/minizinc/gecode_presolver/fzn_distribute.mzn new file mode 100644 index 0000000..27c090f --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_distribute.mzn @@ -0,0 +1,46 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-05-19 04:08:39 +1000 (Thu, 19 May 2011) $ by $Author: tack $ +% $Revision: 12007 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_distribute(array[int] of var int: card, + array[int] of var int: value, + array[int] of var int: base) = + assert(index_set(card) == index_set(value), + "distribute: card and value arrays must have identical index sets", + forall (i in index_set(card)) ( + bool_sum_eq([value[i] == base[j] | j in index_set(base)], + card[i]) + ) + ); diff --git a/share/minizinc/gecode_presolver/fzn_exactly_int.mzn b/share/minizinc/gecode_presolver/fzn_exactly_int.mzn new file mode 100644 index 0000000..a22daba --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_exactly_int.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +include "count.mzn"; + +predicate fzn_exactly_int(int: n, array[int] of var int: x, int: v) = + count(x, v, n); diff --git a/share/minizinc/gecode_presolver/fzn_exactly_set.mzn b/share/minizinc/gecode_presolver/fzn_exactly_set.mzn new file mode 100644 index 0000000..bd0b07b --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_exactly_set.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_exactly_set(int: n, array[int] of var set of int: x, set of int: v) = + bool_sum_eq([x[i] == v | i in index_set(x)], n); diff --git a/share/minizinc/gecode_presolver/fzn_global_cardinality.mzn b/share/minizinc/gecode_presolver/fzn_global_cardinality.mzn new file mode 100644 index 0000000..47e9a15 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_global_cardinality.mzn @@ -0,0 +1,43 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2013-03-07 12:18:29 +1100 (Thu, 07 Mar 2013) $ by $Author: mears $ +% $Revision: 13455 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_global_cardinality(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts); + +predicate global_cardinality_old(array[int] of var int: x, + array[int] of var int: c) = + global_cardinality(x,[i|i in index_set(c)],c); diff --git a/share/minizinc/gecode_presolver/fzn_global_cardinality_closed.mzn b/share/minizinc/gecode_presolver/fzn_global_cardinality_closed.mzn new file mode 100644 index 0000000..a5143e6 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_global_cardinality_closed.mzn @@ -0,0 +1,39 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-01-14 08:42:12 +1100 (Fri, 14 Jan 2011) $ by $Author: tack $ +% $Revision: 11531 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_global_cardinality_closed(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts); diff --git a/share/minizinc/gecode_presolver/fzn_global_cardinality_low_up.mzn b/share/minizinc/gecode_presolver/fzn_global_cardinality_low_up.mzn new file mode 100644 index 0000000..c1ebc14 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_global_cardinality_low_up.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2010-10-07 08:10:08 +1100 (Thu, 07 Oct 2010) $ by $Author: schulte $ +% $Revision: 11466 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_global_cardinality_low_up(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound); diff --git a/share/minizinc/gecode_presolver/fzn_global_cardinality_low_up_closed.mzn b/share/minizinc/gecode_presolver/fzn_global_cardinality_low_up_closed.mzn new file mode 100644 index 0000000..e790e2b --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_global_cardinality_low_up_closed.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-05-19 04:08:39 +1000 (Thu, 19 May 2011) $ by $Author: tack $ +% $Revision: 12007 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_global_cardinality_low_up_closed(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound); diff --git a/share/minizinc/gecode_presolver/fzn_increasing_bool.mzn b/share/minizinc/gecode_presolver/fzn_increasing_bool.mzn new file mode 100644 index 0000000..96f655d --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_increasing_bool.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_increasing_bool(array[int] of var bool: x); diff --git a/share/minizinc/gecode_presolver/fzn_increasing_int.mzn b/share/minizinc/gecode_presolver/fzn_increasing_int.mzn new file mode 100644 index 0000000..c54e83d --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_increasing_int.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_increasing_int(array[int] of var int: x); diff --git a/share/minizinc/gecode_presolver/fzn_int_set_channel.mzn b/share/minizinc/gecode_presolver/fzn_int_set_channel.mzn new file mode 100644 index 0000000..752791b --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_int_set_channel.mzn @@ -0,0 +1,49 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_int_set_channel(array[int] of var int: x, int: xoff, + array[int] of var set of int: y, int: yoff); + +predicate fzn_int_set_channel(array[int] of var int: x, + array[int] of var set of int: y) = + if (min(index_set(x)) < 0 \/ min(index_set(y)) < 0) then + forall(i in index_set(x)) (x[i] in index_set(y)) /\ + forall(j in index_set(y)) (y[j] subset index_set(x)) /\ + forall(i in index_set(x), j in index_set(y)) + (x[i]=j <-> i in y[j]) + else + gecode_int_set_channel(x,min(index_set(x)),y,min(index_set(y))) + endif; diff --git a/share/minizinc/gecode_presolver/fzn_inverse.mzn b/share/minizinc/gecode_presolver/fzn_inverse.mzn new file mode 100644 index 0000000..7c83804 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_inverse.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_inverse_offsets(array[int] of var int: f, int: foff, + array[int] of var int: invf, int: invfoff); + +predicate fzn_inverse(array[int] of var int: f, + array[int] of var int: invf) = + gecode_inverse_offsets(f, min(index_set(invf)), invf, min(index_set(f))); diff --git a/share/minizinc/gecode_presolver/fzn_inverse_set.mzn b/share/minizinc/gecode_presolver/fzn_inverse_set.mzn new file mode 100644 index 0000000..2e27aec --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_inverse_set.mzn @@ -0,0 +1,53 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% +predicate gecode_inverse_set(array[int] of var set of int: f, + array[int] of var set of int: invf, int: xoff, int: yoff); + +predicate fzn_inverse_set(array[int] of var set of int: f, + array[int] of var set of int: invf) = + forall(i in index_set(f)) ( + f[i] subset index_set(invf) + ) /\ + forall(j in index_set(invf)) ( + invf[j] subset index_set(f) + ) /\ + if (min(index_set(f)) >= 0 /\ min(index_set(invf)) >= 0) then + gecode_inverse_set(f,invf,min(index_set(f)),min(index_set(invf))) + else + forall(i in index_set(f), j in index_set(invf)) ( + (j in f[i] <-> i in invf[j]) + ) + endif; diff --git a/share/minizinc/gecode_presolver/fzn_lex_less_bool.mzn b/share/minizinc/gecode_presolver/fzn_lex_less_bool.mzn new file mode 100644 index 0000000..43b4409 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_lex_less_bool.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-04-11 14:59:27 +1000 (Wed, 11 Apr 2012) $ by $Author: tack $ +% $Revision: 12730 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_array_bool_lt(array[int] of var bool: x, + array[int] of var bool: y); + +predicate fzn_lex_less_bool(array[int] of var bool: x, + array[int] of var bool: y) = + gecode_array_bool_lt(x,y); diff --git a/share/minizinc/gecode_presolver/fzn_lex_less_int.mzn b/share/minizinc/gecode_presolver/fzn_lex_less_int.mzn new file mode 100644 index 0000000..62c540b --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_lex_less_int.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_array_int_lt(array[int] of var int: x, + array[int] of var int: y); + +predicate fzn_lex_less_int(array[int] of var int: x, + array[int] of var int: y) = + gecode_array_int_lt(x,y); diff --git a/share/minizinc/gecode_presolver/fzn_lex_lesseq_bool.mzn b/share/minizinc/gecode_presolver/fzn_lex_lesseq_bool.mzn new file mode 100644 index 0000000..e735ba1 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_lex_lesseq_bool.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-04-11 14:59:27 +1000 (Wed, 11 Apr 2012) $ by $Author: tack $ +% $Revision: 12730 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_array_bool_lq(array[int] of var bool: x, + array[int] of var bool: y); + +predicate fzn_lex_lesseq_bool(array[int] of var bool: x, + array[int] of var bool: y) = + gecode_array_bool_lq(x,y); diff --git a/share/minizinc/gecode_presolver/fzn_lex_lesseq_int.mzn b/share/minizinc/gecode_presolver/fzn_lex_lesseq_int.mzn new file mode 100644 index 0000000..f146c80 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_lex_lesseq_int.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_array_int_lq(array[int] of var int: x, + array[int] of var int: y); + +predicate fzn_lex_lesseq_int(array[int] of var int: x, + array[int] of var int: y) = + gecode_array_int_lq(x,y); diff --git a/share/minizinc/gecode_presolver/fzn_link_set_to_booleans.mzn b/share/minizinc/gecode_presolver/fzn_link_set_to_booleans.mzn new file mode 100644 index 0000000..ece355b --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_link_set_to_booleans.mzn @@ -0,0 +1,44 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_link_set_to_booleans(var set of int: s, array[int] of var bool: b, int: idx); + +predicate fzn_link_set_to_booleans(var set of int: s, array[int] of var bool: b) = + if min(index_set(b)) < 0 then + forall(i in index_set(b)) ( b[i] <-> i in s ) + else + gecode_link_set_to_booleans(s,b,min(index_set(b))) + endif diff --git a/share/minizinc/gecode_presolver/fzn_member_bool.mzn b/share/minizinc/gecode_presolver/fzn_member_bool.mzn new file mode 100644 index 0000000..ef25902 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_member_bool.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_member_bool(array[int] of var bool: x, var bool: y); diff --git a/share/minizinc/gecode_presolver/fzn_member_bool_reif.mzn b/share/minizinc/gecode_presolver/fzn_member_bool_reif.mzn new file mode 100644 index 0000000..e4699d8 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_member_bool_reif.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_member_bool_reif(array[int] of var bool: x, var bool: y, + var bool: b); +predicate fzn_member_bool_reif(array[int] of var bool: x, var bool: y, + var bool: b) = gecode_member_bool_reif(x,y,b); diff --git a/share/minizinc/gecode_presolver/fzn_member_int.mzn b/share/minizinc/gecode_presolver/fzn_member_int.mzn new file mode 100644 index 0000000..0b280f9 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_member_int.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_member_int(array[int] of var int: x, var int: y); diff --git a/share/minizinc/gecode_presolver/fzn_member_int_reif.mzn b/share/minizinc/gecode_presolver/fzn_member_int_reif.mzn new file mode 100644 index 0000000..2969501 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_member_int_reif.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_member_int_reif(array[int] of var int: x, var int: y, + var bool: b); +predicate member_int_reif(array[int] of var int: x, var int: y, + var bool: b) = gecode_member_int_reif(x,y,b); diff --git a/share/minizinc/gecode_presolver/fzn_nvalue.mzn b/share/minizinc/gecode_presolver/fzn_nvalue.mzn new file mode 100644 index 0000000..51fe9b0 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_nvalue.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-08-18 17:42:10 +1000 (Thu, 18 Aug 2011) $ by $Author: tack $ +% $Revision: 12312 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_nvalue(var int: n, array[int] of var int: x); diff --git a/share/minizinc/gecode_presolver/fzn_partition_set.mzn b/share/minizinc/gecode_presolver/fzn_partition_set.mzn new file mode 100644 index 0000000..90fd97f --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_partition_set.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_array_set_partition(array[int] of var set of int: S, + set of int: universe); + +predicate fzn_partition_set(array[int] of var set of int: S, + set of int: universe) = + gecode_array_set_partition(S, universe); diff --git a/share/minizinc/gecode_presolver/fzn_range.mzn b/share/minizinc/gecode_presolver/fzn_range.mzn new file mode 100644 index 0000000..446bce7 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_range.mzn @@ -0,0 +1,51 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% +predicate gecode_range(array[int] of var int: x, int: xoff, + var set of int: s, var set of int: t); + +predicate fzn_range(array[int] of var int: x, var set of int: s, + var set of int: t) = + assert(ub(s) subset index_set(x), + "range: upper bound of 's' must be a subset of the index set of 'x'", + if (min(index_set(x)) >= 0) then + gecode_range(x,min(index_set(x)),s,t) + else + forall(i in ub(s)) (i in s -> x[i] in t) /\ + forall(i in ub(t)) ( + i in t -> exists(j in ub(s)) ( j in s /\ x[j] == i ) + ) + endif + ); diff --git a/share/minizinc/gecode_presolver/fzn_regular.mzn b/share/minizinc/gecode_presolver/fzn_regular.mzn new file mode 100644 index 0000000..940f530 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_regular.mzn @@ -0,0 +1,38 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F); diff --git a/share/minizinc/gecode_presolver/fzn_roots.mzn b/share/minizinc/gecode_presolver/fzn_roots.mzn new file mode 100644 index 0000000..5a89c31 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_roots.mzn @@ -0,0 +1,63 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +include "int_set_channel.mzn"; + +% i in z <-> exists (j in x) (i in y[j]) +predicate gecode_array_set_element_union(var set of int: x, + array[int] of var set of int: y, var set of int: z); + +predicate fzn_roots(array[int] of var int: x, var set of int: s, + var set of int: t) = + assert(ub(s) subset index_set(x), + "roots: upper bound of 's' must be a subset of the index set of 'x'", + if (min(index_set(x)) < 0 \/ min(ub(t)) < 1) then + % All values in 's' must map to a value in 't'. + forall(i in ub(s)) ( + i in s -> x[i] in t + ) /\ + % All values in 't' must be mapped from a value in 's'. + forall(i in ub(t)) ( + i in t -> forall(j in index_set(x)) (x[j] == i -> j in s) + ) + else let { + array[1..max(ub(t))] of var set of 0..max(index_set(x)): y, + } in + int_set_channel(x,y) + /\ + gecode_array_set_element_union(t,y,s) + endif + ); diff --git a/share/minizinc/gecode_presolver/fzn_sort.mzn b/share/minizinc/gecode_presolver/fzn_sort.mzn new file mode 100644 index 0000000..6c97cbc --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_sort.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_sort(array[int] of var int: x, array[int] of var int: y); diff --git a/share/minizinc/gecode_presolver/fzn_sum_pred.mzn b/share/minizinc/gecode_presolver/fzn_sum_pred.mzn new file mode 100644 index 0000000..91ca67e --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_sum_pred.mzn @@ -0,0 +1,42 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-03-19 11:56:37 +1100 (Mon, 19 Mar 2012) $ by $Author: tack $ +% $Revision: 12583 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_set_weights(array[int] of int: csi,array[int] of int: cs, + var set of int: x, var int: y); + +predicate fzn_sum_pred(var int: i, array[int] of set of int: sets, + array[int] of int: cs, var int: s) = + gecode_set_weights([j|j in index_set(cs)],cs,sets[i],s); diff --git a/share/minizinc/gecode_presolver/fzn_table_bool.mzn b/share/minizinc/gecode_presolver/fzn_table_bool.mzn new file mode 100644 index 0000000..0c9eb6c --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_table_bool.mzn @@ -0,0 +1,37 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_table_bool(array[int] of var bool: x, array[int, int] of bool: t); diff --git a/share/minizinc/gecode_presolver/fzn_table_int.mzn b/share/minizinc/gecode_presolver/fzn_table_int.mzn new file mode 100644 index 0000000..19c93d2 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_table_int.mzn @@ -0,0 +1,39 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2009-09-09 01:42:03 +1000 (Wed, 09 Sep 2009) $ by $Author: schulte $ +% $Revision: 9689 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate fzn_table_int(array[int] of var int: x, array[int, int] of int: t) = + fzn_table_int(x, array1d(t)); +predicate fzn_table_int(array[int] of var int: x, array[int] of int: t); diff --git a/share/minizinc/gecode_presolver/fzn_value_precede_int.mzn b/share/minizinc/gecode_presolver/fzn_value_precede_int.mzn new file mode 100644 index 0000000..cd8c062 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_value_precede_int.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2013-07-19 22:10:52 +1000 (Fri, 19 Jul 2013) $ by $Author: schulte $ +% $Revision: 13911 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_precede(array[int] of var int: x, int: s, int: t); + +predicate fzn_value_precede_int(int: s, int: t, array[int] of var int: x) = + gecode_precede(x,s,t); diff --git a/share/minizinc/gecode_presolver/fzn_value_precede_set.mzn b/share/minizinc/gecode_presolver/fzn_value_precede_set.mzn new file mode 100644 index 0000000..30d70e1 --- /dev/null +++ b/share/minizinc/gecode_presolver/fzn_value_precede_set.mzn @@ -0,0 +1,40 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2013-07-19 22:10:52 +1000 (Fri, 19 Jul 2013) $ by $Author: schulte $ +% $Revision: 13911 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_precede_set(array[int] of var set of int: x, int: s, int: t); + +predicate fzn_value_precede_set(int: s, int: t, array[int] of var set of int: x) = + gecode_precede_set(x,s,t); diff --git a/share/minizinc/gecode_presolver/gecode.mzn b/share/minizinc/gecode_presolver/gecode.mzn new file mode 100644 index 0000000..367b0c1 --- /dev/null +++ b/share/minizinc/gecode_presolver/gecode.mzn @@ -0,0 +1,170 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2015-01-05 17:33:06 +1100 (Mon, 05 Jan 2015) $ by $Author: tack $ +% $Revision: 14337 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + + +/*** + @groupdef gecode Additional declarations for Gecode + + These annotations and predicates are available for the Gecode + solver. In order to use them in a model, include the file "gecode.mzn". +*/ + +/*** + @groupdef gecode.annotations Additional Gecode search annotations +*/ + +/** @group gecode.annotations Select variable with smallest accumulated failure count */ +annotation afc_min; +/** @group gecode.annotations Select variable with smallest accumulated failure count divided by domain size */ +annotation afc_size_min; +/** @group gecode.annotations Select variable with largest accumulated failure count */ +annotation afc_max; +/** @group gecode.annotations Select variable with largest accumulated failure count divided by domain size */ +annotation afc_size_max; +/** @group gecode.annotations Select variable with smallest activity count */ +annotation activity_min; +/** @group gecode.annotations Select variable with smallest activity count divided by domain size */ +annotation activity_size_min; +/** @group gecode.annotations Select variable with largest activity count */ +annotation activity_max; +/** @group gecode.annotations Select variable with largest activity count divided by domain size */ +annotation activity_size_max; +/** @group gecode.annotations Select random variable */ +annotation random; + +/** @group gecode.annotations Specify default search strategy for integer variables to use variable selection + strategy \a varsel, and value choice strategy \a valsel. */ +annotation int_default_search(ann: varsel, ann: valsel); +/** @group gecode.annotations Specify default search strategy for Boolean variables to use variable selection + strategy \a varsel, and value choice strategy \a valsel. */ +annotation bool_default_search(ann: varsel, ann: valsel); +/** @group gecode.annotations Specify default search strategy for set variables to use variable selection + strategy \a varsel, and value choice strategy \a valsel. */ +annotation set_default_search(ann: varsel, ann: valsel); +/** @group gecode.annotations Specify default search strategy for float variables to use variable selection + strategy \a varsel, and value choice strategy \a valsel. */ +annotation float_default_search(ann: varsel, ann: valsel); + +/** @group gecode.annotations + Simple large neighbourhood search strategy: upon restart, for each variable in \a x, + the probability of it being fixed to the previous solution is \a percentage (out of 100). +*/ +annotation relax_and_reconstruct(array[int] of var int: x, int: percentage); + +/** @group gecode.annotations + Simple large neighbourhood search strategy: upon restart, for each variable in \a x, + the probability of it being fixed to the previous solution is \a percentage (out of 100). + Start from an initial solution \a y. +*/ +annotation relax_and_reconstruct(array[int] of var int: x, int: percentage, array[int] of int: y); + +/*** + @groupdef gecode.constraints Additional Gecode constraints +*/ + +/** @group gecode.constraints + Constrain \a z to be the intersection of all sets + in \a y that are selected by \a x: \(i \in \a z \leftrightarrow \forall j \in \a x: (i \in \a y[j]) \) +*/ +predicate gecode_array_set_element_intersect(var set of int: x, + array[int] of var set of int: y, var set of int: z); + +/** @group gecode.constraints + Constrain \a z to be the disjoint union of all sets + in \a y that are selected by \a x: \(i \in \a z \leftrightarrow \exists j \in \a x: (i \in \a y[j]) \) + and \( i \in \a x \land j \in \a x \land i\neq j \rightarrow \a y[i] \cap \a y[j]=\emptyset \) +*/ +predicate gecode_array_set_element_partition(var set of int: x, + array[int] of var set of int: y, var set of int: z); + +/** @group gecode.constraints + Constrain \a z to be a subset of \a u, and \a z to be the intersection of all sets + in \a y that are selected by \a x: \(i \in \a z \leftrightarrow \forall j \in \a x: (i \in \a y[j]) \) +*/ +predicate gecode_array_set_element_intersect_in(var set of int: x, + array[int] of var set of int: y, + var set of int: z, set of int: u); + +predicate gecode_among_seq_int(array[int] of var int: x, set of int: S, + int: l, int: m, int: n); + +predicate gecode_among_seq_bool(array[int] of var bool: x, bool: b, + int: l, int: m, int: n); + +/** @group gecode.constraints + Every subsequence of \a x of length \a l has at least \a m and at most \a n occurrences + of the values in \a S +*/ +predicate among_seq(array[int] of var int: x, set of int: S, + int: l, int: m, int: n) = gecode_among_seq_int(x,S,l,m,n); +/** @group gecode.constraints + Every subsequence of \a x of length \a l has at least \a m and at most \a n occurrences + of the values in \a S +*/ +predicate among_seq(array[int] of var bool: x, bool: b, + int: l, int: m, int: n) = gecode_among_seq_bool(x,b,l,m,n); + +predicate gecode_circuit_cost_array(array[int] of int: c, + array[int] of var int: x, array[int] of var int: y, var int: z); +predicate gecode_circuit_cost(array[int] of int: c, array[int] of var int: x, + var int: z); + +/** @group gecode.constraints + Constrains the elements of \a x to define a circuit where \a x[\p i] = \p j means + that \p j is the successor of \p i. Additionally, constrain \a z to + be the cost of the circuit. Each edge cost is defined by array \a c. The variables + \a y[i] are constrained to be the edge cost of the node \a x[i]. +*/ +predicate circuit_cost_array(array[int] of int: c, + array[int] of var int: x, array[int] of var int: y, var int: z) = + gecode_circuit_cost_array(c,[x[i]-min(index_set(x)) | i in index_set(x)], + y,z); +/** @group gecode.constraints + Constrains the elements of \a x to define a circuit where \a x[\p i] = \p j means + that \p j is the successor of \p i. Additionally, constrain \a z to + be the cost of the circuit. Each edge cost is defined by array \a c. +*/ +predicate circuit_cost(array[int] of int: c, array[int] of var int: x, + var int: z) = + gecode_circuit_cost(c, [x[i]-min(index_set(x)) | i in index_set(x)], z); + +predicate gecode_schedule_unary(array[int] of var int: x, array[int] of int: p); +predicate gecode_schedule_unary_optional(array[int] of var int: x, + array[int] of int: p, array[int] of var bool: m); + +predicate gecode_schedule_cumulative_optional(array[int] of var int: start, + array[int] of int: duration, array[int] of int: usage, + array[int] of var bool: m, int: capacity); diff --git a/share/minizinc/gecode_presolver/precedence.mzn b/share/minizinc/gecode_presolver/precedence.mzn new file mode 100644 index 0000000..28abe16 --- /dev/null +++ b/share/minizinc/gecode_presolver/precedence.mzn @@ -0,0 +1,41 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2011-07-12 21:52:11 +1000 (Tue, 12 Jul 2011) $ by $Author: tack $ +% $Revision: 12175 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +predicate gecode_precede(array[int] of var int: x, array[int] of int: c); + +predicate precedence(array[int] of var int: x) = + forall (i in index_set(x)) (x[i] <= length(x)) /\ + gecode_precede(x, [i|i in 1..length(x)]); diff --git a/share/minizinc/gecode_presolver/redefinitions-2.0.mzn b/share/minizinc/gecode_presolver/redefinitions-2.0.mzn new file mode 100644 index 0000000..9d7d19f --- /dev/null +++ b/share/minizinc/gecode_presolver/redefinitions-2.0.mzn @@ -0,0 +1,33 @@ +% This file contains redefinitions of standard builtins that can be overridden +% by solvers. + +predicate bool_clause_reif(array[int] of var bool: as, + array[int] of var bool: bs, + var bool: b) = + clause(as,bs++[b]) /\ + forall (i in index_set(as)) (as[i] -> b) /\ + forall (i in index_set(bs)) (bs[i] \/ b); + +predicate array_int_maximum(var int: m, array[int] of var int: x); + +predicate array_float_maximum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +predicate array_int_minimum(var int: m, array[int] of var int: x); + +predicate array_float_minimum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); diff --git a/share/minizinc/gecode_presolver/redefinitions.mzn b/share/minizinc/gecode_presolver/redefinitions.mzn new file mode 100644 index 0000000..9d2f1b5 --- /dev/null +++ b/share/minizinc/gecode_presolver/redefinitions.mzn @@ -0,0 +1,75 @@ +% +% Main authors: +% Guido Tack +% +% Copyright: +% Guido Tack, 2007 +% +% Last modified: +% $Date: 2012-04-06 16:43:44 +1000 (Fri, 06 Apr 2012) $ by $Author: tack $ +% $Revision: 12706 $ +% +% This file is part of Gecode, the generic constraint +% development environment: +% http://www.gecode.org +% +% Permission is hereby granted, free of charge, to any person obtaining +% a copy of this software and associated documentation files (the +% "Software"), to deal in the Software without restriction, including +% without limitation the rights to use, copy, modify, merge, publish, +% distribute, sublicense, and/or sell copies of the Software, and to +% permit persons to whom the Software is furnished to do so, subject to +% the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +% LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +% OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +% WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +% +% + +% Sums over Boolean variables +predicate bool_lin_eq(array[int] of int: a, array[int] of var bool: x, + var int: c); +predicate bool_lin_ne(array[int] of int: a, array[int] of var bool: x, + var int: c); +predicate bool_lin_le(array[int] of int: a, array[int] of var bool: x, + var int: c); +predicate bool_lin_lt(array[int] of int: a, array[int] of var bool: x, + var int: c); +predicate bool_lin_ge(array[int] of int: a, array[int] of var bool: x, + var int: c); +predicate bool_lin_gt(array[int] of int: a, array[int] of var bool: x, + var int: c); + +predicate bool_sum_eq(array[int] of var bool: x, var int: c) = + bool_lin_eq([1 | i in index_set(x)],x,c); +predicate bool_sum_ne(array[int] of var bool: x, var int: c) = + bool_lin_ne([1 | i in index_set(x)],x,c); +predicate bool_sum_le(array[int] of var bool: x, var int: c) = + bool_lin_le([1 | i in index_set(x)],x,c); +predicate bool_sum_lt(array[int] of var bool: x, var int: c) = + bool_lin_lt([1 | i in index_set(x)],x,c); +predicate bool_sum_ge(array[int] of var bool: x, var int: c) = + bool_lin_ge([1 | i in index_set(x)],x,c); +predicate bool_sum_gt(array[int] of var bool: x, var int: c) = + bool_lin_gt([1 | i in index_set(x)],x,c); + +predicate float_sinh(var float: a, var float: b) = + b == (exp(a)-exp(-a))/2.0; + +predicate float_cosh(var float: a, var float: b) = + b == (exp(a)+exp(-a))/2.0; + +predicate float_tanh(var float: a, var float: b) = + let { var float: e2a = exp(2.0*a) } in + b == (e2a-1.0)/(e2a+1.0); + +predicate float_ne_reif(var float: a, var float: b, var bool: c) = + not c <-> a==b; diff --git a/share/minizinc/linear/CHANGELOG.txt b/share/minizinc/linear/CHANGELOG.txt new file mode 100644 index 0000000..8f53e03 --- /dev/null +++ b/share/minizinc/linear/CHANGELOG.txt @@ -0,0 +1,6 @@ + +2015-07-20 + Special handling of array_int_element which is a linear fn. No much diff on ProjPlanning_12_8. + [REVERTED] + Replaced array_int_element by the old (forall(.. -> ..)) + 82s vs 45s on ProjPlanning_12_8 \ No newline at end of file diff --git a/share/minizinc/linear/domain_encodings.mzn b/share/minizinc/linear/domain_encodings.mzn new file mode 100644 index 0000000..f2694f7 --- /dev/null +++ b/share/minizinc/linear/domain_encodings.mzn @@ -0,0 +1,111 @@ +/*%-----------------------------------------------------------------------------% +% Domain encodings +%-----------------------------------------------------------------------------% +*/ +% Linear equality encoding + + % Single variable: x = d <-> x_eq_d[d] +predicate equality_encoding(var int: x, array[int] of var int: x_eq_d) = + x in index_set(x_eq_d) + /\ + sum(d in dom(x))( x_eq_d[d] ) = 1 + /\ + sum(d in dom(x))( d * x_eq_d[d] ) = x + /\ +% my_trace( "eq_enc: \(x), index_set(pp)=" ++ show(index_set( x_eq_d )) ++ "\n" ) /\ + if fPostprocessDomains then + equality_encoding__POST(x, x_eq_d) + else true endif + ; + + % Two variables: x = d /\ y = e <-> x_eq_d[d] /\ y_eq_e[e] /\ xy_eq_de[d, e] +predicate equality_encoding(var int: x, var int: y, + array[int] of var int: x_eq_d, + array[int] of var int: y_eq_e, + array[int, int] of var int: xy_eq_de + ) = + x in index_set(x_eq_d) /\ + y in index_set(y_eq_e) /\ + index_set(x_eq_d) == index_set_1of2(xy_eq_de) /\ + index_set(y_eq_e) == index_set_2of2(xy_eq_de) /\ + sum(d in dom(x), e in dom(y))( xy_eq_de[d, e] ) = 1 + /\ + forall(d in dom(x)) (sum(e in dom(y))( xy_eq_de[d, e] ) = x_eq_d[d]) + /\ + forall(e in dom(y)) (sum(d in dom(x))( xy_eq_de[d, e] ) = y_eq_e[e]) + ; + + % Array of variables: x[i] = d <-> x_eq_d[i,d] +predicate equality_encoding(array[int] of var int: x, + array[int, int] of var int: x_eq_d) = + forall(i in index_set(x))( + x[i] in index_set_2of2(x_eq_d) + /\ + sum(d in index_set_2of2(x_eq_d))( x_eq_d[i,d] ) = 1 + /\ + sum(d in index_set_2of2(x_eq_d))( d * x_eq_d[i,d] ) = x[i] + ); + +function var int: eq_new_var(var int: x, int: i) ::promise_total = + if i in dom(x) then + let { + var 0..1: xi; + } in xi + else 0 endif; + +function array[int] of var int: eq_encode(var int: x) ::promise_total = + let { + array[int] of var int: y = array1d(lb(x)..ub(x),[eq_new_var(x,i) | i in lb(x)..ub(x)]); + constraint equality_encoding(x,y); +% constraint +% if card(dom(x))>0 then +% my_trace(" eq_encode: dom(\(x)) = " ++ show(dom(x)) ++ ", card( dom(\(x)) ) = " ++ show(card(dom(x))) ++ "\n") +% else true endif; + %% constraint assert(card(dom(x))>1, " eq_encode: card(dom(\(x))) == " ++ show(card(dom(x)))); + } in y; + +function array[int] of int: eq_encode(int: x) ::promise_total = + array1d(lb(x)..ub(x),[ if i=x then 1 else 0 endif | i in lb(x)..ub(x)]); + +%%% The same for 2 variables: +function var int: eq_new_var(var int: x, int: i, var int: y, int: j) ::promise_total = + if i in dom(x) /\ j in dom(y) then + let { + var 0..1: xi; + } in xi + else 0 endif; + +function array[int, int] of var int: eq_encode(var int: x, var int: y) ::promise_total = + let { + array[int] of var int: pX = eq_encode(x), + array[int] of var int: pY = eq_encode(y), + array[int, int] of var int: pp = array2d(index_set(pX), index_set(pY), + [eq_new_var(x,i,y,j) | i in index_set(pX), j in index_set(pY)]); + constraint equality_encoding(x, y, pX, pY, pp); + } in pp; + +function array[int, int] of int: eq_encode(int: x, int: y) ::promise_total = +% let { +% constraint if card(dom(x))*card(dom(y))>200 then +% my_trace(" eq_encode: dom(\(x)) = " ++ show(dom(x)) ++ ", dom(\(y)) = " ++ show(dom(y)) ++ "\n") +% else true endif; +% } in + array2d(lb(x)..ub(x), lb(y)..ub(y), + [if i==x /\ j==y then 1 else 0 endif | i in lb(x)..ub(x), j in lb(y)..ub(y)]); + +function array[int,int] of var int: eq_encode(array[int] of var int: x) ::promise_total = + let { + array[index_set(x),lb_array(x)..ub_array(x)] of var int: y = + array2d(index_set(x),lb_array(x)..ub_array(x), + [ let { + array[int] of var int: xi = eq_encode(x[i]) + } in if j in index_set(xi) then xi[j] else 0 endif + | i in index_set(x), j in lb_array(x)..ub_array(x)] + ) + } in y; + +function array[int,int] of int: eq_encode(array[int] of int: x) ::promise_total = + array2d(index_set(x),lb_array(x)..ub_array(x),[ if j=x[i] then 1 else 0 endif | i in index_set(x), j in lb_array(x)..ub_array(x)]); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear/fzn_all_different_int.mzn b/share/minizinc/linear/fzn_all_different_int.mzn new file mode 100644 index 0000000..58a425e --- /dev/null +++ b/share/minizinc/linear/fzn_all_different_int.mzn @@ -0,0 +1,23 @@ +%-----------------------------------------------------------------------------% +% 'all_different' constrains an array of objects to be all different. +% +% Linear version: equality encoding; see e.g. [Refalo, CP 2000] +% +% For a given d in dom(x), at most one i with x_i = d can exist. +%-----------------------------------------------------------------------------% + +include "domain_encodings.mzn"; + +predicate fzn_all_different_int(array[int] of var int: x) = + if length(x)<=1 then true + else + let { + array[int,int] of var 0..1: x_eq_d = eq_encode(x) + } in ( +% my_trace(" all_different_int: x[" ++ show(index_set(x)) ++ "]\n") /\ + forall(d in index_set_2of2(x_eq_d))( sum(i in index_set_1of2(x_eq_d))( x_eq_d[i,d] ) <= 1 ) + ) + endif; + + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear/fzn_alldifferent_except_0.mzn b/share/minizinc/linear/fzn_alldifferent_except_0.mzn new file mode 100644 index 0000000..d6b07df --- /dev/null +++ b/share/minizinc/linear/fzn_alldifferent_except_0.mzn @@ -0,0 +1,17 @@ +/** @group globals.alldifferent + Constrain the array of integers \a vs to be all different except those + elements that are assigned the value 0. +*/ +predicate fzn_alldifferent_except_0(array[int] of var int: vs) = +% forall(i, j in index_set(vs) where i < j) ( +% (vs[i] != 0 /\ vs[j] != 0) -> vs[i] != vs[j] +% ); + if length(vs)<=1 then true + else + let { + array[int,int] of var 0..1: x_eq_d = eq_encode(vs) + } in ( +% my_trace(" alldifferent_except_0: x[" ++ show(index_set(vs)) ++ "]\n") /\ + forall(d in index_set_2of2(x_eq_d) diff {0})( sum(i in index_set_1of2(x_eq_d))( x_eq_d[i,d] ) <= 1 ) + ) + endif; diff --git a/share/minizinc/linear/fzn_circuit.mzn b/share/minizinc/linear/fzn_circuit.mzn new file mode 100644 index 0000000..d409109 --- /dev/null +++ b/share/minizinc/linear/fzn_circuit.mzn @@ -0,0 +1,43 @@ +include "alldifferent.mzn"; + +/** @group globals + Constrains the elements of \a x to define a circuit where \a x[\p i] = \p j means + that \p j is the successor of \p i. +*/ +% Linear version. +predicate fzn_circuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + constraint forall( i in S )( x[i] in S diff {i} ), %% Self-mapping and exclude i->i before alldifferent + } in + alldifferent(x) /\ +% alldifferent(order) /\ + if nMZN__fSECcuts>0 then + let { + array [int, int] of var int: eq_x = eq_encode( x ), + constraint assert( l==min( index_set_2of2( eq_x ) ), "circuit: index set mismatch" ), %% self-mapping + constraint assert( u==max( index_set_2of2( eq_x ) ), "circuit: index set mismatch" ), + } in + circuit__SECcuts( eq_x ) + else true endif /\ + if nMZN__fSECcuts<2 then + %%% MTZ model. Note that INTEGER order vars seem better!: + let { + array[l+1..l+n-1] of var 2..n: order, + } in + forall (i,j in l+1..l+n-1 where i!=j /\ j in dom(x[i])) ( + order[i] - order[j] + (n-1)* bool2int(x[i]==j) + + (n-3)*bool2int(x[j]==i) %% the Desrochers & Laporte '91 term + %%%% --- strangely enough it is much worse on vrp-s2-v2-c7_vrp-v2-c7_det_ADAPT_1_INVERSE.mzn! + <= n-2 ) + else true endif + %% ... but seems improved with this (leaving also for SEC) + /\ if n>2 then forall (i,j in S where i= 0 and \a r[\p i] >= 0 + + Linear version. +*/ + + +predicate fzn_cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + if mzn_in_redundant_constraint() /\ fMZN__IgnoreRedundantCumulative then + true + else + let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = dom_array( [ s[i] | i in tasks ] ) + } in + if 0==card(tasks) then /*true*/ 0==card(index_set(s)) \/ b>=0 + elseif MZN__Cumulative_Fixed_d_r /\ + is_fixed(d) /\ is_fixed(r) /\ is_fixed(b) then + fzn_cumulative_fixed_d_r(s, fix(d), fix(r), fix(b)) + elseif nMZN__UnarySizeMax_cumul>=card(times)*card(tasks) then + cumulative_time_decomp(s, d, r, b, times) + else + cumulative_task_decomp(s, d, r, b) + endif + endif; + +%% Can be called with a given set of times: +predicate cumulative_set_times(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b, + set of int: TIMES01) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = dom_array( [ s[i] | i in tasks ] ) + intersect TIMES01 + } in + if false then + cumulative_time_decomp(s, d, r, b, times) + else + cumulative_task_decomp(s, d, r, b) + endif )); + +predicate cumulative_time_decomp(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b, + set of int: TIMES01) = +let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = + { i | i in min([ lb(s[i]) | i in tasks ]) .. + max([ ub(s[i]) + ub(d[i]) | i in tasks ]) where i in TIMES01 } +} +in + forall( t in times ) ( + b >= sum( i in tasks ) ( + if is_fixed(d[i]) then + bool2int( s[i] in t-fix(d[i])+1..t ) + else + bool2int( s[i] <= t /\ t < s[i] + d[i] ) + endif + * r[i] + ) + ); + +predicate cumulative_task_decomp(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b) = +let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 } +} +in + forall( j in tasks) ( + b-r[j] >= sum( i in tasks where i != j + /\ lb(s[i])<=ub(s[j]) /\ lb(s[j])= c[i]) /\ + sum(d)=1 /\ + y = sum (i in index_set(c)) ( d[i]*x[i] ); diff --git a/share/minizinc/linear/fzn_if_then_else_int.mzn b/share/minizinc/linear/fzn_if_then_else_int.mzn new file mode 100644 index 0000000..10b1293 --- /dev/null +++ b/share/minizinc/linear/fzn_if_then_else_int.mzn @@ -0,0 +1,7 @@ +predicate fzn_if_then_else_int(array[int] of var bool: c, array[int] of int: x, var int: y) = + let { + array[index_set(c)] of var 0..1: d; + } in + forall (i in index_set(c)) (sum(j in 1..i-1)(c[j])+d[i] >= c[i]) /\ + sum(d)=1 /\ + y = sum (i in index_set(c)) ( d[i]*x[i] ); diff --git a/share/minizinc/linear/fzn_inverse.mzn b/share/minizinc/linear/fzn_inverse.mzn new file mode 100644 index 0000000..96ac20a --- /dev/null +++ b/share/minizinc/linear/fzn_inverse.mzn @@ -0,0 +1,14 @@ +%-----------------------------------------------------------------------------% +% Constrains two arrays of int variables to represent inverse functions. +% All the values in each array must be within the index set of the other array. +% +% Linear version. +%-----------------------------------------------------------------------------% + +include "fzn_inverse_in_range.mzn"; + +predicate fzn_inverse(array[int] of var int: f, + array[int] of var int: invf) = + forall(i in index_set(f)) ( f[i] in index_set(invf) ) /\ + forall(j in index_set(invf)) ( invf[j] in index_set(f) ) /\ + fzn_inverse_in_range(f, invf); diff --git a/share/minizinc/linear/fzn_inverse_in_range.mzn b/share/minizinc/linear/fzn_inverse_in_range.mzn new file mode 100644 index 0000000..f50b2d2 --- /dev/null +++ b/share/minizinc/linear/fzn_inverse_in_range.mzn @@ -0,0 +1,5 @@ +predicate fzn_inverse_in_range(array[int] of var int: f, + array[int] of var int: invf) = + forall(i in index_set(f), j in index_set(invf)) ( + (j == f[i] <-> i == invf[j]) + ); diff --git a/share/minizinc/linear/fzn_inverse_in_range_reif.mzn b/share/minizinc/linear/fzn_inverse_in_range_reif.mzn new file mode 100644 index 0000000..03e75f6 --- /dev/null +++ b/share/minizinc/linear/fzn_inverse_in_range_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_inverse_in_range_reif(array[int] of var int: f, + array[int] of var int: invf, + var bool: b) = + b <-> forall(i in index_set(f), j in index_set(invf)) ( + (j == f[i] <-> i == invf[j]) + ); diff --git a/share/minizinc/linear/fzn_inverse_reif.mzn b/share/minizinc/linear/fzn_inverse_reif.mzn new file mode 100644 index 0000000..6ad84bf --- /dev/null +++ b/share/minizinc/linear/fzn_inverse_reif.mzn @@ -0,0 +1,8 @@ +predicate fzn_inverse_reif(array[int] of var int: f, + array[int] of var int: invf, + var bool: b) = + b <-> forall(i in index_set(f), j in index_set(invf)) ( + f[i] in index_set(invf) /\ + invf[j] in index_set(f) /\ + (j == f[i] <-> i == invf[j]) + ); diff --git a/share/minizinc/linear/fzn_lex_less_bool_reif.mzn b/share/minizinc/linear/fzn_lex_less_bool_reif.mzn new file mode 100644 index 0000000..ca137f1 --- /dev/null +++ b/share/minizinc/linear/fzn_lex_less_bool_reif.mzn @@ -0,0 +1,21 @@ +include "../std/fzn_lex_less_bool_reif.mzn"; + +predicate fzn_lex_less_bool_imp(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + (c -> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); diff --git a/share/minizinc/linear/fzn_lex_lesseq_bool.mzn b/share/minizinc/linear/fzn_lex_lesseq_bool.mzn new file mode 100644 index 0000000..16af74a --- /dev/null +++ b/share/minizinc/linear/fzn_lex_lesseq_bool.mzn @@ -0,0 +1,49 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_bool(array[int] of var bool: x, + array[int] of var bool: y) = +% if (min(card(index_set(x)), card(index_set(y))) <= 25) then +% let { int: size = min(card(index_set(x)), card(index_set(y))); +% } in +% sum(i in 0..size-1)(pow(2, (size-1-i)) * bool2int(x[i+min(index_set(x))])) +% <= sum(i in 0..size-1)(pow(2, (size-1-i)) * bool2int(y[i+min(index_set(y))])) +% else +% my_trace ("lex_lesseq_bool(\(x), \(y))") /\ + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = min(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + b[0] + /\ + forall(i in 0..size) ( + b[i] -> ( ( ( x[lx + i] <= y[ly + i] ) ) /\ + % bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) <= 2 /\ + +% ( b[i] -> +( x[lx + i] < y[ly + i] \/ b[i+1] ) ) +% /\ ( bool2int(b[i]) <= bool2int(x[lx + i] < y[ly + i]) + bool2int(b[i+1]) ) /\ + % bool2int(b[i]) + (1-bool2int(x[lx + i])) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + % /\ bool2int(b[i]) + bool2int(x[lx + i]) + bool2int(y[ly + i]) + (1-bool2int(b[i+1])) <= 3 + %% This guy is dominated by the 1st one above but helps: + % /\ bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + ) + /\ b[size+1] = (ux-lx <= uy-ly) +% endif +; + +% forall(i in 0..size) ( +% ( b[i] == ( x[lx + i] <= y[ly + i] ) ) +% /\ +% if i < size then +% ( b[i] == ( x[lx + i] < y[ly + i] \/ b[i+1] +% ) ) else true endif +% ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear/fzn_lex_lesseq_bool_reif.mzn b/share/minizinc/linear/fzn_lex_lesseq_bool_reif.mzn new file mode 100644 index 0000000..a4a63f5 --- /dev/null +++ b/share/minizinc/linear/fzn_lex_lesseq_bool_reif.mzn @@ -0,0 +1,50 @@ +predicate fzn_lex_lesseq_bool_reif(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + ( b[i] -> ( x[lx + i] <= y[ly + i] ) ) /\ + bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) <= 2 + /\ + ( b[i] -> ( x[lx + i] < y[ly + i] \/ b[i+1] ) ) /\ + bool2int(b[i]) + (1-bool2int(x[lx + i])) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + bool2int(y[ly + i]) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + ) + /\ b[size+1] = (ux-lx <= uy-ly) +% endif +; + +predicate fzn_lex_lesseq_bool_imp(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c -> b[0]) + /\ + forall(i in 0..size) ( + ( b[i] -> ( x[lx + i] <= y[ly + i] ) ) /\ + bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) <= 2 + /\ + ( b[i] -> ( x[lx + i] < y[ly + i] \/ b[i+1] ) ) /\ + bool2int(b[i]) + (1-bool2int(x[lx + i])) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + bool2int(y[ly + i]) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + ) + /\ b[size+1] = (ux-lx <= uy-ly) +; diff --git a/share/minizinc/linear/fzn_regular.mzn b/share/minizinc/linear/fzn_regular.mzn new file mode 100644 index 0000000..02bedf9 --- /dev/null +++ b/share/minizinc/linear/fzn_regular.mzn @@ -0,0 +1,99 @@ +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the range 1..\a S) + is accepted by the DFA of \a Q states with input 1..\a S and transition + function \a d (which maps (1..\a Q, 1..\a S) -> 0..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). We reserve state 0 to be an always failing state. +*/ +predicate fzn_regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F) = +% my_trace(" regular: index_set(x)=" ++ show(index_set(x)) +% ++ ", dom_array(x)=" ++ show(dom_array(x)) +% ++ ", dom_array(a)=" ++ show(1..Q) +% ++ "\n") /\ + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)), + int: n = max(index_set(x)) + 1, + array[m..n] of var 1..Q: a, + constraint a[m] = q0 /\ % Set a[0]. + a[n] in F, % Check the final state is in F. + constraint + forall(i in index_set(x)) ( + x[i] in 1..S % Do this in case it's a var. + /\ %% trying to eliminate non-reachable states: + let { + set of int: va_R = { d[va, vx] | va in dom(a[i]), vx in dom(x[i]) } diff { 0 } %% Bug in MZN 2.0.4 + } in + a[i+1] in va_R + ) + } in + let { + constraint + forall(i in [n-i | i in 1..length(x)]) ( + a[i] in { va | va in dom(a[i]) + where exists(vx in dom(x[i]))(d[va, vx] in dom(a[i+1])) } + /\ + x[i] in { vx | vx in dom(x[i]) + where exists(va in dom(a[i]))(d[va, vx] in dom(a[i+1])) } + ) + } in + forall(i in index_set(x)) ( + let { + set of int: va_R = { d[va, vx] | va in dom(a[i]), vx in dom(x[i]) } diff { 0 } %% Bug in MZN 2.0.4 + } in +% my_trace(" S" ++ show(i) +% ++ ": dom(a[i])=" ++ show(dom(a[i])) +% ++ ", va_R="++show(va_R) +% ++ ", index_set_2of2(eq_a) diff va_R=" ++ show(index_set_2of2(eq_a) diff va_R) +% ++ ", dom(a[i+1])=" ++ show(dom(a[i+1])) +% ) /\ + a[i+1] in va_R + %/\ a[i+1] in min(va_R)..max(va_R) + ) +% /\ my_trace(" regular -- domains after prop: index_set(x)=" ++ show(index_set(x)) +% ++ ", dom_array(x)=" ++ show(dom_array(x)) +% ++ ", dom_array(a)=" ++ show(dom_array(a)) +% ++ "\n") +% /\ my_trace("\n") + /\ + let { + array[int, int] of var int: eq_a=eq_encode(a), + array[int, int] of var int: eq_x=eq_encode(x), + } in + forall(i in index_set(x)) ( + % a[i+1] = d[a[i], x[i]] % Determine a[i+1]. + if card(dom(a[i]))*card(dom(x[i])) > nMZN__UnarySizeMax_1step_regular then + %% Implication decomposition: + forall(va in dom(a[i]), vx in dom(x[i]))( + if d[va, vx] in dom(a[i+1]) then + eq_a[i+1, d[va, vx]] >= eq_a[i, va] + eq_x[i, vx] - 1 %% The only-if part of conj + else + 1 >= eq_a[i, va] + eq_x[i, vx] + endif + ) + else + %% Network-flow decomposition: + %% {regularIP07} M.-C. C{\^o}t{\'e}, B.~Gendron, and L.-M. Rousseau. + %% \newblock Modeling the regular constraint with integer programming. + let { + % array[int, int] of set of int: VX_a12 = %% set of x for given a1 that produce a2 + % array2d(1..S, 1..Q, [ { vx | vx in 1..S where d[va1, vx]==va2 } | va1 in dom(a[i]), va2 in dom(a[i+1]) ]); + array[int, int] of var int: ppAX = eq_encode(a[i], x[i]); + } in + forall (va2 in dom(a[i+1])) ( + eq_a[i+1, va2] = sum(va1 in dom(a[i]), vx in dom(x[i]) where d[va1, vx]==va2) + (ppAX[va1, vx]) + ) + /\ + forall(va1 in dom(a[i]), vx in dom(x[i]))( + if not (d[va1, vx] in dom(a[i+1])) then + ppAX[va1, vx] == 0 + else + true + endif + ) + endif + ); diff --git a/share/minizinc/linear/fzn_sliding_sum.mzn b/share/minizinc/linear/fzn_sliding_sum.mzn new file mode 100644 index 0000000..894f33e --- /dev/null +++ b/share/minizinc/linear/fzn_sliding_sum.mzn @@ -0,0 +1,14 @@ +/** @group globals + Requires that in each subsequence \a vs[\p i], ..., \a vs[\p i + \a seq - 1] the sum of the + values belongs to the interval [\a low, \a up]. +*/ +predicate fzn_sliding_sum(int: low, int: up, int: seq, array[int] of var int: vs) = + let { int: lx = min(index_set(vs)), + int: ux = max(index_set(vs)), + } in + forall (i in lx .. ux - seq + 1) ( + let { + var int: sum_of_l = sum(j in i..i + seq - 1) (vs[j]) + } in + low <= sum_of_l /\ sum_of_l <= up + ); diff --git a/share/minizinc/linear/fzn_subcircuit.mzn b/share/minizinc/linear/fzn_subcircuit.mzn new file mode 100644 index 0000000..6bb40e7 --- /dev/null +++ b/share/minizinc/linear/fzn_subcircuit.mzn @@ -0,0 +1,68 @@ +include "alldifferent.mzn"; + +/** @group globals + Constrains the elements of \a x to define a subcircuit where \a x[\p i] = \p j + means that \p j is the successor of \p i and \a x[\p i] = \p i means that \p i + is not in the circuit. +*/ +%% Linear version +predicate fzn_subcircuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + array[S] of var 1..n: order, + array[S] of var bool: ins = array1d(S,[ x[i] != i | i in S]), + var l..u+1: firstin = min([ u+1 + bool2int(ins[i])*(i-u-1) | i in S]), %% ... + var S: lastin, + var bool: empty = (firstin == u+1), + } in + alldifferent(x) /\ + % NO alldifferent(order) /\ + + % If the subcircuit is empty then each node points at itself. + % + (empty <-> forall(i in S)(not ins[i])) /\ + + % If the subcircuit is non-empty then order numbers the subcircuit. + % + ((not empty) <-> + + %% Another way to express minimum. +% forall(i in l..u+1)( +% i==firstin <-> ins[i] +% /\ forall(j in S where j firstin /\ + + % The lastin node points at firstin. + x[lastin] = firstin /\ + + % And both are in + ins[lastin] /\ ins[firstin] /\ + + % The successor of each node except where it is firstin is + % numbered one more than the predecessor. +% forall(i in S) ( +% (ins[i] /\ x[i] != firstin) -> order[x[i]] = order[i] + 1 +% ) /\ + %%% MTZ model. Note that INTEGER order vars seem better!: + forall (i,j in S where i!=j) ( + order[i] - order[j] + n*bool2int( x[i]==j /\ i!=lastin ) + % + (n-2)*bool2int(x[j]==i) %% the Desrochers & Laporte '91 term + <= n-1 ) /\ + + % Each node that is not in is numbered after the lastin node. + forall(i in S) ( + true +% (not ins[i]) <-> (n == order[i]) + ) + + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear/fzn_table_int.mzn b/share/minizinc/linear/fzn_table_int.mzn new file mode 100644 index 0000000..30cfc65 --- /dev/null +++ b/share/minizinc/linear/fzn_table_int.mzn @@ -0,0 +1,18 @@ +%-----------------------------------------------------------------------------% +% A 'table' constraint table(x, T) represents the constraint x in T where we +% consider each row in T to be a tuple and T as a set of tuples. +% +% Linear version. +% +% See also the equality encoding of the 'element' constraint. +%-----------------------------------------------------------------------------% + +predicate fzn_table_int(array[int] of var int: x, array[int, int] of int: t) = + let { set of int: it = index_set_1of2(t), + array[it] of var 0..1: lambda } + in + sum(lambda) = 1 + /\ + forall(j in index_set(x))( sum(i in it)( t[i,j]*lambda[i] ) = x[j] ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear/options.mzn b/share/minizinc/linear/options.mzn new file mode 100644 index 0000000..90b80c3 --- /dev/null +++ b/share/minizinc/linear/options.mzn @@ -0,0 +1,180 @@ +/* +% Controls +% +*/ + +%-----------------------------------------------------------------------------% +%---------- USER and LAZY CUTS -----------------------------------------------% +/* + PLEASE NOTE: + If you export FZN file with lazy_constraint/user_cut annotations, + their declarations are not exported currently (as of 7.11.17). + WORKAROUND: when solving that fzn, add -G linear, + e.g., as follows: mzn-cplex -G linear model.fzn + * For Gurobi, the constraints marked as MIP_cut and/or MIP_lazy are added + * into the overall model and marked with the foll values of Lazy attribute: + * ::MIP_lazy 1 + * ::MIP_cut ::MIP_lazy 2 + * ::MIP_cut 3 + */ +ann: user_cut; +ann: lazy_constraint; +%%% comment away the below assignments (leaving, e.g., ann: MIP_cut;) to have them as normal constraints +%%% In particular, they may be used by redundant_constraint() and symmetry_breaking_constraint(), see redefs-2.0.2.mzn +ann: MIP_cut = user_cut; %% MIP_cut: make sure no feasible solutions are cut off + %% -- seems better on average but in CPLEX, wrong LB e.g. on carpet-cutting +ann: MIP_lazy = lazy_constraint; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% "GENERAL" constraints %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% opt bool: fIndConstr; %% User option, e.g., with -D + %% Attention: as of MZN 2.4.3, you also need -DfMIPdomains=false +bool: fMZN__UseIndicators = false; %% Pass on indicator constraints + % if absent( fIndConstr ) then false + % else deopt( fIndConstr ) endif; + %% CPLEX 12.6.2 Concert: reifs give wrong result on 2012/amaze, so using implications only + +%% MAX/MIN +% opt bool: MinMaxGeneral; %% User option, e.g., with -D + %% pass on min/max to the backend as fzn_array_float_minimum +bool: MZN__MinMaxGeneral = false; % if absent(MinMaxGeneral) then false else deopt(MinMaxGeneral) endif; + +%% CUMULATIVE +% opt bool: CumulativeSolverConfig; %% As set in share/minizinc/Preferences.json +% opt bool: Cumulative; %% User option, e.g., with -D +bool: MZN__Cumulative_Fixed_d_r = false; % if occurs(Cumulative) then deopt(Cumulative) + % elseif occurs(CumulativeSolverConfig) then deopt(CumulativeSolverConfig) + % else false endif; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Quadratic expressions %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +%% Forward float_times as fzn_float_times +% opt bool: QuadrFloatSolverConfig; %% As set in share/minizinc/Preferences.json +% opt bool: QuadrFloat; %% User option, e.g., with -D +bool: MZN__QuadrFloat = false; % if occurs(QuadrFloat) then deopt(QuadrFloat) + % elseif occurs(QuadrFloatSolverConfig) then deopt(QuadrFloatSolverConfig) + % else false endif; + +%% Forward int_times as fzn_int_times +% opt bool: QuadrIntSolverConfig; %% As set in share/minizinc/Preferences.json +% opt bool: QuadrInt; %% User option, e.g., with -D +bool: QuadrIntFinal = false; % if occurs(QuadrInt) then deopt(QuadrInt) + % elseif occurs(QuadrIntSolverConfig) then deopt(QuadrIntSolverConfig) + % else false endif; +% opt int: QuadrIntCard; %% Convert int_times to fzn_int_times if the minimum + %% of x, y's domain cardinalities as at least QuadrIntCard +int: MZN__QuadrIntCard = 0; % if occurs(QuadrIntCard) then deopt(QuadrIntCard) + % elseif QuadrIntFinal then 0 else infinity endif; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Subtour elimination in circuit %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +% opt int: nSECcuts; %% 0,1: use MTZ formulation +int: nMZN__fSECcuts = 0; %% 1,2: pass on circuit constraints to the MIP_solverinstance's cut gen + % if absent( nSECcuts ) then 0 + % else deopt( nSECcuts ) endif; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MIPdomains %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +%% Paper: % Belov, Stuckey, Tack, Wallace. Improved Linearization of Constraint Programming Models. CP 2016 Proceedings. + %%% The below option enables translation of domain constraints into the ...POST predicates. + %%% The code in MIPdomains.cpp processes them and also non-contiguous domains + %%% (only-range-domains is then standardly off). MIPdomains.cpp needs all the required + %%% __POST predicates to be declared to kick in. +% opt bool: fMIPDomains; %% unified decomposition constraints (...__POST) to FlatZinc +% opt bool: fMIPdomains; %% Can be defined from cmdline: -D "fMIPdomains=false" +bool: fPostprocessDomains = false; %% True to pass all domain-related + % if absent( fMIPdomains ) /\ absent( fMIPDomains ) then true + % elseif not absent( fMIPdomains ) then deopt( fMIPdomains ) + % else deopt( fMIPDomains ) + % endif; +% opt bool: fMIPdomAux; +bool: fPostproDom_AUX = false; %% Specialized for aux_ constr + % if absent( fMIPdomAux ) then false + % else deopt( fMIPdomAux ) endif; +% opt bool: fMIPdomDiff; +bool: fPostproDom_DIFF = false; %% Specialized for differences: x z=x-y<0 + % if absent( fMIPdomDiff ) then false %% seems best for Gurobi, worse for CBC + % else deopt( fMIPdomDiff ) endif; + +% mzn_opt_only_range_domains = not fPostprocessDomains; %% currently unused + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Avoid creating new int vars %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +% opt bool: fAvoidNewInts; +bool: fAvoidNI = false; %% Actually this is only for ..._lin_..., not for just x-y + % if absent( fAvoidNewInts ) then false + % else deopt( fAvoidNewInts ) endif; +% opt bool: fNewVarsInAuxEq; +bool: fAuxIntEqOLD00 = false; % if absent(fNewVarsInAuxEq) then false else deopt(fNewVarsInAuxEq) endif; +bool: fAuxFloatEqOLD00 = false; % if absent(fNewVarsInAuxEq) then false else deopt(fNewVarsInAuxEq) endif; + +%%%%%%%%%%%%%%%%%%%%% Redundant constraints ---------------------------------------------- % +bool: fMZN__IgnoreRedundantCumulative=false; +%% NOT WORKING NOW, use redefs_2.0.2.mzn: +%%%%% bool: fMZN__IgnoreAllUserRedundant=false; %% ignore all user-spec redundant constr + +%%%%%%%%%%%%%%%%%%%%% Element, minimuum convex hull --------------------------------------- % +% opt bool: fXBZCuts01; %% orders 0, 1 +% opt bool: fXBZCutGen; %% only works if Cuts01 +bool: fElementCutsXZ=false; %% Use simple XZ & XZB cuts for element +bool: fElementCutsXZB = false; % if absent(fXBZCuts01) then false else deopt(fXBZCuts01) endif; +bool: fMinimumCutsXZ=false; %% Use simple XZ & XZB cuts for minimum +bool: fMinimumCutsXZB = false; % if absent(fXBZCuts01) then false else deopt(fXBZCuts01) endif; +bool: fUseXBZCutGen = false; % if absent(fXBZCutGen) then false else deopt(fXBZCutGen) endif; + +% ----------------------------------------------------------------------------------------- % +bool: fIntTimesBool=true; %% Special handling of multiplication with a boolean(*const) + +%-----------------------------------------------------------------------------% +% If not postprocessing domains: For unary encoding: maximal domain length to invoke it + +int: nMZN__UnarySizeMax_intTimes=20; +int: nMZN__UnarySizeMax_cumul=2000; +int: nMZN__UnarySizeMax_1step_regular=20000; %% network-flow decomp in the regular constraint + +int: nMZN__UnaryLenMin__ALL=1; %% can be used by the indiv. cases +int: nMZN__UnaryLenMax__ALL=2000; %% can be used by the indiv. cases +% Some more detailed parameters +int: nMZN__UnaryLenMin_leq = 1; +int: nMZN__UnaryLenMin_neq = nMZN__UnaryLenMin__ALL; +int: nMZN__UnaryLenMin_eq = nMZN__UnaryLenMin__ALL; +int: nMZN__UnaryLenMax_leq = -1; +int: nMZN__UnaryLenMax_neq = nMZN__UnaryLenMax__ALL; +int: nMZN__UnaryLenMax_eq = nMZN__UnaryLenMax__ALL; +int: nMZN__UnaryLenMax_setIn = nMZN__UnaryLenMax__ALL; +int: nMZN__UnaryLenMax_setInReif = nMZN__UnaryLenMax__ALL; + +%-----------------------------------------------------------------------------% +% Strict inequality +% The relative epsilon +%%% Has the problem that when relating to upper bound of various differences, +%%% getting different absolute eps...? +%% float: float_lt_EPS_coef__ = 1e-03; ABANDONED 12.4.18 due to #207 +%%% Absolute one, used everywhere +%%% Might make no sense for floats with smaller domains etc. +% opt float: float_EPS; +float: float_lt_EPS = 1e-6; % if absent( float_EPS ) then 1e-6 else deopt( float_EPS ) endif; + +%-----------------------------------------------------------------------------% +%%% Set =true to PRINT TRACING messages for some constraints: +% opt bool: fMIPTrace; +bool: mzn__my_trace_on = false; + % if absent( fMIPTrace ) then false else deopt( fMIPTrace ) endif; +test my_trace(string: msg) ::promise_total + = if mzn__my_trace_on then trace(msg) + else true endif; +test my_trace(string: msg, bool: bb) ::promise_total + = if mzn__my_trace_on then trace(msg, bb) + else bb endif; +function var bool: my_trace(string: msg, var bool: bb) ::promise_total + = if mzn__my_trace_on then trace(msg, bb) + else bb endif; +%%% Set =true to PRINT TRACING messages for the currently debugged constraints: +% opt bool: fMIPTraceDBG; +bool: mzn__my_trace__DBG_on = false; + % if absent( fMIPTraceDBG ) then false else deopt( fMIPTraceDBG ) endif; +test my_trace__DBG(string: msg) ::promise_total + = if mzn__my_trace__DBG_on then trace(msg) + else true endif; + diff --git a/share/minizinc/linear/redefinitions-2.0.2.mzn b/share/minizinc/linear/redefinitions-2.0.2.mzn new file mode 100644 index 0000000..5b0baf6 --- /dev/null +++ b/share/minizinc/linear/redefinitions-2.0.2.mzn @@ -0,0 +1,29 @@ +% This file contains redefinitions of standard builtins for version 2.0.2 +% that can be overridden by solvers. + + +predicate symmetry_breaking_constraint(var bool: b) = + (b) %:: MIP_lazy %:: MIP_cut %% MIP_cut wrong in CPLEX 12.6.3 + %% Symm breaking as lazy is 1% better in Gurobi 6.5.2 on the Challenges 2012-2015 + %% But caused a bug in 7.5.1 - switched off + %% true %% TO omit all symmetry_breaking_constraint's + ; + +%% Make sure no feasible solutions are cut off: +predicate redundant_constraint(var bool: b) = + (b) %:: MIP_cut + % true %% To omit all redundant_constraint's + ; + +%% Linearized element: just call without shifting +predicate array_var_bool_element_nonshifted(var int: idx, array[int] of var bool: x, var bool: c) = + array_var_bool_element(idx,x,c); + +predicate array_var_int_element_nonshifted(var int: idx, array[int] of var int: x, var int: c) = + array_var_int_element(idx,x,c); + +predicate array_var_float_element_nonshifted(var int: idx, array[int] of var float: x, var float: c) = + array_var_float_element(idx,x,c); + +predicate array_var_set_element_nonshifted(var int: idx, array[int] of var set of int: x, var set of int: c) = + array_var_set_element(idx,x,c); diff --git a/share/minizinc/linear/redefinitions-2.0.mzn b/share/minizinc/linear/redefinitions-2.0.mzn new file mode 100644 index 0000000..d274def --- /dev/null +++ b/share/minizinc/linear/redefinitions-2.0.mzn @@ -0,0 +1,23 @@ +predicate bool_clause_reif(array[int] of var bool: p, array[int] of var bool: n, bool: c) = + if c then + bool_clause(p, n) + else + forall([not x | x in p] ++ n) + endif; + +predicate bool_clause_reif(array[int] of var bool: p, array[int] of var bool: n, var bool: c) = + c = ( sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1 + ); + +predicate array_int_minimum(var int: m, array[int] of var int: x) = + array_int_minimum_I( m, [ x[i] | i in index_set(x)]); +predicate array_int_maximum(var int: m, array[int] of var int: x) = + array_int_minimum_I(-m, [-x[i] | i in index_set(x)]); +predicate array_float_minimum(var float: m, array[int] of var float: x) = + array_float_minimum_I( m, [ x[i] | i in index_set(x)]); +predicate array_float_maximum(var float: m, array[int] of var float: x) = + array_float_minimum_I(-m, [-x[i] | i in index_set(x)]); + diff --git a/share/minizinc/linear/redefinitions-2.2.1.mzn b/share/minizinc/linear/redefinitions-2.2.1.mzn new file mode 100644 index 0000000..a9ed844 --- /dev/null +++ b/share/minizinc/linear/redefinitions-2.2.1.mzn @@ -0,0 +1,7 @@ +% This file contains redefinitions of standard builtins for version 2.2.1 +% that can be overridden by solvers. + +/** @group flatzinc.int Constrains \a z = \(\a x ^ {\a y}\) */ +predicate int_pow_fixed(var int: x, int: y, var int: z) = + int_pow( x, y, z ); + diff --git a/share/minizinc/linear/redefinitions.mzn b/share/minizinc/linear/redefinitions.mzn new file mode 100644 index 0000000..5192f6e --- /dev/null +++ b/share/minizinc/linear/redefinitions.mzn @@ -0,0 +1,709 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov (2015-) +% cf. Belov, Stuckey, Tack, Wallace. Improved Linearization of Constraint Programming Models. CP 2016. +*/ + +%----------------------------- BOOL2INT --------------------------------% +function var bool: reverse_map(var int: x) = (x==1); +function bool: reverse_map(int: x) = (x==1); + +predicate mzn_reverse_map_var(var bool: b) = let { + var int: x = bool2int(b) +} in true; + +function var int: bool2int(var bool: x) :: promise_total = + let { var 0..1: b2i; + constraint (x = reverse_map(b2i)) ::is_reverse_map ; + } in + b2i; + +predicate bool_eq(var bool: x, var bool: y) = +%% trace(" bool_eq: \(x), \(y) \n") /\ + bool2int(x)==bool2int(y); + +predicate bool2int(var bool: x, var int: y) = y = bool2int(x); + +%---------------------------- BASIC (HALF)REIFS -----------------------------% + +include "options.mzn"; +include "redefs_bool_reifs.mzn"; +include "redefs_bool_imp.mzn"; + +include "domain_encodings.mzn"; +include "redefs_lin_reifs.mzn"; +include "redefs_lin_imp.mzn"; +include "redefs_lin_halfreifs.mzn"; + +include "nosets.mzn"; %% For set_le, set_lt ... Usind std/nosets + %% as long as the linearization is good. + +%-----------------------------------------------------------------------------% +% Strict inequality + +% Uncomment the following redefinition for FlatZinc MIP solver interfaces that +% do not support strict inequality. Note that it does not preserve equivalence +% (some solutions of the original problem may become invalid). + +predicate float_lt(var float: x, var float: y) = +% (x - y) <= (-float_lt_EPS_coef__)*max(abs(ub(x - y)), abs(ub(y-x))); + x <= y - float_lt_EPS; + +predicate float_lin_lt(array[int] of float: c, array[int] of var float: x, + float: d) = + float_lt(sum(i in index_set(x))( c[i]*x[i] ), d); + +%-----------------------------------------------------------------------------% +% Minimum, maximum, absolute value +% Use unary as well? TODO + +predicate int_abs(var int: x, var int: z) = +%% The simplifications seem worse on league.mzn model90-18-20.dzn: +%% but the .lp seem to differ just by order...?? TODO + if lb(x)>=0 then z==x + elseif ub(x)<=0 then z==-x + else + let { var bool: p } + in + z >= x /\ + z >= -x /\ + z >= 0 /\ % This is just for preprocessor + z <= max([ub(x), -lb(x)]) /\ % And this + % z <= x \/ z <= -x %% simple + aux_int_le_if_1(z, x, p) /\ %% even simpler + aux_int_le_if_0(z, -x, p) /\ + int_le_reif(0, x, p) % with reifs + %int_eq_reif(z, x, p) /\ + %int_eq_reif(z, -x, not p) + endif + ; + +predicate int_min(var int: x, var int: y, var int: z) = + array_int_minimum(z, [x, y]); + +predicate int_max(var int: x, var int: y, var int: z) = + array_int_maximum(z, [x, y]); + +predicate float_abs(var float: x, var float: z) = + if lb(x)>=0.0 then z==x + elseif ub(x)<=0.0 then z==-x + else + let { var bool: p } + in + z >= x /\ + z >= -x /\ + z >= 0.0 /\ % This is just for preprocessor + z <= max([ub(x), -lb(x)]) /\ % And this + % z <= x \/ z <= -x + aux_float_le_if_1(z, x, (p)) /\ + aux_float_le_if_0(z, -x, (p)) + % /\ + % float_le_reif(0.0, x, p) % with reifs - no point for floats? TODO + % float_eq_reif(z, x, p) /\ + % float_eq_reif(z, -x, not p) + endif; + +predicate float_min(var float: x, var float: y, var float: z) = + array_float_minimum(z, [x, y]); + +predicate float_max(var float: x, var float: y, var float: z) = + array_float_maximum(z, [x, y]); + +predicate array_int_minimum_I(var int: m, array[int] of var int: x) = + let { int: n = length(x), + constraint assert(1 == min(index_set(x)), " array_int_minimum_I: argument indexed not from 1??"), + int: iMinUB = arg_min([ub(x[i]) | i in 1..n]), + int: MinUB = ub(x[iMinUB]), + set of int: sLBLess = { i | i in 1..n where lb(x[i])0 then sLBLess + else sLBLess union { iMinUB } endif, + } in + if 1==card(sActive) then + m == x[min(sActive)] + elseif MZN__MinMaxGeneral then + fzn_array_float_minimum(m, x) %% Send to backend + else + let { + array[1..n] of var int: p = [ + if i in sActive then let { var 0..1: pi } in pi else 0 endif | i in 1..n ], + constraint 1==sum(p), + constraint m >= lb_array(x), + constraint m <= MinUB, + } in + forall (i in index_set(x)) + ( + if i in sActive %% for at least 1 element + then + m<=x[i] /\ aux_int_ge_if_1(m, x[i], p[i]) + endif ) %% -- exclude too big x[i] + endif; + +predicate array_float_minimum_I(var float: m, array[int] of var float: x) = + let { int: n = length(x), + constraint assert(1 == min(index_set(x)), " array_float_minimum_I: argument indexed not from 1??"), + int: iMinUB = arg_min([ub(x[i]) | i in 1..n]), + float: MinUB = ub(x[iMinUB]), + set of int: sLBLess = { i | i in 1..n where lb(x[i])0 then sLBLess + else sLBLess union { iMinUB } endif, + } in + if 1==card(sActive) then + m == x[min(sActive)] + elseif MZN__MinMaxGeneral then + fzn_array_float_minimum(m, x) %% Send to backend + else + let { + array[1..n] of var int: p = [ + if i in sActive then let { var 0..1: pi } in pi else 0 endif | i in 1..n ], + constraint 1==sum(p), + constraint m >= lb_array(x), + constraint m <= MinUB, + } in + forall (i in index_set(x)) + ( + if i in sActive %% for at least 1 element + then + m<=x[i] /\ aux_float_ge_if_1(m, x[i], p[i]) + endif ) %% -- exclude too big x[i] + /\ + if card(sActive)>1 /\ fMinimumCutsXZ then + let { + array[int] of float: AL = [ lb(x[i]) | i in 1..n], + array[int] of int: srt = sort_by([i | i in 1..n], AL), + %indices of lb in sorted order + array[int] of float: AL_srt = [AL[srt[i]] | i in 1..n], + array[int] of float: AU_srt = [ub(x[srt[i]]) | i in 1..n], + array[int] of float: AM_srt = AL_srt ++ [MinUB] + %% -- these are z-levels of extreme points + } in + forall (i in 2..n+1 where + AM_srt[i]<=MinUB /\ %% this is a new "start level" + AM_srt[i]!=AM_srt[i-1] )( %% and would produce a new cut + m >= AM_srt[i] + - sum(j in 1..i-1 where AL_srt[j]1 /\ fMinimumCutsXZB then + array_var_float_element__XBZ_lb([ -x[i] | i in sActive ], [ p[i] | i in sActive ], -m) :: MIP_cut + else true endif + endif; + +%-----------------------------------------------------------------------------% +% Multiplication and division + +predicate int_div(var int: x, var int: y, var int: q) = + q == aux_int_division_modulo_fn(x,y)[1]; + + +predicate int_mod(var int: x, var int: y, var int: r) = + r == aux_int_division_modulo_fn(x,y)[2]; + + +function array[int] of var int: aux_int_division_modulo_fn(var int: x, var int: y) = + let { + %% Domain of q + set of int: dom_q = + if lb(y)*ub(y)>0 then + let { + set of int: EP = { ub(x) div ub(y), ub(x) div lb(y), lb(x) div ub(y), lb(x) div lb(y) }, + } in min(EP)..max(EP) + else + let { + int: mm = max( abs(lb(x)), abs(ub(x)) ), + } in -mm..mm %% TODO case when -1 or 1 not in dom(x) + endif, + var dom_q: q; + int: by = max(abs(lb(y)), abs(ub(y))); + var -by+1..by-1: r; + constraint x = y * q + r, + constraint 0 <= x -> 0 <= r, %% which is 0 > x \/ 0 <= r + constraint x < 0 -> r <= 0, %% which is x >= 0 \/ r <= 0 + % abs(r) < abs(y) + var 1.. max(abs(lb(y)), abs(ub(y))): w = abs(y), + constraint w > r /\ w > -r, + } in + [ q, r ]; + +%% Can also have int_times(var float, var int) ......... TODO + +predicate int_times(int: x, var int: y, var int: z); +predicate int_times(var int: x, int: y, var int: z) = int_times(y, x, z); + +predicate int_times(var int: x, var int: y, var int: z) = + if 0..1==dom(x) /\ 0..1==dom(y) then bool_and__INT(x,y,z) + elseif card(dom(x))==2 /\ card(dom(y))==2 /\ 0 in dom(x) /\ 0 in dom(y) + then let { + var 0..1: xn; + var 0..1: yn; + var 0..1: zn; + constraint x=xn*max(dom(x) diff {0}); + constraint y=yn*max(dom(y) diff {0}); + constraint z=zn*max(dom(x) diff {0})*max(dom(y) diff {0}); + } in + bool_and__INT(xn,yn,zn) + elseif min(card(dom(x)), card(dom(y))) >= MZN__QuadrIntCard then + fzn_int_times(x, y, z) + elseif card(dom(x)) * card(dom(y)) > nMZN__UnarySizeMax_intTimes + \/ ( fIntTimesBool /\ ( + %% Peter's idea for *bool. More optimal but worse values on carpet cutting. + (card(dom(x))==2 /\ 0 in dom(x)) + \/ (card(dom(y))==2 /\ 0 in dom(y)) + ) ) + then %% PARAM + %% ALSO NO POINT IF <=4. TODO + if card(dom(x)) > card(dom(y)) \/ + ( card(dom(x))==card(dom(y)) /\ 0 in dom(y) /\ not (0 in dom(x)) ) + then int_times(y,x,z) + else + let { + set of int: s = lb(x)..ub(x), + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + array[s] of var min(r)..max(r): ady = array1d(s, [ + if d in dom(x) then d*y else min(r) endif | d in s ]) } + in + ady[x] = z %% use element() + endif + else + int_times_unary(x, { }, y, z) + endif; + +%% domx__ can be used to narrow domain... NOT IMPL. +predicate int_times_unary(var int: x, set of int: domx__, var int: y, var int: z) = + let { + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + %% set of int: domx = if card(domx__)>0 then domx__ else dom(x) endif, + array[int, int] of var int: pp=eq_encode(x, y) + } in + z>=min(r) /\ z<=max(r) /\ + z==sum(i in index_set_1of2(pp), j in index_set_2of2(pp)) + (i * j * pp[i, j]) /\ + forall(i in index_set_1of2(pp), j in index_set_2of2(pp) + where not ((i*j) in dom(z)) + )(pp[i, j]==0) + ; + + +predicate int_times_unary__NOFN(var int: x, set of int: domx__, var int: y, var int: z) = + let { + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + %% set of int: domx = if card(domx__)>0 then domx__ else dom(x) endif, + array[int] of var int: pX = eq_encode(x), + array[int] of var int: pY = eq_encode(y), + array[int] of int: valX = [ v | v in index_set(pX) ], %% NOT domx. + array[int] of int: valY = [ v | v in index_set(pY) ], %% -- according to eq_encode! + array[index_set(valX), index_set(valY)] of var 0..1: pp %% both dim 1.. + } in + if is_fixed(x) \/ is_fixed(y) then + z==x*y + else + z>=min(r) /\ z<=max(r) /\ + sum(pp)==1 /\ + z==sum(i in index_set(valX), j in index_set(valY)) + (valX[i] * valY[j] * pp[i, j]) /\ + forall(i in index_set(valX)) + ( pX[valX[i]] == sum(j in index_set(valY))( pp[i, j] ) ) /\ + forall(j in index_set(valY)) + ( pY[valY[j]] == sum(i in index_set(valX))( pp[i, j] ) ) + endif; + +predicate float_times(var float: x, var float: y, var float: z) = + if is_fixed(x) then + z==fix(x)*y %%%%% Need to use fix() otherwise added to CSE & nothing happens + elseif is_fixed(y) then + z==x*fix(y) + elseif MZN__QuadrFloat then + fzn_float_times(x, y, z) + else + abort("Unable to create linear formulation for the `float_times(\(x), \(y), \(z))` constraint." ++ + " Define QuadrFloat=true if your linear solver supports quadratic constraints," ++ + " or use integer variables.") + endif; + +%%%Define int_pow +predicate int_pow( var int: x, var int: y, var int: r ) = + let { + array[ int, int ] of int: x2y = array2d( lb(x)..ub(x), lb(y)..ub(y), + [ pow( X, Y ) | X in lb(x)..ub(x), Y in lb(y)..ub(y) ] ) + } in + r == x2y[ x, y ]; + +%%% Adding a version returning float for efficiency +/** @group builtins.arithmetic Return \(\a x ^ {\a y}\) */ +function var float: pow_float(var int: x, var int: y) = + let { + int: yy = if is_fixed(y) then fix(y) else -1 endif; + } in + if yy = 0 then 1 + elseif yy = 1 then x else + let { var float: r ::is_defined_var; + constraint int_pow_float(x,y,r) ::defines_var(r); + } in r + endif; +%%%Define int_pow_float +predicate int_pow_float( var int: x, var int: y, var float: r ) = + let { + array[ int, int ] of float: x2y = array2d( lb(x)..ub(x), lb(y)..ub(y), + [ pow( X, Y ) | X in lb(x)..ub(x), Y in lb(y)..ub(y) ] ) + } in + r == x2y[ x, y ]; + + + +%-----------------------------------------------------------------------------% +% Array 'element' constraints + +predicate array_bool_element(var int: x, array[int] of bool: a, var bool: z) = + array_int_element(x, arrayXd(a, [bool2int(a[i]) | i in index_set(a)]), bool2int(z)); + +predicate array_var_bool_element(var int: x, array[int] of var bool: a, + var bool: z) = + array_var_int_element(x, arrayXd(a, [bool2int(a[i]) | i in index_set(a)]), bool2int(z)); + +predicate array_int_element(var int: i00, array[int] of int: a, var int: z) = + let { + set of int: ix = index_set(a), + constraint i00 in { i | i in ix where a[i] in dom(z) }, + } in %%% Tighten domain of i00 before dMin/dMax + let { + int: dMin = min(i in dom(i00))(a[i]), + int: dMax = max(i in dom(i00))(a[i]), + } in + if dMin==dMax then + z==dMin + else + z >= dMin /\ + z <= dMax /\ + let { + int: nUBi00 = max(dom(i00)), + int: nLBi00 = min(dom(i00)), + int: nMinDist = min(i in nLBi00 .. nUBi00-1)(a[i+1]-a[i]), + int: nMaxDist = max(i in nLBi00 .. nUBi00-1)(a[i+1]-a[i]), + } in + if nMinDist == nMaxDist then %% The linear case + z == a[nLBi00] + nMinDist*(i00-nLBi00) + else + let { + array[int] of var int: p = eq_encode(i00) %% this needs i00 in ix + } %% Faster flattening than (i==i00) @2a9df1f7 + in + sum(i in dom(i00))( a[i] * p[i] ) == z + endif + endif; + +predicate array_var_int_element(var int: i00, array[int] of var int: a, + var int: z) = + let { + constraint i00 in { i | i in index_set(a) where + 0 < card(dom(a[i]) intersect dom(z)) }, + } in %% finish domain first + let { + int: minLB=min(i in dom(i00))(lb(a[i])), + int: maxUB=max(i in dom(i00))(ub(a[i])) + } in + if minLB==maxUB then + z==minLB + else + z >= minLB /\ + z <= maxUB /\ + if {0,1}==dom(i00) /*ub(i00)-lb(i00)==1*/ /*2==card( dom( i00 ) )*/ then + aux_int_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ + aux_int_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) + else + let { + array[int] of var int: p = eq_encode(i00) %% this needs i00 in ix + } %% Faster flattening than (i==i00) @2a9df1f7 + in + forall (i in dom(i00))( + aux_int_eq_if_1(z, a[i], p[i]) + ) + endif + endif; + +predicate array_float_element(var int: i00, array[int] of float: a, + var float: z) = + let { set of int: ix = index_set(a), + constraint i00 in { i | i in ix where a[i]>=lb(z) /\ a[i]<=ub(z) }, + } in %%% Tighten domain of i00 before dMin/dMax + let { + float: dMin = min(i in dom(i00))(a[i]), + float: dMax = max(i in dom(i00))(a[i]), + } in + if dMin==dMax then + z==dMin + else + z >= dMin /\ + z <= dMax /\ + let { + int: nUBi00 = max(dom(i00)), + int: nLBi00 = min(dom(i00)), + float: nMinDist = min(i in nLBi00 .. nUBi00-1)(a[i+1]-a[i]), + float: nMaxDist = max(i in nLBi00 .. nUBi00-1)(a[i+1]-a[i]), + } in + if nMinDist == nMaxDist then %% The linear case + z == a[nLBi00] + nMinDist*(i00-nLBi00) + else + let { + array[int] of var int: p = eq_encode(i00) %% this needs i00 in ix + } %% Faster flattening than (i==i00) @2a9df1f7 + in + sum(i in dom(i00))( a[i] * p[i] ) == z + endif + endif; + +predicate array_var_float_element(var int: i00, array[int] of var float: a, + var float: z) = + let { set of int: ix = index_set(a), + constraint i00 in { i | i in ix where + lb(a[i])<=ub(z) /\ ub(a[i])>=lb(z) + }, + } in %% finish domain first + let { + float: minLB=min(i in dom(i00))(lb(a[i])), + float: maxUB=max(i in dom(i00))(ub(a[i])) + } in + if minLB==maxUB then + z==minLB + else + z >= minLB /\ + z <= maxUB /\ + if {0,1}==dom(i00) /*ub(i00)-lb(i00)==1*/ /*2==card( dom( i00 ) )*/ then + aux_float_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ + aux_float_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) + else + %%% The convexified bounds seem slow for ^2 and ^3 equations: + % sum(i in dom(i01))( lb(a[i]) * (i==i00) ) <= z /\ %% convexify lower bounds + % sum(i in dom(i01))( ub(a[i]) * (i==i00) ) >= z /\ %% convexify upper bounds + let { + array[int] of var int: p = eq_encode(i00) %% this needs i00 in ix + } %% Faster flattening than (i==i00) @2a9df1f7 + in + forall (i in dom(i00))( + aux_float_eq_if_1(z, a[i], p[i]) + ) + %% Cuts: + /\ + if fElementCutsXZ then + array_var_float_element__ROOF([ a[i] | i in dom(i00) ], z) :: MIP_cut %% these 2 as user cuts - too slow + /\ array_var_float_element__ROOF([ -a[i] | i in dom(i00) ], -z) :: MIP_cut %% or even skip them + else true endif + /\ + if fElementCutsXZB then + array_var_float_element__XBZ_lb([ a[i] | i in dom(i00) ], [ p[i] | i in dom(i00) ], z) :: MIP_cut + /\ array_var_float_element__XBZ_lb([ -a[i] | i in dom(i00) ], [ p[i] | i in dom(i00) ], -z) :: MIP_cut + else true endif + endif + endif; + +%%% Facets on the upper surface of the z-a polytope +%%% Possible parameter: maximal number of first cuts taken only +predicate array_var_float_element__ROOF(array[int] of var float: a, + var float: z) = + let { set of int: ix = index_set(a), + int: n = length(a), + array[int] of float: AU = [ ub(a[i]) | i in 1..n], + array[int] of int: srt_ub = sort_by([i | i in 1..n], AU), + %indices of ub sorted up + array[int] of float: AU_srt_ub = [ub(a[srt_ub[i]]) | i in 1..n], + array[int] of float: AL_srt_ub = [lb(a[srt_ub[i]]) | i in 1..n], + array[int] of float: MaxLBFrom = + [ max(j in index_set(AL_srt_ub) where j>=i)(AL_srt_ub[j]) + | i in 1..n ], %% direct, O(n^2) + array[int] of float: ULB = [ + if 1==i then MaxLBFrom[1] + else max([AU_srt_ub[i-1], MaxLBFrom[i]]) + endif | i in 1..n ] + } in + %%% "ROOF" + forall (i in 1..n where + if i==n then true else ULB[i]!=ULB[i+1] endif %% not the same base bound + )( + z <= ULB[i] + + sum( j in i..n where AU_srt_ub[i] != AL_srt_ub[i] ) %% not a const + ( (AU_srt_ub[j]-ULB[i]) * (a[srt_ub[j]]-AL_srt_ub[j]) / (AU_srt_ub[j]-AL_srt_ub[j]) ) ) + ; + +predicate array_var_float_element__XBZ_lb(array[int] of var float: x, array[int] of var int: b, var float: z) = + if fUseXBZCutGen then + array_var_float_element__XBZ_lb__cutgen(x, b, z) :: MIP_cut + else + %% Adding some cuts a priori, also to make solver extract the variables + let { + int: i1 = min(index_set(x)) + } in + (z <= sum(i in index_set(x))(ub(x[i]) * b[i])) %:: MIP_cut -- does not work to put them here TODO + /\ + forall(i in index_set(x) intersect i1..(i1+19)) %% otherwise too many on amaze2 + ( assert(lb(x[i]) == -ub(-x[i]) /\ ub(x[i]) == -lb(-x[i]), " negated var's bounds should swap " ) /\ + z <= x[i] + sum(j in index_set(x) where i!=j)((ub(x[j])-lb(x[i]))*b[j])) %:: MIP_cut %% (ub_j-lb_i) * b_j + /\ + forall(i in index_set(x) intersect i1..(i1+19)) + ( z <= ub(x[i])*b[i] + sum(j in index_set(x) where i!=j)(x[j]+lb(x[j])*(b[j]-1)) ) %:: MIP_cut + /\ + (z <= sum(i in index_set(x))(x[i] + lb(x[i]) * (b[i]-1))) %:: MIP_cut + endif; + +%-----------------------------------------------------------------------------% +% Set constraints +%% ----------------------------------------------- (NO) SETS ---------------------------------------------- +% XXX only for a fixed set here, general see below. +% Normally not called because all plugged into the domain. +% Might be called instead of set_in(x, var set of int s) if s gets fixed? +predicate set_in(var int: x, set of int: s__) = + let { + set of int: s = if has_bounds(x) then s__ intersect dom(x) else s__ endif, + constraint min(s) <= x, + constraint x <= max(s), + } in + if s = min(s)..max(s) then true + elseif fPostprocessDomains then + set_in__POST(x, s) + else %% Update eq_encode + let { + array[int] of var int: p = eq_encode(x); + } in + forall(i in index_set(p) diff s)(p[i]==0) +% let { +% array[int] of int: sL = [ e | e in s where not (e - 1 in s) ]; +% array[int] of int: sR = [ e | e in s where not (e + 1 in s) ]; +% array [index_set(sR)] of var 0..1: B; +% constraint assert(length(sR)==length(sL), "N of lb and ub of sub-intervals of a set should be equal"); +% } in +% sum(B) = 1 %% use indicators +% /\ +% x >= sum(i in index_set(sL))(B[i]*sL[i]) +% /\ +% x <= sum(i in index_set(sR))(B[i]*sR[i]) + endif; + +%%% for a fixed set +predicate set_in_reif(var int: x, set of int: s__, var bool: b) = + if is_fixed(b) then + if fix(b) then x in s__ else x in dom(x) diff s__ endif + elseif has_bounds(x) /\ not (s__ subset dom(x)) then + b <-> x in s__ intersect dom(x) %% Use CSE + else + let { + set of int: s = if has_bounds(x) then s__ intersect dom(x) else s__ endif, + } in + ( + if dom(x) subset s then b==true + elseif card(dom(x) intersect s)==0 then b==false + elseif fPostprocessDomains then + set_in_reif__POST(x, s, b) + %% Bad. Very much so for CBC. 27.06.2019: elseif s == min(s)..max(s) then + %% b <-> (min(s) <= x /\ x <= max(s)) + else + if card(dom(x))<=nMZN__UnaryLenMax_setInReif then %% PARAM TODO + let { + array[int] of var int: p = eq_encode(x); + } in + sum(i in s intersect dom(x))(p[i]) == bool2int(b) + else + bool2int(b) == fVarInBigSetOfInt(x, s) + endif + endif + ) + endif; + + % Alternative +predicate alt_set_in_reif(var int: x, set of int: s, var bool: b) = + b <-> + exists(i in 1..length([ 0 | e in s where not (e - 1 in s) ]))( + let { int: l = [ e | e in s where not (e - 1 in s) ][i], + int: r = [ e | e in s where not (e + 1 in s) ][i] } + in + l <= x /\ x <= r + ); + +%%% for a fixed set +predicate set_in_imp(var int: x, set of int: s__, var bool: b) = + if is_fixed(b) then + if fix(b) then x in s__ else true endif + elseif has_bounds(x) /\ not (s__ subset dom(x)) then + b -> x in s__ intersect dom(x) %% Use CSE + else + let { + set of int: s = if has_bounds(x) then s__ intersect dom(x) else s__ endif, + } in + ( + if dom(x) subset s then true + elseif card(dom(x) intersect s)==0 then b==false + elseif s == min(s)..max(s) then + (b -> min(s) <= x) /\ (b -> x <= max(s)) + else + if card(dom(x))<=nMZN__UnaryLenMax_setInReif then %% PARAM TODO + let { + array[int] of var int: p = eq_encode(x); + } in + sum(i in s intersect dom(x))(p[i]) >= bool2int(b) + else + bool2int(b) <= fVarInBigSetOfInt(x, s) + endif + endif + ) + endif; + +function var 0..1: fVarInBigSetOfInt(var int: x, set of int: s) + = let { + array[int] of int: sL = [ e | e in s where not (e - 1 in s) ]; + array[int] of int: sR = [ e | e in s where not (e + 1 in s) ]; + constraint assert(length(sR)==length(sL), "N of lb and ub of sub-intervals of a set should be equal"); + } in + sum(i in index_set(sL)) (bool2int(x>=sL[i] /\ x<=sR[i])); %% use indicators + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% OTHER SET STUFF COMING FROM nosets.mzn %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% + +annotation bool_search(array[$X] of var bool: x, ann:a1, ann:a2, ann:a3) = + let { array[int] of var bool: xx = array1d(x) } in + int_search([bool2int(xx[i]) | i in index_set(xx)],a1,a2,a3); + +annotation warm_start( array[int] of var bool: x, array[int] of bool: v ) = + warm_start( [ bool2int(x[i]) | i in index_set(x) ], [ bool2int(v[i]) | i in index_set(v) ] ); + + +annotation sat_goal(var bool: b) = int_max_goal(b); +annotation int_max_goal(var int: x); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DOMAIN POSTPROCESSING BUILT-INS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Single variable: x = d <-> x_eq_d[d] +predicate equality_encoding__POST(var int: x, array[int] of var int: x_eq_d); + +%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn +predicate set_in__POST(var int: x, set of int: s__); +predicate set_in_reif__POST(var int: x, set of int: s__, var int: b); + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LOGICAL CONSTRAINTS TO THE SOLVER %%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn => REPEAT TESTS. TODO +predicate int_lin_eq_reif__IND(array[int] of int: c, array[int] of var int: x, int: d, var int: b); +predicate int_lin_le_reif__IND(array[int] of int: c, array[int] of var int: x, int: d, var int: b); +predicate int_lin_ne__IND(array[int] of int: c, array[int] of var int: x, int: d); +predicate aux_int_le_zero_if_0__IND(var int: x, var int: b); +predicate float_lin_le_reif__IND(array[int] of float: c, array[int] of var float: x, float: d, var int: b); +predicate aux_float_eq_if_1__IND(var float: x, var float: y, var int: b); +predicate aux_float_le_zero_if_0__IND(var float: x, var int: b); + +predicate array_int_minimum__IND(var int: m, array[int] of var int: x); +predicate array_int_maximum__IND(var int: m, array[int] of var int: x); +predicate array_float_minimum__IND(var float: m, array[int] of var float: x); +predicate array_float_maximum__IND(var float: m, array[int] of var float: x); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% XBZ cut generator, currently CPLEX only %%%%%%%%%%%%%%%%%%%%%%%%%% +predicate array_var_float_element__XBZ_lb__cutgen(array[int] of var float: x, array[int] of var int: b, var float: z); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Quadratic expressions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%5 +predicate fzn_float_times(var float: a, var float: b, var float: c); +predicate fzn_int_times(var int: a, var int: b, var int: c); +predicate fzn_array_float_minimum(var float: m, array[int] of var float: x); diff --git a/share/minizinc/linear/redefs_bool_imp.mzn b/share/minizinc/linear/redefs_bool_imp.mzn new file mode 100644 index 0000000..3799761 --- /dev/null +++ b/share/minizinc/linear/redefs_bool_imp.mzn @@ -0,0 +1,76 @@ +include "redefs_lin_halfreifs.mzn"; + +% SIMPLE BOOLEAN LOGIC +% TODO: why not check "is_fixed(r)" everywhere?? + +predicate bool_eq_imp(var bool: p, var bool: q, var bool: r) = + if is_fixed(r) then + if fix(r) then p = q else true endif + else + let { + var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) + } in x + z <= y + 1 /\ y + z <= x + 1 + endif; + +predicate bool_ne_imp(var bool: p, var bool: q, var bool: r) = + bool_xor_imp(p, q, r); + +predicate bool_le_imp(var bool: p, var bool: q, var bool: r) = + let { + var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r), + } in 1 - x + y >= z; + +predicate bool_lt_imp(var bool: p, var bool: q, var bool: r) = + bool_and_imp(not p, q, r); + +predicate bool_or_imp(var bool: p, var bool: q, var bool: r) = + array_bool_or_imp([p,q], r); + +predicate bool_and_imp(var bool: p, var bool: q, var bool: r) = + array_bool_and_imp([p,q],r); + +predicate bool_xor_imp(var bool: p, var bool: q, var bool: r) = + let { + var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r), + } in x + y >= z /\ x + y + z <= 2; + +% BOOLEAN ARRAY OPERATIONS +predicate array_bool_or_imp(array[int] of var bool: a, var bool: b) = + if forall( i in index_set( a ) )( is_fixed(a[i]) /\ not fix(a[i]) ) then + not b + elseif exists( i in index_set( a ) )( is_fixed(a[i]) /\ fix(a[i]) ) then + true + else + let { + array[index_set(a)] of var bool: a1; + var int: x = bool2int(b), + } in forall(i in index_set(a)) (a1[i] -> a[i]) /\ sum(a1) = x + endif; + +predicate array_bool_and_imp(array[int] of var bool: a, var bool: b) = + if is_fixed(b) then + if fix(b) then + forall(i in index_set(a))( a[i] ) + else + true + endif + elseif forall( i in index_set( a ) )( is_fixed(a[i]) /\ fix(a[i]) ) then + true + else + let { + var int: x = bool2int(b), + array[index_set(a)] of var int: c = + array1d(index_set(a), [ bool2int(a[i] ) | i in index_set(a) ]) + } in forall(i in index_set(c)) ( c[i] >= x ) + endif; + +%% No var int d, sorry TODO: why not??? +predicate bool_lin_eq_imp(array[int] of int: c, array[int] of var bool: x, + int: d, var bool: b) = + aux_int_eq_if_1(sum(i in index_set(x))( c[i]*bool2int(x[i]) ), d, bool2int(b)); diff --git a/share/minizinc/linear/redefs_bool_reifs.mzn b/share/minizinc/linear/redefs_bool_reifs.mzn new file mode 100644 index 0000000..3e8010d --- /dev/null +++ b/share/minizinc/linear/redefs_bool_reifs.mzn @@ -0,0 +1,190 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov +*/ + +%-----------------------------------------------------------------------------% +% +% Logic operations +% Use indicators for reifs (CPLEX)? Seems weak. +% +%-----------------------------------------------------------------------------% + +predicate bool_not(var bool: p) = bool2int(p)=0; + +predicate bool_not(var bool: p, var bool: q) = + bool2int(p) + bool2int(q) = 1; + +predicate bool_and(var bool: p, var bool: q, var bool: r) = +% my_trace(" bool_and: \(p) /\\ \(q) <-> \(r) \n") /\ + if false then + int_lin_le_reif__IND( [-1, -1], [p, q], -2, r) + else + array_bool_and( [p, q], r ) +% bool_and__INT(bool2int(p), bool2int(q), bool2int(r)) + endif; + +predicate bool_and__INT(var int: x, var int: y, var int: z) = + x + y <= z + 1 /\ + %% x + y >= z * 2; % weak + x >= z /\ y >= z; % strong + + +predicate bool_or(var bool: p, var bool: q, var bool: r) = + if false then + int_lin_le_reif__IND( [-1, -1], [p, q], -1, r) + elseif true then + array_bool_or( [p, q], r ) + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + x + y >= z /\ + % x + y <= z * 2; % weak + x <= z /\ y <= z % strong + endif; + +predicate bool_xor(var bool: p, var bool: q) = + 1==p+q; + +predicate bool_xor(var bool: p, var bool: q, var bool: r) = + if false then +% int_lin_eq_reif__IND( [1, 1], [p, q], 1, r) /\ + true + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + x <= y + z /\ + y <= x + z /\ + z <= x + y /\ + x + y + z <= 2 + endif; + + +predicate bool_eq_reif(var bool: p, var bool: q, var bool: r) = +%% trace(" bool_eq_reif: \(p), \(q), \(r) \n") /\ + if is_fixed(r) then % frequent case + if fix(r) = true then p = q else bool_not(p,q) endif + elseif is_fixed(q) then + if fix(q) = true then p = r + else bool_not(p,r) endif + elseif is_fixed(p) then + if fix(p) = true then q = r + else bool_not(q,r) endif + elseif false then +% int_lin_eq_reif__IND( [1, -1], [p, q], 0, r) /\ + true + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + x + y <= z + 1 /\ + x + z <= y + 1 /\ + y + z <= x + 1 /\ + x + y + z >= 1 + endif; + + +predicate bool_ne_reif(var bool: p, var bool: q, var bool: r) = + bool_xor(p, q, r); + + +predicate bool_le(var bool: p, var bool: q) = + let { var int: x = bool2int(p), + var int: y = bool2int(q) } + in + x <= y; + + +predicate bool_le_reif(var bool: p, var bool: q, var bool: r) = + if false then +% int_lin_le_reif__IND( [1, -1], [p, q], 0, r) /\ + true + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + 1 - x + y >= z /\ + %% /\ 1 - x + y <= z * 2 not needed + 1 - x <= z /\ y <= z % strong + endif; + + +predicate bool_lt(var bool: p, var bool: q) = + not p /\ q; + + +predicate bool_lt_reif(var bool: p, var bool: q, var bool: r) = + (not p /\ q) <-> r; + +%-----------------------------------------------------------------------------% + +%% Reified disjunction +predicate array_bool_or(array[int] of var bool: a, var bool: b) = + if exists( i in index_set( a ) )( is_fixed(a[i]) /\ fix(a[i]) ) then + b + elseif is_fixed(b) then % frequent case + if fix(b) = true then + sum(i in index_set(a))( bool2int(a[i]) ) >= 1 %% >=1 seems better for MIPDomains... 5.4.19 + else + forall(i in index_set(a))( not a[i] ) + endif + else + let { var int: x = bool2int(b), + array[1..length(a)] of var int: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + sum(c) >= x /\ + % sum(c) <= x * length(a) % weak + forall (i in index_set(a)) (x >= c[i]) % strong + endif; + +%% Reified conjunction +predicate array_bool_and(array[int] of var bool: a, var bool: b) = + if exists( i in index_set( a ) )( is_fixed(a[i]) /\ not fix(a[i]) ) then + not b + elseif is_fixed(b) then % frequent case + if fix(b) = false then + sum(i in index_set(a))( bool2int(a[i]) ) <= length(a)-1 + else + forall(i in index_set(a))( a[i] ) + endif + else + let { var int: x = bool2int(b), + array[1..length(a)] of var int: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + length(a) - sum(c) >= 1 - x /\ + % length(a) - sum(c) <= (1 - x) * length(a); % weak + forall (i in index_set(a)) (x <= c[i]) % strong + endif; + +% predicate array_bool_xor(array[int] of var bool: a) = .. sum(a) is odd .. +predicate array_bool_xor(array[int] of var bool: a) = + let { var 0..(length(a)-1) div 2: m, + var 1..((length(a)-1) div 2)*2+1: ss = sum(i in index_set(a))( bool2int(a[i]) ) } + in + ss == 1 + 2 * m; + +predicate bool_clause(array[int] of var bool: p, array[int] of var bool: n) = + sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1; + +predicate bool_lin_eq(array[int] of int: c, array[int] of var bool: x, var int: d) :: promise_total = + sum(i in index_set(x))( c[i]*bool2int(x[i]) ) == d; + +predicate bool_lin_eq_reif(array[int] of int: c, array[int] of var bool: x, + int: d, var bool: b) = %% No var int d, sorry + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*bool2int(x[i]) ), d, bool2int(b)); + + diff --git a/share/minizinc/linear/redefs_lin_halfreifs.mzn b/share/minizinc/linear/redefs_lin_halfreifs.mzn new file mode 100644 index 0000000..33fe9af --- /dev/null +++ b/share/minizinc/linear/redefs_lin_halfreifs.mzn @@ -0,0 +1,227 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov +*/ + +%-----------------------------------------------------------------------------% +% Auxiliary: indicator constraints +% p -> x # 0 where p is a 0/1 variable and # is a comparison + +% Base cases + +%% used e.g. in element +predicate aux_float_eq_if_1(var float: x, var float: y, var int: p) = + if is_fixed(p) then + if 1==fix(p) then x==y else true endif + elseif is_fixed(x) /\ is_fixed(y) then %%% Needed to avoid constant domain var + if fix(x)!=fix(y) then p==0 else true endif + elseif is_fixed(x-y) then %%% Hypothetically possible to land here + if 0.0!=fix(x-y) then p==0 else true endif + elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + aux_float_eq_zero_if_1__POST(x - y, p, p) + elseif fMZN__UseIndicators then + aux_float_eq_if_1__IND(x, y, p) + else + aux_float_le_if_1(x, y, p) /\ + aux_float_ge_if_1(x, y, p) + endif; + +predicate aux_int_eq_if_1(var int: x, var int: y, var int: p) = + if is_fixed(p) then + if fix(p) = 1 then (x = y) else true endif + elseif is_fixed(x) /\ is_fixed(y) then + if fix(x) != fix(y) then (p = 0) else true endif + % elseif is_fixed(x-y) then % TODO: Necessary for integers?? + % if 0.0!=fix(x-y) then p==0 else true endif + % elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + % % TODO MIPDomains + % elseif fMZN__UseIndicators then % TODO: Necessary for integers?? + % aux_float_eq_if_1__IND(x, y, p) + else + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) + endif; + +predicate aux_float_ne_if_1(var float: x, var float: y, var int: p) = + if is_fixed(p) then + if fix(p) = 1 then (x != y) else true endif + elseif is_fixed(x) /\ is_fixed(y) then %%% Needed to avoid constant domain var + if (fix(x) = fix(y)) then (p = 0) else true endif + elseif is_fixed(x-y) then %%% Hypothetically possible to land here + if (0.0 = fix(x-y)) then (p = 0) else true endif + % TODO: What is necessary for not equals? + % elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + % aux_float_ne_zero_if_1__POST(x - y, p, p) + % elseif fMZN__UseIndicators then + % aux_float_ne_if_1__IND(x, y, p) + else + let { array[1..2] of var bool: q } in + ( q[1] -> (x < y) ) /\ + ( q[2] -> (x > y) ) /\ + (sum(q) = p) + endif; + +predicate aux_int_ne_if_1(var int: x, var int: y, var int: p) = + if is_fixed(p) then + if fix(p) = 1 then (x != y) else true endif + elseif is_fixed(x) /\ is_fixed(y) then + if fix(x) = fix(y) then (p = 0) else true endif + % elseif is_fixed(x-y) then % TODO: Necessary for integers?? + % if 0.0!=fix(x-y) then p==0 else true endif + % elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + % % TODO MIPDomains + % elseif fMZN__UseIndicators then % TODO: Necessary for integers?? + % aux_float_eq_if_1__IND(x, y, p) + else + let { array[1..2] of var bool: q } in + ( q[1] -> (x < y) ) /\ + ( q[2] -> (x > y) ) /\ + (sum(q) = p) + endif; + +predicate aux_int_le_zero_if_0(var int: x, var int: p) = + if is_fixed(p) then + if 0==fix(p) then x<=0 else true endif %% 0==fix !! + elseif lb(x)>0 then + p==1 + elseif not (0 in dom(x)) then + let { + constraint assert( ub(x) < infinity, + "aux_int_le_zero_if_0: variable \(x)'s domain: dom(\(x)) = \(dom(x)), should have finite upper bound\n" ), + set of int: sDomNeg = dom(x) intersect -infinity..-1, + constraint assert( card( sDomNeg ) > 0, + "Variable \(x): dom(\(x)) = \(dom(x)), but dom() intersect -inf..-1: \(sDomNeg)\n" ), + } in + aux_int_le_if_0( x, max( sDomNeg ), p ) + elseif fPostprocessDomains /\ fPostproDom_AUX then + aux_int_le_zero_if_1__POST(x, 1-p) + elseif fMZN__UseIndicators then + aux_int_le_zero_if_0__IND(x, p) + else + assert( ub(x)0.0 then + p==1 + elseif fPostprocessDomains /\ fPostproDom_AUX then + aux_float_le_zero_if_1__POST(x, 1-p, 1-p) + elseif fMZN__UseIndicators then + aux_float_le_zero_if_0__IND(x, p) + else + x <= ub(x) * p + endif; + +predicate aux_float_lt_zero_if_0(var float: x, var int: p) = + assert( has_bounds(x), "Variable \(x) needs finite bounds for a big-M constraint" ) /\ + if is_fixed(p) then + if 0==fix(p) then x<0.0 else true endif + elseif lb(x)>=0.0 then + p==1 + elseif fPostprocessDomains /\ fPostproDom_AUX then + aux_float_lt_zero_if_1__POST(x, 1-p, 1-p, float_lt_EPS) + elseif fMZN__UseIndicators then + aux_float_le_zero_if_0__IND(x+float_lt_EPS, p) %% Here just absolute EPS, TODO + else +%% let { float: rho = float_lt_EPS_coef__ * max(abs(ub(x)), abs(lb(x))) } % same order of magnitude as ub(x) + let { float: rho = float_lt_EPS } % absolute eps + in + %%% This one causes 2x- derivation of EPS: + %% x < (ub(x) + rho) * p + %%% Better? + x <= (ub(x) + rho) * p - rho + %%% This just uses absolute eps: + %% x < (ub(x) + float_lt_EPS) * p + endif; + + +% Derived cases +predicate aux_int_le_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, p); + +predicate aux_int_ge_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, p); + +predicate aux_int_le_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, 1 - p); + +predicate aux_int_ge_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, 1 - p); + +predicate aux_int_lt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, p); + +predicate aux_int_gt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x + 1, p); + +predicate aux_int_lt_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, 1 - p); + +predicate aux_int_gt_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x + 1, 1 - p); + +%% int: switching differences to float to avoid creating integer vars +/* Used anywhere? +predicate aux_int_le_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, p); + +predicate aux_int_ge_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, p); + +predicate aux_int_le_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, 1 - p); + +predicate aux_int_ge_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, 1 - p); + +predicate aux_int_lt_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y + 1.0, p); + +predicate aux_int_gt_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x + 1.0, p); + +predicate aux_int_lt_if_1(var float: x, float: y, var int: p) = + aux_float_le_zero_if_0(x - y + 1.0, 1 - p); +*/ + +predicate aux_float_le_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, p); + +predicate aux_float_ge_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, p); + +predicate aux_float_le_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, 1 - p); + +predicate aux_float_ge_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, 1 - p); + +predicate aux_float_lt_if_0(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(x - y, p); + +predicate aux_float_gt_if_0(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(y - x, p); + +predicate aux_float_lt_if_1(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(x - y, 1 - p); + +predicate aux_float_gt_if_1(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(y - x, 1 - p); + + +% -------------------------- Domains postpro --------------------------- + %% To avoid looking if an original int var x-y exists and has eq_encode: + %% Passing both int and float version of the indicator for flexibility: +predicate aux_float_eq_zero_if_1__POST(var float: x, var int: pI, var float: p); + +predicate aux_int_le_zero_if_1__POST(var int: x, var int: p); +predicate aux_float_le_zero_if_1__POST(var float: x, var int: pI, var float: p); +predicate aux_float_lt_zero_if_1__POST(var float: x, var int: pI, var float: p, float: eps); diff --git a/share/minizinc/linear/redefs_lin_imp.mzn b/share/minizinc/linear/redefs_lin_imp.mzn new file mode 100644 index 0000000..86259fd --- /dev/null +++ b/share/minizinc/linear/redefs_lin_imp.mzn @@ -0,0 +1,271 @@ +include "redefs_lin_halfreifs.mzn"; +include "redefs_lin_reifs.mzn"; + +%% var, var +predicate int_le_imp(var int: x, var int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then x<=y else true endif + elseif ub(x)<=lb(y) then true + elseif lb(x)>ub(y) then (not b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_int_le_if_1(x, y, b) + endif; + +%% var, var +predicate int_lt_imp(var int: x, var int: y, var bool: b) = + if is_fixed(x) then + int_le_imp(x + 1, y, b) + else + int_le_imp(x, y - 1, b) + endif; + +%% var, var +predicate int_eq_imp(var int: x, var int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + elseif card( dom(x) intersect dom(y) ) > 0 then + if is_fixed(x) then + if is_fixed(y) then + b -> (fix(x) = fix(y)) + else + int_eq_imp(y, fix(x), b) + endif + elseif is_fixed(y) then + int_eq_imp(x, fix(y), b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_int_eq_if_1(x, y, b) + endif + else + not b + endif; + +%% var, const +predicate int_eq_imp(var int: x, int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + elseif y in dom(x) then + if is_fixed(x) then + b -> (y = fix(x)) + % elseif fPostprocessDomains then + % % TODO MIPDomains + else + aux_int_eq_if_1(x, y, b) + endif + else + not b + endif; + +%% var, var +predicate int_ne_imp(var int: x, var int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + true + else + if is_fixed(x) then + if is_fixed(y) then + b -> (fix(x) != fix(y)) + else + int_ne_imp(y, fix(x), b) + endif + elseif is_fixed(y) then + int_ne_imp(x, fix(y), b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_int_ne_if_1(x, y, b) + endif + endif; + +%% var, const +predicate int_ne_imp(var int: x, int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + true + else + if is_fixed(x) then + b -> (y != fix(x)) + % elseif fPostprocessDomains then + % % TODO MIPDomains + else + aux_int_ne_if_1(x, y, b) + endif + endif; + +%-----------------------------------------------------------------------------% + +%% lin_expr, const +predicate int_lin_le_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if length(c) == 1 /\ (d mod c[1] = 0) then + if c[1] > 0 then + int_le_imp(x[1], d div c[1], b) + else + int_le_imp(d div c[1], x[1], b) + endif + elseif (d = 0) /\ (length(c) = 2) /\ (abs(c[1]) = 1) /\ (c[1] = -1 * c[2]) then + if (c[1] < 0) then + int_le_imp(x[2], x[1], b) + else + int_le_imp(x[1], x[2], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_le_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + elseif fAvoidNI then + aux_float_le_if_1(sum2float(c, x), d, b) + else + aux_int_le_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate int_lin_lt_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if true then + abort("int_lin_lt_imp not supposed to be called") + else + int_lin_le_imp(c, x, d - 1, b) + endif; + +%% lin_expr, const +predicate int_lin_eq_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if length(c) == 1 then + if d mod c[1] != 0 then + not b + else + int_eq_imp(x[1], d div c[1], b) + endif + elseif (d = 0) /\ (length(c) = 2) /\ (abs(c[1]) = 1) /\ (c[1] = -1 * c[2]) then + int_ne_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_eq_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + elseif fAvoidNI then + aux_float_eq_if_1(sum2float(c, x), d, b) + else + aux_int_eq_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +%% lin_expr, const +predicate int_lin_ne_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if length(c) == 1 then + if d mod c[1] != 0 then + true + else + int_ne_imp(x[1], d div c[1], b) + endif + elseif (d = 0) /\ (length(c) = 2) /\ (abs(c[1]) = 1) /\ (c[1] = -1 * c[2]) then + int_ne_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_ne_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + elseif fAvoidNI then + aux_float_ne_if_1(sum2float(c, x), d, b) + else + aux_int_ne_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +%-----------------------------------------------------------------------------% + +%% var float, var float +predicate float_le_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then x <= y else true endif + elseif ub(x) <= lb(y) then true + elseif lb(x) > ub(y) then (not b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_le_if_1(x, y, b) + endif; + +%% var float, var float +predicate float_lt_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x < y) else true endif + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_lt_if_1(x, y, b) + endif; + +%% var float, var float +predicate float_eq_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + not b + elseif is_fixed(x) /\ is_fixed(y) then + b -> (fix(x) == fix(y)) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_eq_if_1(x, y, b) + endif; + +%% var float, var float +predicate float_ne_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + true + elseif is_fixed(x) /\ is_fixed(y) then + b -> (fix(x) != fix(y)) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_ne_if_1(x, y, b) + endif; + +%-----------------------------------------------------------------------------% + +predicate float_lin_eq_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + float_eq_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_eq_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + else + aux_float_eq_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate float_lin_ne_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + float_ne_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_ne_imp(sum(i in index_set(x))(c[i] * x[i]), d, not b) + else + aux_float_ne_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate float_lin_le_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + if (c[1] < 0.0) then + float_le_imp(x[2], x[1], b) + else + float_le_imp(x[1], x[2], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_le_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + else + aux_float_le_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate float_lin_lt_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + if (c[1] < 0.0) then + float_lt_imp(x[2], x[1], b) + else + float_lt_imp(x[1], x[2], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_lt_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + else + aux_float_lt_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; diff --git a/share/minizinc/linear/redefs_lin_reifs.mzn b/share/minizinc/linear/redefs_lin_reifs.mzn new file mode 100644 index 0000000..a93ae98 --- /dev/null +++ b/share/minizinc/linear/redefs_lin_reifs.mzn @@ -0,0 +1,522 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov +*/ + +%-----------------------------------------------------------------------------% +% +% Linear equations and inequations +% TODO Use indicators for (half)reifs. +% Otherwise and more using unary encoding for reasonable domains +% +%-----------------------------------------------------------------------------% + +% Domains: reduce all to aux_ stuff? +%% never use Concert's reif feature + +%% var, var +predicate int_le_reif(var int: x, var int: y, var bool: b) = +%% trace(" int_le_reif VV: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_le_reif__POST(x-y, 0, b) + else + int_le_reif__NOPOST(x, y, b) + endif +; + +%% const, var +predicate int_le_reif(int: x, var int: y, var bool: b) = +%% trace(" int_le_reif CV: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif not (x in dom(y)) then %% dom(y) has holes + let { + set of int: sDom2 = dom(y) intersect x+1..infinity, + constraint assert( card( sDom2 ) > 0, + "Variable: dom(\(y)) = \(dom(y)), but dom() intersect \(x)..inf: \(sDom2)\n" ), + } in + b <-> min( sDom2 ) <= y + elseif fPostprocessDomains then + int_ge_reif__POST(y, x, b) + else + int_le_reif(-y, -x, b) + endif +; + +%% var, const +predicate int_le_reif(var int: x, int: y, var bool: b) = +%% trace(" int_le_reif VC: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif not (y in dom(x)) then %% dom(x) has holes + let { + set of int: sDom2 = dom(x) intersect -infinity..y-1, + constraint assert( card( sDom2 ) > 0, + "Variable: dom(\(x)) = \(dom(x)), but dom() intersect -inf..\(y): \(sDom2)\n" ), + } in + b <-> x <= max( sDom2 ) + else + if fPostprocessDomains then + int_le_reif__POST(x, y, b) + else + int_le_reif__NOPOST(x, y, b) + endif + endif +; + +%% var int, var int +predicate int_le_reif__NOPOST(var int: x, var int: y, var bool: b) = + aux_int_le_if_1(x, y, b) /\ %% This can call POSTs... TODO + aux_int_gt_if_0(x, y, b) + ; + +%% var, var +predicate int_lt_reif(var int: x, var int: y, var bool: b) = + %% int_le_reif(x-y, -1, b); %% This would produce a new variable x-y and possibly POST it + %% but it looks like differences should not be + if is_fixed( x ) then + int_le_reif(x+1, y, b) + else + int_le_reif(x, y-1, b) + endif; + +%% var, var +predicate int_ne(var int: x, var int: y) = + if fPostproDom_DIFF then + int_ne(x-y, 0) + else + int_ne__SIMPLE(x-y, 0) + endif; + +%% var, const +predicate int_ne(var int: x, int: y) = + if y in dom(x) then + if y==ub(x) then + x <= y-1 + elseif y==lb(x) then + x >= y+1 + elseif fPostprocessDomains then + int_ne__POST(x, y) + elseif card(dom(x))y) + endif; + +%% var, var +predicate int_eq_reif(var int: x, var int: y, var bool: b) = +%% trace(" int_eq_reif VV: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if fix(b) then x==y else x!=y endif + elseif card(dom(x) intersect dom(y))>0 then + if is_fixed(x) then + if is_fixed(y) then + b <-> fix(x)==fix(y) + else + int_eq_reif(y, fix(x), b) + endif + elseif is_fixed(y) then + int_eq_reif(x, fix(y), b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_eq_reif(x-y, 0, b) + else + aux_int_eq_iff_1(x, y, b) + endif + else + not b + endif; + +%% var, const +predicate int_eq_reif(var int: x, int: y, var bool: b) = +%% trace(" int_eq_reif VC: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if fix(b) then x==y else x!=y endif + elseif y in dom(x) then + if is_fixed(x) then + b <-> y==fix(x) + elseif card(dom(x))==2 then + x == max(dom(x) diff {y}) + b*(y - max(dom(x) diff {y})) %% This should directly connect b to var 0..1: x + elseif fPostprocessDomains then + int_eq_reif__POST(x, y, b) +%%% THIS seems pretty complex, especially for binaries, and does not connect to eq_encoding (/ MIPD?) +%% elseif y==lb(x) then +%% int_lt_reif(y, x, not b) +%% elseif y==ub(x) then +%% int_lt_reif(x, y, not b) + elseif card(dom(x)) 0 then + int_le_reif(x[1], d div c[1], b) + else + int_le_reif(d div c[1], x[1], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_le_reif(sum(i in index_set(x))( c[i] * x[i] ), d, b) + elseif fAvoidNI then + aux_float_le_if_1(sum2float( c, x ), d, b) /\ + aux_float_ge_if_0(sum2float( c, x ), d+1, b) + else + int_le_reif__NOPOST(sum(i in index_set(x))( c[i] * x[i] ), d, b) + endif; + + +predicate int_lin_lt_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if true then + abort("int_lin_lt_reif not supposed to be called") + else + int_lin_le_reif(c, x, d - 1, b) + endif; + +%% lin_expr, const +predicate int_lin_eq_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if length(c) == 1 then + if d mod c[1] != 0 then + not b + else + int_eq_reif(x[1], d div c[1], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_eq_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b) + elseif fAvoidNI /\ fAuxIntEqOLD00 then + aux_int_eq_iff_1__float(sum2float( c, x ), d, b) + else + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, b) + endif; + +%% lin_expr, const +predicate int_lin_ne(array[int] of int: c, array[int] of var int: x, int: d) = + if length(c) == 1 then + (d mod c[1] = 0) -> int_ne(x[1], d div c[1]) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_ne(sum(i in index_set(x))( c[i]*x[i] ),d) + elseif fAvoidNI then + int_lin_eq_reif(c, x, d, false) + else + int_ne__SIMPLE(sum(i in index_set(x))( c[i]*x[i] ),d) + endif; + +%% lin_expr, const +predicate int_lin_ne_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if length(c) == 1 then + if d mod c[1] != 0 then + b + else + int_ne_reif(x[1], d div c[1], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_ne_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b) + else + int_lin_eq_reif(c, x, d, not (b)) + endif; + + +%-----------------------------------------------------------------------------% + +%% var float, const +predicate float_le_reif(var float: x, float: y, var bool: b) = +%% trace( "float_le_reif(\(x), \(y), \(b))\n" ) /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif false then + float_lin_le_reif__IND( [1.0, -1.0], [x, y], 0.0, b) + elseif ub(x) <= y then + b == true + elseif lb(x) > y then + b == false + elseif fPostprocessDomains then + float_le_reif__POST(x, y, b, float_lt_EPS) + else + float_le_reif__NOPOST(x, y, b) + endif; + +%% const, var float +predicate float_le_reif(float: x, var float: y, var bool: b) = + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif false then + float_lin_le_reif__IND( [1.0, -1.0], [x, y], 0.0, b) + elseif ub(x) <= lb(y) then + b == true + elseif lb(x) > ub(y) then + b == false + elseif fPostprocessDomains then + float_ge_reif__POST(y, x, b, float_lt_EPS) + else + float_le_reif(-y, -x, b) + endif; + +%% var float, var float +predicate float_le_reif(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_le_reif(x-y, 0.0, b) + else + float_le_reif__NOPOST(x-y, 0, b) + endif +; + +%% var float, var float +predicate float_le_reif__NOPOST(var float: x, var float: y, var bool: b) = + aux_float_le_if_1(x, y, (b)) /\ %% Can call __POSTs TODO + aux_float_gt_if_0(x, y, (b)) + ; + +%% TODO +predicate float_lt_reif(var float: x, var float: y, var bool: b) = + %% Actually = float_le_reif(x, y-eps, b). + if is_fixed(b) then + if true==fix(b) then x=y endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + aux_float_lt_zero_iff_1__POST(x - y, b, float_lt_EPS) + else + aux_float_lt_if_1(x, y, (b)) /\ + aux_float_ge_if_0(x, y, (b)) + endif; + +%% var, const +predicate float_ne(var float: x, float: y) = + if fPostprocessDomains then + float_ne__POST(x, y, float_lt_EPS) + else + float_ne__SIMPLE(x, y) + endif; + +predicate float_ne__SIMPLE(var float: x, var float: y) = + if true then + let { var 0..1: p } + in + aux_float_lt_if_1(x, y, (p)) /\ + aux_float_gt_if_0(x, y, (p)) + else %TODO: Why is this not half-reified? + 1 == (x>y) + (xub(y) then + b == false + elseif is_fixed(x) /\ is_fixed(y) then + b == (fix(x) == fix(y)) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_eq_reif__POST(x-y, 0, b, float_lt_EPS) + else + aux_float_eq_iff_1(x, y, (bool2int(b))) + endif; + +predicate float_ne_reif(var float: x, var float: y, var bool: b) = + float_eq_reif(x, y, not (b)); + +%-----------------------------------------------------------------------------% + +predicate float_lin_eq_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if fPostprocessDomains /\ fPostproDom_DIFF then + float_eq_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b) + else + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, b) + endif; + +predicate float_lin_ne_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if fPostprocessDomains /\ fPostproDom_DIFF then + float_ne_reif(sum(i in index_set(x))( c[i]*x[i] ), d, not b) + else + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, not b) + endif; + +predicate float_lin_le_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if fPostprocessDomains /\ fPostproDom_DIFF then + float_le_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b) + else + float_le_reif__NOPOST(sum(i in index_set(x))( c[i]*x[i] ), d, b) + endif; + +predicate float_lin_lt_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + float_lt_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b); + + +%-----------------------------------------------------------------------------% +% Auxiliary: equality reified onto a 0/1 variable + +predicate aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + if is_fixed(p) then + if 1==fix(p) then x==y else x!=y endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + abort(" aux_int_eq_iff_1 should not be used with full domain postprocessing") + elseif false then + true + elseif fAuxIntEqOLD00 then + let { array[1..2] of var 0..1: q } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) /\ + aux_int_lt_if_0(x, y, q[1]) /\ + aux_int_gt_if_0(x, y, q[2]) /\ + sum(q) == p + 1 + else + %% redundant p == (x<=y /\ y<=x) /\ + 1+p == (x<=y) + (y<=x) + endif; + +predicate aux_int_eq_iff_1__float(var float: x, float: y, var int: p) = + if fAuxIntEqOLD00 then + assert( false, "Don't use aux_int_eq_iff_1__float" ) +/* let { array[1..2] of var 0..1: q } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) /\ + aux_int_lt_if_0(x, y, q[1]) /\ + aux_int_gt_if_0(x, y, q[2]) /\ + sum(q) == p + 1 */ + else + assert( false, "Don't use aux_int_eq_iff_1__float with fAuxIntEqOLD00" ) + endif; + + % Alternative 2 +predicate aux_int_eq_iff_1__WEAK1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q_458 } + in + aux_int_lt_if_0(x - p, y, q_458[1]) /\ + aux_int_gt_if_0(x + p, y, q_458[2]) /\ + sum(q_458) <= 2 - 2*p /\ + sum(q_458) <= 1 + p; + + % Alternative 1 +predicate alt_1_aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q } + in + aux_int_lt_if_0(x - p, y, q[1]) /\ + aux_int_gt_if_0(x + p, y, q[2]) /\ + q[1] <= 1 - p /\ + q[2] <= 1 - p /\ + sum(q) <= 1 + p; + +predicate aux_float_eq_iff_1(var float: x, var float: y, var int: p) = + if is_fixed(p) then + if 1==fix(p) then x==y else x!=y endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + abort(" aux_float_eq_iff_1 should not be used with full domain postprocessing") + elseif fAuxFloatEqOLD00 then + let { array[1..2] of var 0..1: q } + in + aux_float_le_if_1(x, y, p) /\ + aux_float_ge_if_1(x, y, p) /\ + aux_float_lt_if_0(x, y, (q[1])) /\ + aux_float_gt_if_0(x, y, (q[2])) /\ + sum(i in 1..2)((q[i])) == 1 + p + else + %% redundant p == (x<=y /\ y<=x) /\ + 1+p == (x<=y) + (y<=x) + endif; + + + +% ----------------------------- Domains postpro --------------------------- + +%%%%%%%%%%%%%%%%%% predicate int_le_reif__POST(var int: x, var int: y, var int: b); +%%%%%%%%%%%%%%%%%% predicate int_le_reif__POST(int: x, var int: y, var int: b); +%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn +%% var, const +predicate int_le_reif__POST(var int: x, int: y, var int: b); +%% var, const +predicate int_ge_reif__POST(var int: x, int: y, var int: b); + +%% var, const +predicate int_eq_reif__POST(var int: x, int: y, var int: b); +%% var, const +predicate int_ne__POST(var int: x, int: y); + +%%%%%%%%%%%%%%%%%% predicate float_le_reif__POST(var float: x, var float: y, var int: b); +%%%%%%%%%%%%%%%%%% predicate float_le_reif__POST(float: x, var float: y, var int: b); +%% var, const +predicate float_le_reif__POST(var float: x, float: y, var int: b, float: epsRel); +%% var, const +predicate float_ge_reif__POST(var float: x, float: y, var int: b, float: epsRel); + +%% var, var +predicate aux_float_lt_zero_iff_1__POST(var float: x, var int: b, float: epsRel); + +%% var, const +predicate float_eq_reif__POST(var float: x, float: y, var int: b, float: epsRel); +%% var, const +predicate float_ne__POST(var float: x, float: y, float: epsRel); + + diff --git a/share/minizinc/linear/subcircuit_wDummy.mzn b/share/minizinc/linear/subcircuit_wDummy.mzn new file mode 100644 index 0000000..48fb747 --- /dev/null +++ b/share/minizinc/linear/subcircuit_wDummy.mzn @@ -0,0 +1,86 @@ +/* Linearized version + * Uses a predicate which constructs a subcircuit which always includes an extra dummy vertex + * Is worse than the just slightly adapted standard variant... +*/ +include "alldifferent.mzn"; + +/** @group globals + Constrains the elements of \a x to define a subcircuit where \a x[\p i] = \p j + means that \p j is the successor of \p i and \a x[\p i] = \p i means that \p i + is not in the circuit. +*/ +%% Linear version +predicate subcircuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + constraint forall(i in S)(x[i] in l..u), + array[l..u+1] of var l..u+1: xx, + constraint forall(i in S)(xx[i] in dom(x[i]) union {u+1}), + } in + alldifferent(x) /\ + subcircuit_wDummy(xx) /\ + forall( i in S, j in dom(x[i]) )( %% also when i==j? + eq_encode(x[i])[j] >= 2*eq_encode(xx[i])[j] + + eq_encode(xx[i])[u+1] + eq_encode(xx[u+1])[j] - 1 %% -1 + /\ + eq_encode(x[i])[j] >= eq_encode(xx[i])[j] + /\ + eq_encode(x[i])[j] <= eq_encode(xx[i])[j] + eq_encode(xx[i])[u+1] + /\ + eq_encode(x[i])[j] <= eq_encode(xx[i])[j] + eq_encode(xx[u+1])[j] + ) + /\ + forall( i in S )( + eq_encode(x[i])[i] == eq_encode(xx[i])[i] + ) + ; + +%% Should include at least 2 nodes if >0? +%% xx[n] is dummy +predicate subcircuit_wDummy(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + set of int: S__ = S diff {u}, %% the real nodes + array[S__] of var 2..n: order, +%% constraint order[n]==1, %% fix the dummy +%% var bool: empty = (firstin == u+1), no, break 2-cycles with dummy + } in + alldifferent(x) /\ + % NO alldifferent(order) /\ + + %%% MTZ model. Note that INTEGER order vars seem better!: + forall( i in S__, j in dom(x[i]) where i!=j /\ j!=u )( + order[i] - order[j] + (n-1)*eq_encode(x[i])[j] + % + (n-3)*bool2int(x[j]==i) %% the Desrochers & Laporte '91 term + % --- strangely enough it is much worse on vrp-s2-v2-c7_vrp-v2-c7_det_ADAPT_1_INVERSE.mzn! + <= n-2 ) /\ + + %% Break 2-cycles with dummy: + forall( i in S__ )( + eq_encode(x[i])[u] + eq_encode(x[u])[i] <= 1 + /\ + %% Ensure dummy is in: + if i in dom(x[i]) then + eq_encode(x[i])[i] >= eq_encode(x[u])[u] + else + true + endif + ) + /\ + + % Symmetry? Each node that is not in is numbered after the lastin node. + forall(i in S) ( + true +% (not ins[i]) <-> (n == order[i]) + ) + ; + +predicate subcircuit_reif(array[int] of var int: x, var bool: b) = + abort("Reified subcircuit/1 is not supported."); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/all_different_int.mzn b/share/minizinc/linear_old/all_different_int.mzn new file mode 100644 index 0000000..26a245d --- /dev/null +++ b/share/minizinc/linear_old/all_different_int.mzn @@ -0,0 +1,19 @@ +%-----------------------------------------------------------------------------% +% 'all_different' constrains an array of objects to be all different. +% +% Linear version: equality encoding; see e.g. [Refalo, CP 2000] +% +% For a given d in dom(x), at most one i with x_i = d can exist. +%-----------------------------------------------------------------------------% + +include "domain_encodings.mzn"; + +predicate all_different_int(array[int] of var int: x) = + let { + array[int,int] of var 0..1: x_eq_d = eq_encode(x) + } in ( + forall(d in index_set_2of2(x_eq_d))( sum(i in index_set_1of2(x_eq_d))( x_eq_d[i,d] ) <= 1 ) + ); + + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/domain_encodings.mzn b/share/minizinc/linear_old/domain_encodings.mzn new file mode 100644 index 0000000..7a37905 --- /dev/null +++ b/share/minizinc/linear_old/domain_encodings.mzn @@ -0,0 +1,57 @@ +%-----------------------------------------------------------------------------% +% Domain encodings +%-----------------------------------------------------------------------------% + +% Linear equality encoding + + % Single variable: x = d <-> x_eq_d[d] +predicate equality_encoding(var int: x, array[int] of var 0..1: x_eq_d) = + x in index_set(x_eq_d) + /\ + sum(d in dom(x))( x_eq_d[d] ) = 1 + /\ + sum(d in dom(x))( d * x_eq_d[d] ) = x; + + % Array of variables: x[i] = d <-> x_eq_d[i,d] +predicate equality_encoding(array[int] of var int: x, + array[int, int] of var 0..1: x_eq_d) = + forall(i in index_set(x))( + x[i] in index_set_2of2(x_eq_d) + /\ + sum(d in index_set_2of2(x_eq_d))( x_eq_d[i,d] ) = 1 + /\ + sum(d in index_set_2of2(x_eq_d))( d * x_eq_d[i,d] ) = x[i] + ); + +function var int: eq_new_var(var int: x, int: i) = + if i in dom(x) then + let { + var 0..1: xi; + } in xi + else 0 endif; + +function array[int] of var 0..1: eq_encode(var int: x) ::promise_total = + let { + array[int] of var 0..1: y = array1d(lb(x)..ub(x),[eq_new_var(x,i) | i in lb(x)..ub(x)]); + constraint equality_encoding(x,y); + } in y; + +function array[int] of int: eq_encode(int: x) ::promise_total = + array1d(lb(x)..ub(x),[ if i=x then 1 else 0 endif | i in lb(x)..ub(x)]); + +function array[int,int] of var int: eq_encode(array[int] of var int: x) ::promise_total = + let { + array[index_set(x),lb_array(x)..ub_array(x)] of var 0..1: y = + array2d(index_set(x),lb_array(x)..ub_array(x), + [ let { + array[int] of var int: xi = eq_encode(x[i]) + } in if j in index_set(xi) then xi[j] else 0 endif + | i in index_set(x), j in lb_array(x)..ub_array(x)] + ) + } in y; + +function array[int,int] of int: eq_encode(array[int] of int: x) ::promise_total = + array2d(index_set(x),lb_array(x)..ub_array(x),[ if j=x[i] then 1 else 0 endif | i in index_set(x), j in lb_array(x)..ub_array(x)]); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/inverse.mzn b/share/minizinc/linear_old/inverse.mzn new file mode 100644 index 0000000..06f06e0 --- /dev/null +++ b/share/minizinc/linear_old/inverse.mzn @@ -0,0 +1,16 @@ +%-----------------------------------------------------------------------------% +% Constrains two arrays of int variables to represent inverse functions. +% All the values in each array must be within the index set of the other array. +% +% Linear version. +%-----------------------------------------------------------------------------% + +include "domain_encodings.mzn"; + +predicate inverse(array[int] of var int: f, array[int] of var int: g) = + let { + array[int,int] of var 0..1: map_f = eq_encode(f); + array[int,int] of var 0..1: map_g = eq_encode(g); + } in forall (i in index_set(f), j in index_set(g)) (map_f[i,j] = map_g[j,i]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/linear/all_different_int.mzn b/share/minizinc/linear_old/linear/all_different_int.mzn new file mode 100644 index 0000000..26a245d --- /dev/null +++ b/share/minizinc/linear_old/linear/all_different_int.mzn @@ -0,0 +1,19 @@ +%-----------------------------------------------------------------------------% +% 'all_different' constrains an array of objects to be all different. +% +% Linear version: equality encoding; see e.g. [Refalo, CP 2000] +% +% For a given d in dom(x), at most one i with x_i = d can exist. +%-----------------------------------------------------------------------------% + +include "domain_encodings.mzn"; + +predicate all_different_int(array[int] of var int: x) = + let { + array[int,int] of var 0..1: x_eq_d = eq_encode(x) + } in ( + forall(d in index_set_2of2(x_eq_d))( sum(i in index_set_1of2(x_eq_d))( x_eq_d[i,d] ) <= 1 ) + ); + + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/linear/domain_encodings.mzn b/share/minizinc/linear_old/linear/domain_encodings.mzn new file mode 100644 index 0000000..7a37905 --- /dev/null +++ b/share/minizinc/linear_old/linear/domain_encodings.mzn @@ -0,0 +1,57 @@ +%-----------------------------------------------------------------------------% +% Domain encodings +%-----------------------------------------------------------------------------% + +% Linear equality encoding + + % Single variable: x = d <-> x_eq_d[d] +predicate equality_encoding(var int: x, array[int] of var 0..1: x_eq_d) = + x in index_set(x_eq_d) + /\ + sum(d in dom(x))( x_eq_d[d] ) = 1 + /\ + sum(d in dom(x))( d * x_eq_d[d] ) = x; + + % Array of variables: x[i] = d <-> x_eq_d[i,d] +predicate equality_encoding(array[int] of var int: x, + array[int, int] of var 0..1: x_eq_d) = + forall(i in index_set(x))( + x[i] in index_set_2of2(x_eq_d) + /\ + sum(d in index_set_2of2(x_eq_d))( x_eq_d[i,d] ) = 1 + /\ + sum(d in index_set_2of2(x_eq_d))( d * x_eq_d[i,d] ) = x[i] + ); + +function var int: eq_new_var(var int: x, int: i) = + if i in dom(x) then + let { + var 0..1: xi; + } in xi + else 0 endif; + +function array[int] of var 0..1: eq_encode(var int: x) ::promise_total = + let { + array[int] of var 0..1: y = array1d(lb(x)..ub(x),[eq_new_var(x,i) | i in lb(x)..ub(x)]); + constraint equality_encoding(x,y); + } in y; + +function array[int] of int: eq_encode(int: x) ::promise_total = + array1d(lb(x)..ub(x),[ if i=x then 1 else 0 endif | i in lb(x)..ub(x)]); + +function array[int,int] of var int: eq_encode(array[int] of var int: x) ::promise_total = + let { + array[index_set(x),lb_array(x)..ub_array(x)] of var 0..1: y = + array2d(index_set(x),lb_array(x)..ub_array(x), + [ let { + array[int] of var int: xi = eq_encode(x[i]) + } in if j in index_set(xi) then xi[j] else 0 endif + | i in index_set(x), j in lb_array(x)..ub_array(x)] + ) + } in y; + +function array[int,int] of int: eq_encode(array[int] of int: x) ::promise_total = + array2d(index_set(x),lb_array(x)..ub_array(x),[ if j=x[i] then 1 else 0 endif | i in index_set(x), j in lb_array(x)..ub_array(x)]); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/linear/inverse.mzn b/share/minizinc/linear_old/linear/inverse.mzn new file mode 100644 index 0000000..06f06e0 --- /dev/null +++ b/share/minizinc/linear_old/linear/inverse.mzn @@ -0,0 +1,16 @@ +%-----------------------------------------------------------------------------% +% Constrains two arrays of int variables to represent inverse functions. +% All the values in each array must be within the index set of the other array. +% +% Linear version. +%-----------------------------------------------------------------------------% + +include "domain_encodings.mzn"; + +predicate inverse(array[int] of var int: f, array[int] of var int: g) = + let { + array[int,int] of var 0..1: map_f = eq_encode(f); + array[int,int] of var 0..1: map_g = eq_encode(g); + } in forall (i in index_set(f), j in index_set(g)) (map_f[i,j] = map_g[j,i]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/linear/redefinitions-2.0.2.mzn b/share/minizinc/linear_old/linear/redefinitions-2.0.2.mzn new file mode 100644 index 0000000..21c746d --- /dev/null +++ b/share/minizinc/linear_old/linear/redefinitions-2.0.2.mzn @@ -0,0 +1,21 @@ +% This file contains redefinitions of standard builtins for version 2.0.2 +% that can be overridden by solvers. + + +predicate symmetry_breaking_constraint(var bool: b) = b; + +predicate redundant_constraint(var bool: b) = b; + + +%% Linearized element: just call without shifting +predicate array_var_bool_element_nonshifted(var int: idx, array[int] of var bool: x, var bool: c) = + array_var_bool_element(idx,x,c); + +predicate array_var_int_element_nonshifted(var int: idx, array[int] of var int: x, var int: c) = + array_var_int_element(idx,x,c); + +predicate array_var_float_element_nonshifted(var int: idx, array[int] of var float: x, var float: c) = + array_var_float_element(idx,x,c); + +predicate array_var_set_element_nonshifted(var int: idx, array[int] of var set of int: x, var set of int: c) = + array_var_set_element(idx,x,c); diff --git a/share/minizinc/linear_old/linear/redefinitions-2.0.mzn b/share/minizinc/linear_old/linear/redefinitions-2.0.mzn new file mode 100644 index 0000000..454cdcc --- /dev/null +++ b/share/minizinc/linear_old/linear/redefinitions-2.0.mzn @@ -0,0 +1,6 @@ +predicate bool_clause_reif(array[int] of var bool: p, array[int] of var bool: n, var bool: c) = + c = ( sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1 + ); diff --git a/share/minizinc/linear_old/linear/redefinitions.mzn b/share/minizinc/linear_old/linear/redefinitions.mzn new file mode 100644 index 0000000..b28be33 --- /dev/null +++ b/share/minizinc/linear_old/linear/redefinitions.mzn @@ -0,0 +1,615 @@ +%-----------------------------------------------------------------------------% +% FlatZinc built-in redefinitions for linear solvers. +% +% Sebastian Brand +% Gleb Belov Corrected array_var_float_element and float_lin_lt_reif +%-----------------------------------------------------------------------------% + +function var bool: reverse_map(var int: x) = (x==1); +function bool: reverse_map(int: x) = (x==1); + +function var int: bool2int(var bool: x) :: promise_total = + let { var 0..1: b2i; + constraint (x = reverse_map(b2i)) ::is_reverse_map ; + } in b2i; + +predicate bool_eq(var bool: x, var bool: y) = + bool2int(x)==bool2int(y); + +%-----------------------------------------------------------------------------% +% Strict inequality +% +% Uncomment the following redefinition for FlatZinc MIP solver interfaces that +% do not support strict inequality. Note that it does not preserve equivalence +% (some solutions of the original problem may become invalid). + +% predicate float_lt(var float: x, var float: y) = x + 1e-06 <= y; + +%-----------------------------------------------------------------------------% +% +% Logic operations +% +%-----------------------------------------------------------------------------% + +predicate bool_not(var bool: p, var bool: q) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q) } + in + x + y = 1; + + +predicate bool_and(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x + y <= z + 1 /\ + x + y >= z * 2; + % x >= z /\ y >= z; % alternative + + +predicate bool_or(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x + y >= z /\ + x + y <= z * 2; + % x <= z /\ y <= z; % alternative + + +predicate bool_xor(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x <= y + z /\ + y <= x + z /\ + z <= x + y /\ + x + y + z <= 2; + + +predicate bool_eq_reif(var bool: p, var bool: q, var bool: r) = + if is_fixed(q) then % frequent case + if fix(q) = true then p = r else bool_not(p,r) endif + else + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x + y <= z + 1 /\ + x + z <= y + 1 /\ + y + z <= x + 1 /\ + x + y + z >= 1 + endif; + + +predicate bool_ne_reif(var bool: p, var bool: q, var bool: r) = + bool_xor(p, q, r); + + +predicate bool_le(var bool: p, var bool: q) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q) } + in + x <= y; + + +predicate bool_le_reif(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + 1 - x + y >= z /\ + 1 - x + y <= z * 2; + % 1 - x <= z /\ y <= z; % alternative + + +predicate bool_lt(var bool: p, var bool: q) = + not p /\ q; + + +predicate bool_lt_reif(var bool: p, var bool: q, var bool: r) = + (not p /\ q) <-> r; + +%-----------------------------------------------------------------------------% + +predicate array_bool_or(array[int] of var bool: a, var bool: b) = + if is_fixed(b) then % frequent case + if fix(b) = true then + sum(i in index_set(a))( bool2int(a[i]) ) >= 1 + else + forall(i in index_set(a))( not a[i] ) + endif + else + let { var 0..1: x = bool2int(b), + array[1..length(a)] of var 0..1: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + sum(c) >= x /\ + sum(c) <= x * length(a) + endif; + +predicate array_bool_and(array[int] of var bool: a, var bool: b) = + let { var 0..1: x = bool2int(b), + array[1..length(a)] of var 0..1: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + length(a) - sum(c) >= 1 - x /\ + length(a) - sum(c) <= (1 - x) * length(a); + +predicate array_bool_xor(array[int] of var bool: a) = + let { var 0..length(a): m } + in + sum(i in 1..length(a))( bool2int(a[i]) ) = 1 + 2 * m; + +predicate bool_clause(array[int] of var bool: p, array[int] of var bool: n) = + sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1; + +% predicate array_bool_xor(array[int] of var bool: a) = .. sum(a) is odd .. + +%-----------------------------------------------------------------------------% +% +% Linear equations and inequations +% +%-----------------------------------------------------------------------------% + +predicate int_le_reif(var int: x, var int: y, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_gt_if_0(x, y, p); + + +predicate int_lt_reif(var int: x, var int: y, var bool: b) = + int_le_reif(x, y - 1, b); + + +predicate int_ne(var int: x, var int: y) = + let { var 0..1: p } + in + aux_int_lt_if_1(x, y, p) /\ + aux_int_gt_if_0(x, y, p); + +predicate int_lin_ne(array[int] of int: c, array[int] of var int: x, int: d) = + int_ne(sum(i in index_set(x))( c[i]*x[i] ),d); + +predicate int_eq_reif(var int: x, var int: y, var bool: b) = + aux_int_eq_iff_1(x, y, bool2int(b)); + + +predicate int_ne_reif(var int: x, var int: y, var bool: b) = + aux_int_eq_iff_1(x, y, 1 - bool2int(b)); + +%-----------------------------------------------------------------------------% + +predicate int_lin_eq_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, bool2int(b)); + + +predicate int_lin_ne_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, 1 - bool2int(b)); + + +predicate int_lin_le_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_int_le_if_1(sum(i in index_set(x))( c[i] * x[i] ), d, p) /\ + aux_int_gt_if_0(sum(i in index_set(x))( c[i] * x[i] ), d, p); + + +predicate int_lin_lt_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + int_lin_le_reif(c, x, d - 1, b); + +%-----------------------------------------------------------------------------% + +predicate float_le_reif(var float: x, var float: y, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_float_le_if_1(x, y, int2float(p)) /\ + aux_float_gt_if_0(x, y, int2float(p)); + + +predicate float_lt_reif(var float: x, var float: y, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_float_lt_if_1(x, y, int2float(p)) /\ + aux_float_ge_if_0(x, y, int2float(p)); + + +predicate float_ne(var float: x, var float: y) = + let { var 0..1: p } + in + aux_float_lt_if_1(x, y, int2float(p)) /\ + aux_float_gt_if_0(x, y, int2float(p)); + + +predicate float_eq_reif(var float: x, var float: y, var bool: b) = + aux_float_eq_iff_1(x, y, int2float(bool2int(b))); + + +predicate float_ne_reif(var float: x, var float: y, var bool: b) = + aux_float_eq_iff_1(x, y, 1.0 - int2float(bool2int(b))); + +%-----------------------------------------------------------------------------% + +predicate float_lin_eq_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, + int2float(bool2int(b))); + + +predicate float_lin_ne_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, + 1.0 - int2float(bool2int(b))); + + +predicate float_lin_le_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + let { var 0.0..1.0: p = int2float(bool2int(b)) } + in + aux_float_le_if_1(sum(i in index_set(x))( c[i] * x[i] ), d, p) /\ + aux_float_gt_if_0(sum(i in index_set(x))( c[i] * x[i] ), d, p); + + +predicate float_lin_lt_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + let { var 0.0..1.0: p = int2float(bool2int(b)) } + in + aux_float_lt_if_1(sum(i in index_set(x))( c[i] * x[i] ), d, p) /\ + aux_float_ge_if_0(sum(i in index_set(x))( c[i] * x[i] ), d, p); + +%-----------------------------------------------------------------------------% +% Minimum, maximum, absolute value + +predicate int_abs(var int: x, var int: z) = + let { var 0..1: p } + in + % z <= x \/ z <= -x + aux_int_le_if_1(z, x, p) /\ + aux_int_le_if_0(z, -x, p) /\ + z >= x /\ + z >= -x /\ + z >= 0; + + +predicate int_min(var int: x, var int: y, var int: z) = + let { var 0..1: p } + in + % z >= x \/ z >= y + aux_int_ge_if_1(z, x, p) /\ + aux_int_ge_if_0(z, y, p) /\ + z <= x /\ + z <= y; + + +predicate int_max(var int: x, var int: y, var int: z) = + let { var 0..1: p } + in + % z <= x \/ z <= y + aux_int_le_if_1(z, x, p) /\ + aux_int_le_if_0(z, y, p) /\ + z >= x /\ + z >= y; + + +predicate float_abs(var float: x, var float: z) = + let { var 0..1: p } + in + % z <= x \/ z <= -x + aux_float_le_if_1(z, x, int2float(p)) /\ + aux_float_le_if_0(z, -x, int2float(p)) /\ + z >= x /\ + z >= -x /\ + z >= 0.0; + + +predicate float_min(var float: x, var float: y, var float: z) = + let { var 0..1: p } + in + % z >= x \/ z >= y + aux_float_ge_if_1(z, x, int2float(p)) /\ + aux_float_ge_if_0(z, y, int2float(p)) /\ + z <= x /\ + z <= y; + + +predicate float_max(var float: x, var float: y, var float: z) = + let { var 0..1: p } + in + % z <= x \/ z <= y + aux_float_le_if_1(z, x, int2float(p)) /\ + aux_float_le_if_0(z, y, int2float(p)) /\ + z >= x /\ + z >= y; + +%-----------------------------------------------------------------------------% +% Multiplication and division + +predicate int_div(var int: x, var int: y, var int: q) = + let { var 0..max(abs(lb(y)), abs(ub(y))) - 1: r } + in + aux_int_division_modulo(x,y,q,r); + + +predicate int_mod(var int: x, var int: y, var int: r) = + let { + int: bx = max(abs(lb(x)), abs(ub(x))); + var -bx..bx: q; + int: by = max(abs(lb(y)), abs(ub(y))); + constraint r in -by..by; + } + in + aux_int_division_modulo(x,y,q,r); + + +predicate aux_int_division_modulo(var int: x, var int: y, var int: q, + var int: r) = + x = y * q + r /\ + let { array[1..2] of var 0..1: p } + in + % 0 < x -> 0 <= r which is 0 >= x \/ 0 <= r + aux_int_le_if_1(x, 0, p[1]) /\ + aux_int_ge_if_0(r, 0, p[1]) /\ + % x < 0 -> r <= 0 which is x >= 0 \/ r <= 0 + aux_int_ge_if_1(x, 0, p[2]) /\ + aux_int_le_if_0(r, 0, p[2]) /\ + % abs(r) < abs(y) + let { var 1.. max(abs(lb(y)), abs(ub(y))): w = abs(y) } + in + w > r /\ + w > -r; + + +predicate int_times(var int: x, var int: y, var int: z) = + if card(dom(x)) > card(dom(y)) then int_times(y,x,z) + else + let { set of int: s = lb(x)..ub(x), + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + array[s] of var min(r)..max(r): ady = array1d(s, [ d*y | d in s ]) } + in + ady[x] = z + endif; + +%-----------------------------------------------------------------------------% +% Array 'element' constraints + +predicate array_bool_element(var int: x, array[int] of bool: a, var bool: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + + +predicate array_var_bool_element(var int: x, array[int] of var bool: a, + var bool: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + + +predicate array_int_element(var int: x, array[int] of int: a, var int: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + +predicate array_var_int_element(var int: x, array[int] of var int: a, + var int: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + + +predicate array_float_element(var int: x, array[int] of float: a, + var float: z) = + let { set of int: ix = index_set(a), + array[ix] of var 0..1: x_eq_d } + in + sum(i in ix)( x_eq_d[i] ) = 1 + /\ + sum(i in ix)( i * x_eq_d[i] ) = x + /\ + sum(i in ix)( a[i] * int2float(x_eq_d[i]) ) = z; + + +predicate array_var_float_element(var int: x, array[int] of var float: a, + var float: z) = + let { set of int: ix = index_set(a), + array[ix] of var 0..1: x_eq_d } + in + sum(i in ix)( x_eq_d[i] ) = 1 + /\ + sum(i in ix)( i * x_eq_d[i] ) = x + /\ + forall(i in ix)( + % x_eq_d[i] -> a[i] = a2[i] + a[i] - z >= (lb(a[i])-ub(z))*int2float(1-x_eq_d[i]) + /\ + z - a[i] >= (lb(z)-ub(a[i]))*int2float(1-x_eq_d[i]) + ); + +%-----------------------------------------------------------------------------% +% Domain constraints + +% XXX only for a fixed set + +predicate set_in(var int: x, set of int: s) = + if s = min(s)..max(s) then + min(s) <= x /\ x <= max(s) + else + exists(e in s)( x = e ) + endif; + +% XXX only for a fixed set +predicate set_in_reif(var int: x, set of int: s, var bool: b) = + b <-> + exists(i in 1..length([ 0 | e in s where not (e - 1 in s) ]))( + let { int: l = [ e | e in s where not (e - 1 in s) ][i], + int: r = [ e | e in s where not (e + 1 in s) ][i] } + in + l <= x /\ x <= r + ); + + % Alternative +predicate alt_set_in_reif(var int: x, set of int: s, var bool: b) = + b <-> + if s = min(s)..max(s) then + min(s) <= x /\ x <= max(s) + else + exists(e in s)( x = e ) + endif; + +%-----------------------------------------------------------------------------% +% Auxiliary: equality reified onto a 0/1 variable + +predicate aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q_458 } + in + aux_int_lt_if_0(x - p, y, q_458[1]) /\ + aux_int_gt_if_0(x + p, y, q_458[2]) /\ + sum(q_458) <= 2 - 2*p /\ + sum(q_458) <= 1 + p; + + % Alternative 1 +predicate alt_1_aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q } + in + aux_int_lt_if_0(x - p, y, q[1]) /\ + aux_int_gt_if_0(x + p, y, q[2]) /\ + q[1] <= 1 - p /\ + q[2] <= 1 - p /\ + sum(q) <= 1 + p; + + % Alternative 2 +predicate alt_2_aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) /\ + aux_int_lt_if_0(x, y, q[1]) /\ + aux_int_gt_if_0(x, y, q[2]) /\ + sum(q) <= p + 1; + + +predicate aux_float_eq_iff_1(var float: x, var float: y, var float: p) = + let { array[1..2] of var 0..1: q } + in + aux_float_le_if_1(x, y, p) /\ + aux_float_ge_if_1(x, y, p) /\ + aux_float_lt_if_0(x, y, int2float(q[1])) /\ + aux_float_gt_if_0(x, y, int2float(q[2])) /\ + int2float(sum(q)) <= 1.0 + p; + +%-----------------------------------------------------------------------------% +% Auxiliary: indicator constraints +% p -> x # 0 where p is a 0/1 variable and # is a comparison + +% Base cases + +predicate aux_int_le_zero_if_0(var int: x, var int: p) = + x <= ub(x) * p; + +predicate aux_float_le_zero_if_0(var float: x, var float: p) = + x <= ub(x) * p; + +predicate aux_float_lt_zero_if_0(var float: x, var float: p) = + let { float: rho = 1e-02 * abs(ub(x)) } % same order of magnitude as ub(x) + in + x < (ub(x) + rho) * p; + + +% Derived cases + +predicate aux_int_le_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, p); + +predicate aux_int_ge_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, p); + +predicate aux_int_le_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, 1 - p); + +predicate aux_int_ge_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, 1 - p); + +predicate aux_int_lt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, p); + +predicate aux_int_gt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x + 1, p); + +predicate aux_int_lt_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, 1 - p); + + +predicate aux_float_le_if_0(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(x - y, p); + +predicate aux_float_ge_if_0(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(y - x, p); + +predicate aux_float_le_if_1(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(x - y, 1.0 - p); + +predicate aux_float_ge_if_1(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(y - x, 1.0 - p); + +predicate aux_float_lt_if_0(var float: x, var float: y, var float: p) = + aux_float_lt_zero_if_0(x - y, p); + +predicate aux_float_gt_if_0(var float: x, var float: y, var float: p) = + aux_float_lt_zero_if_0(y - x, p); + +predicate aux_float_lt_if_1(var float: x, var float: y, var float: p) = + aux_float_lt_zero_if_0(x - y, 1.0 - p); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% + +annotation bool_search(array[int] of var bool: x, ann:a1, ann:a2, ann:a3) = + int_search([bool2int(x[i]) | i in index_set(x)],a1,a2,a3); + +predicate array_int_maximum(var int: m, array[int] of var int: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: ly = lb_array(x), + int: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +predicate array_float_maximum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +predicate array_int_minimum(var int: m, array[int] of var int: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: ly = lb_array(x), + int: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); + +predicate array_float_minimum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); + +mzn_opt_only_range_domains = true; diff --git a/share/minizinc/linear_old/linear/table_int.mzn b/share/minizinc/linear_old/linear/table_int.mzn new file mode 100644 index 0000000..7b49507 --- /dev/null +++ b/share/minizinc/linear_old/linear/table_int.mzn @@ -0,0 +1,21 @@ +%-----------------------------------------------------------------------------% +% A 'table' constraint table(x, T) represents the constraint x in T where we +% consider each row in T to be a tuple and T as a set of tuples. +% +% Linear version. +% +% See also the equality encoding of the 'element' constraint. +%-----------------------------------------------------------------------------% + +predicate table_int(array[int] of var int: x, array[int, int] of int: t) = + assert(index_set_2of2(t) = index_set(x), + "The second dimension of the table must equal the number of " ++ + "variables in the first argument", + let { set of int: it = index_set_1of2(t), + array[it] of var 0..1: lambda } + in + sum(lambda) = 1 + /\ + forall(j in index_set(x))( sum(i in it)( t[i,j]*lambda[i] ) = x[j] ) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_old/redefinitions-2.0.2.mzn b/share/minizinc/linear_old/redefinitions-2.0.2.mzn new file mode 100644 index 0000000..21c746d --- /dev/null +++ b/share/minizinc/linear_old/redefinitions-2.0.2.mzn @@ -0,0 +1,21 @@ +% This file contains redefinitions of standard builtins for version 2.0.2 +% that can be overridden by solvers. + + +predicate symmetry_breaking_constraint(var bool: b) = b; + +predicate redundant_constraint(var bool: b) = b; + + +%% Linearized element: just call without shifting +predicate array_var_bool_element_nonshifted(var int: idx, array[int] of var bool: x, var bool: c) = + array_var_bool_element(idx,x,c); + +predicate array_var_int_element_nonshifted(var int: idx, array[int] of var int: x, var int: c) = + array_var_int_element(idx,x,c); + +predicate array_var_float_element_nonshifted(var int: idx, array[int] of var float: x, var float: c) = + array_var_float_element(idx,x,c); + +predicate array_var_set_element_nonshifted(var int: idx, array[int] of var set of int: x, var set of int: c) = + array_var_set_element(idx,x,c); diff --git a/share/minizinc/linear_old/redefinitions-2.0.mzn b/share/minizinc/linear_old/redefinitions-2.0.mzn new file mode 100644 index 0000000..454cdcc --- /dev/null +++ b/share/minizinc/linear_old/redefinitions-2.0.mzn @@ -0,0 +1,6 @@ +predicate bool_clause_reif(array[int] of var bool: p, array[int] of var bool: n, var bool: c) = + c = ( sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1 + ); diff --git a/share/minizinc/linear_old/redefinitions.mzn b/share/minizinc/linear_old/redefinitions.mzn new file mode 100644 index 0000000..b28be33 --- /dev/null +++ b/share/minizinc/linear_old/redefinitions.mzn @@ -0,0 +1,615 @@ +%-----------------------------------------------------------------------------% +% FlatZinc built-in redefinitions for linear solvers. +% +% Sebastian Brand +% Gleb Belov Corrected array_var_float_element and float_lin_lt_reif +%-----------------------------------------------------------------------------% + +function var bool: reverse_map(var int: x) = (x==1); +function bool: reverse_map(int: x) = (x==1); + +function var int: bool2int(var bool: x) :: promise_total = + let { var 0..1: b2i; + constraint (x = reverse_map(b2i)) ::is_reverse_map ; + } in b2i; + +predicate bool_eq(var bool: x, var bool: y) = + bool2int(x)==bool2int(y); + +%-----------------------------------------------------------------------------% +% Strict inequality +% +% Uncomment the following redefinition for FlatZinc MIP solver interfaces that +% do not support strict inequality. Note that it does not preserve equivalence +% (some solutions of the original problem may become invalid). + +% predicate float_lt(var float: x, var float: y) = x + 1e-06 <= y; + +%-----------------------------------------------------------------------------% +% +% Logic operations +% +%-----------------------------------------------------------------------------% + +predicate bool_not(var bool: p, var bool: q) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q) } + in + x + y = 1; + + +predicate bool_and(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x + y <= z + 1 /\ + x + y >= z * 2; + % x >= z /\ y >= z; % alternative + + +predicate bool_or(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x + y >= z /\ + x + y <= z * 2; + % x <= z /\ y <= z; % alternative + + +predicate bool_xor(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x <= y + z /\ + y <= x + z /\ + z <= x + y /\ + x + y + z <= 2; + + +predicate bool_eq_reif(var bool: p, var bool: q, var bool: r) = + if is_fixed(q) then % frequent case + if fix(q) = true then p = r else bool_not(p,r) endif + else + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + x + y <= z + 1 /\ + x + z <= y + 1 /\ + y + z <= x + 1 /\ + x + y + z >= 1 + endif; + + +predicate bool_ne_reif(var bool: p, var bool: q, var bool: r) = + bool_xor(p, q, r); + + +predicate bool_le(var bool: p, var bool: q) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q) } + in + x <= y; + + +predicate bool_le_reif(var bool: p, var bool: q, var bool: r) = + let { var 0..1: x = bool2int(p), + var 0..1: y = bool2int(q), + var 0..1: z = bool2int(r) } + in + 1 - x + y >= z /\ + 1 - x + y <= z * 2; + % 1 - x <= z /\ y <= z; % alternative + + +predicate bool_lt(var bool: p, var bool: q) = + not p /\ q; + + +predicate bool_lt_reif(var bool: p, var bool: q, var bool: r) = + (not p /\ q) <-> r; + +%-----------------------------------------------------------------------------% + +predicate array_bool_or(array[int] of var bool: a, var bool: b) = + if is_fixed(b) then % frequent case + if fix(b) = true then + sum(i in index_set(a))( bool2int(a[i]) ) >= 1 + else + forall(i in index_set(a))( not a[i] ) + endif + else + let { var 0..1: x = bool2int(b), + array[1..length(a)] of var 0..1: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + sum(c) >= x /\ + sum(c) <= x * length(a) + endif; + +predicate array_bool_and(array[int] of var bool: a, var bool: b) = + let { var 0..1: x = bool2int(b), + array[1..length(a)] of var 0..1: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + length(a) - sum(c) >= 1 - x /\ + length(a) - sum(c) <= (1 - x) * length(a); + +predicate array_bool_xor(array[int] of var bool: a) = + let { var 0..length(a): m } + in + sum(i in 1..length(a))( bool2int(a[i]) ) = 1 + 2 * m; + +predicate bool_clause(array[int] of var bool: p, array[int] of var bool: n) = + sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1; + +% predicate array_bool_xor(array[int] of var bool: a) = .. sum(a) is odd .. + +%-----------------------------------------------------------------------------% +% +% Linear equations and inequations +% +%-----------------------------------------------------------------------------% + +predicate int_le_reif(var int: x, var int: y, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_gt_if_0(x, y, p); + + +predicate int_lt_reif(var int: x, var int: y, var bool: b) = + int_le_reif(x, y - 1, b); + + +predicate int_ne(var int: x, var int: y) = + let { var 0..1: p } + in + aux_int_lt_if_1(x, y, p) /\ + aux_int_gt_if_0(x, y, p); + +predicate int_lin_ne(array[int] of int: c, array[int] of var int: x, int: d) = + int_ne(sum(i in index_set(x))( c[i]*x[i] ),d); + +predicate int_eq_reif(var int: x, var int: y, var bool: b) = + aux_int_eq_iff_1(x, y, bool2int(b)); + + +predicate int_ne_reif(var int: x, var int: y, var bool: b) = + aux_int_eq_iff_1(x, y, 1 - bool2int(b)); + +%-----------------------------------------------------------------------------% + +predicate int_lin_eq_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, bool2int(b)); + + +predicate int_lin_ne_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, 1 - bool2int(b)); + + +predicate int_lin_le_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_int_le_if_1(sum(i in index_set(x))( c[i] * x[i] ), d, p) /\ + aux_int_gt_if_0(sum(i in index_set(x))( c[i] * x[i] ), d, p); + + +predicate int_lin_lt_reif(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + int_lin_le_reif(c, x, d - 1, b); + +%-----------------------------------------------------------------------------% + +predicate float_le_reif(var float: x, var float: y, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_float_le_if_1(x, y, int2float(p)) /\ + aux_float_gt_if_0(x, y, int2float(p)); + + +predicate float_lt_reif(var float: x, var float: y, var bool: b) = + let { var 0..1: p = bool2int(b) } + in + aux_float_lt_if_1(x, y, int2float(p)) /\ + aux_float_ge_if_0(x, y, int2float(p)); + + +predicate float_ne(var float: x, var float: y) = + let { var 0..1: p } + in + aux_float_lt_if_1(x, y, int2float(p)) /\ + aux_float_gt_if_0(x, y, int2float(p)); + + +predicate float_eq_reif(var float: x, var float: y, var bool: b) = + aux_float_eq_iff_1(x, y, int2float(bool2int(b))); + + +predicate float_ne_reif(var float: x, var float: y, var bool: b) = + aux_float_eq_iff_1(x, y, 1.0 - int2float(bool2int(b))); + +%-----------------------------------------------------------------------------% + +predicate float_lin_eq_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, + int2float(bool2int(b))); + + +predicate float_lin_ne_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, + 1.0 - int2float(bool2int(b))); + + +predicate float_lin_le_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + let { var 0.0..1.0: p = int2float(bool2int(b)) } + in + aux_float_le_if_1(sum(i in index_set(x))( c[i] * x[i] ), d, p) /\ + aux_float_gt_if_0(sum(i in index_set(x))( c[i] * x[i] ), d, p); + + +predicate float_lin_lt_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + let { var 0.0..1.0: p = int2float(bool2int(b)) } + in + aux_float_lt_if_1(sum(i in index_set(x))( c[i] * x[i] ), d, p) /\ + aux_float_ge_if_0(sum(i in index_set(x))( c[i] * x[i] ), d, p); + +%-----------------------------------------------------------------------------% +% Minimum, maximum, absolute value + +predicate int_abs(var int: x, var int: z) = + let { var 0..1: p } + in + % z <= x \/ z <= -x + aux_int_le_if_1(z, x, p) /\ + aux_int_le_if_0(z, -x, p) /\ + z >= x /\ + z >= -x /\ + z >= 0; + + +predicate int_min(var int: x, var int: y, var int: z) = + let { var 0..1: p } + in + % z >= x \/ z >= y + aux_int_ge_if_1(z, x, p) /\ + aux_int_ge_if_0(z, y, p) /\ + z <= x /\ + z <= y; + + +predicate int_max(var int: x, var int: y, var int: z) = + let { var 0..1: p } + in + % z <= x \/ z <= y + aux_int_le_if_1(z, x, p) /\ + aux_int_le_if_0(z, y, p) /\ + z >= x /\ + z >= y; + + +predicate float_abs(var float: x, var float: z) = + let { var 0..1: p } + in + % z <= x \/ z <= -x + aux_float_le_if_1(z, x, int2float(p)) /\ + aux_float_le_if_0(z, -x, int2float(p)) /\ + z >= x /\ + z >= -x /\ + z >= 0.0; + + +predicate float_min(var float: x, var float: y, var float: z) = + let { var 0..1: p } + in + % z >= x \/ z >= y + aux_float_ge_if_1(z, x, int2float(p)) /\ + aux_float_ge_if_0(z, y, int2float(p)) /\ + z <= x /\ + z <= y; + + +predicate float_max(var float: x, var float: y, var float: z) = + let { var 0..1: p } + in + % z <= x \/ z <= y + aux_float_le_if_1(z, x, int2float(p)) /\ + aux_float_le_if_0(z, y, int2float(p)) /\ + z >= x /\ + z >= y; + +%-----------------------------------------------------------------------------% +% Multiplication and division + +predicate int_div(var int: x, var int: y, var int: q) = + let { var 0..max(abs(lb(y)), abs(ub(y))) - 1: r } + in + aux_int_division_modulo(x,y,q,r); + + +predicate int_mod(var int: x, var int: y, var int: r) = + let { + int: bx = max(abs(lb(x)), abs(ub(x))); + var -bx..bx: q; + int: by = max(abs(lb(y)), abs(ub(y))); + constraint r in -by..by; + } + in + aux_int_division_modulo(x,y,q,r); + + +predicate aux_int_division_modulo(var int: x, var int: y, var int: q, + var int: r) = + x = y * q + r /\ + let { array[1..2] of var 0..1: p } + in + % 0 < x -> 0 <= r which is 0 >= x \/ 0 <= r + aux_int_le_if_1(x, 0, p[1]) /\ + aux_int_ge_if_0(r, 0, p[1]) /\ + % x < 0 -> r <= 0 which is x >= 0 \/ r <= 0 + aux_int_ge_if_1(x, 0, p[2]) /\ + aux_int_le_if_0(r, 0, p[2]) /\ + % abs(r) < abs(y) + let { var 1.. max(abs(lb(y)), abs(ub(y))): w = abs(y) } + in + w > r /\ + w > -r; + + +predicate int_times(var int: x, var int: y, var int: z) = + if card(dom(x)) > card(dom(y)) then int_times(y,x,z) + else + let { set of int: s = lb(x)..ub(x), + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + array[s] of var min(r)..max(r): ady = array1d(s, [ d*y | d in s ]) } + in + ady[x] = z + endif; + +%-----------------------------------------------------------------------------% +% Array 'element' constraints + +predicate array_bool_element(var int: x, array[int] of bool: a, var bool: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + + +predicate array_var_bool_element(var int: x, array[int] of var bool: a, + var bool: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + + +predicate array_int_element(var int: x, array[int] of int: a, var int: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + +predicate array_var_int_element(var int: x, array[int] of var int: a, + var int: z) = + x in index_set(a) /\ + forall(d in index_set(a))( x = d -> a[d] = z ); + + +predicate array_float_element(var int: x, array[int] of float: a, + var float: z) = + let { set of int: ix = index_set(a), + array[ix] of var 0..1: x_eq_d } + in + sum(i in ix)( x_eq_d[i] ) = 1 + /\ + sum(i in ix)( i * x_eq_d[i] ) = x + /\ + sum(i in ix)( a[i] * int2float(x_eq_d[i]) ) = z; + + +predicate array_var_float_element(var int: x, array[int] of var float: a, + var float: z) = + let { set of int: ix = index_set(a), + array[ix] of var 0..1: x_eq_d } + in + sum(i in ix)( x_eq_d[i] ) = 1 + /\ + sum(i in ix)( i * x_eq_d[i] ) = x + /\ + forall(i in ix)( + % x_eq_d[i] -> a[i] = a2[i] + a[i] - z >= (lb(a[i])-ub(z))*int2float(1-x_eq_d[i]) + /\ + z - a[i] >= (lb(z)-ub(a[i]))*int2float(1-x_eq_d[i]) + ); + +%-----------------------------------------------------------------------------% +% Domain constraints + +% XXX only for a fixed set + +predicate set_in(var int: x, set of int: s) = + if s = min(s)..max(s) then + min(s) <= x /\ x <= max(s) + else + exists(e in s)( x = e ) + endif; + +% XXX only for a fixed set +predicate set_in_reif(var int: x, set of int: s, var bool: b) = + b <-> + exists(i in 1..length([ 0 | e in s where not (e - 1 in s) ]))( + let { int: l = [ e | e in s where not (e - 1 in s) ][i], + int: r = [ e | e in s where not (e + 1 in s) ][i] } + in + l <= x /\ x <= r + ); + + % Alternative +predicate alt_set_in_reif(var int: x, set of int: s, var bool: b) = + b <-> + if s = min(s)..max(s) then + min(s) <= x /\ x <= max(s) + else + exists(e in s)( x = e ) + endif; + +%-----------------------------------------------------------------------------% +% Auxiliary: equality reified onto a 0/1 variable + +predicate aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q_458 } + in + aux_int_lt_if_0(x - p, y, q_458[1]) /\ + aux_int_gt_if_0(x + p, y, q_458[2]) /\ + sum(q_458) <= 2 - 2*p /\ + sum(q_458) <= 1 + p; + + % Alternative 1 +predicate alt_1_aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q } + in + aux_int_lt_if_0(x - p, y, q[1]) /\ + aux_int_gt_if_0(x + p, y, q[2]) /\ + q[1] <= 1 - p /\ + q[2] <= 1 - p /\ + sum(q) <= 1 + p; + + % Alternative 2 +predicate alt_2_aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) /\ + aux_int_lt_if_0(x, y, q[1]) /\ + aux_int_gt_if_0(x, y, q[2]) /\ + sum(q) <= p + 1; + + +predicate aux_float_eq_iff_1(var float: x, var float: y, var float: p) = + let { array[1..2] of var 0..1: q } + in + aux_float_le_if_1(x, y, p) /\ + aux_float_ge_if_1(x, y, p) /\ + aux_float_lt_if_0(x, y, int2float(q[1])) /\ + aux_float_gt_if_0(x, y, int2float(q[2])) /\ + int2float(sum(q)) <= 1.0 + p; + +%-----------------------------------------------------------------------------% +% Auxiliary: indicator constraints +% p -> x # 0 where p is a 0/1 variable and # is a comparison + +% Base cases + +predicate aux_int_le_zero_if_0(var int: x, var int: p) = + x <= ub(x) * p; + +predicate aux_float_le_zero_if_0(var float: x, var float: p) = + x <= ub(x) * p; + +predicate aux_float_lt_zero_if_0(var float: x, var float: p) = + let { float: rho = 1e-02 * abs(ub(x)) } % same order of magnitude as ub(x) + in + x < (ub(x) + rho) * p; + + +% Derived cases + +predicate aux_int_le_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, p); + +predicate aux_int_ge_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, p); + +predicate aux_int_le_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, 1 - p); + +predicate aux_int_ge_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, 1 - p); + +predicate aux_int_lt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, p); + +predicate aux_int_gt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x + 1, p); + +predicate aux_int_lt_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, 1 - p); + + +predicate aux_float_le_if_0(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(x - y, p); + +predicate aux_float_ge_if_0(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(y - x, p); + +predicate aux_float_le_if_1(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(x - y, 1.0 - p); + +predicate aux_float_ge_if_1(var float: x, var float: y, var float: p) = + aux_float_le_zero_if_0(y - x, 1.0 - p); + +predicate aux_float_lt_if_0(var float: x, var float: y, var float: p) = + aux_float_lt_zero_if_0(x - y, p); + +predicate aux_float_gt_if_0(var float: x, var float: y, var float: p) = + aux_float_lt_zero_if_0(y - x, p); + +predicate aux_float_lt_if_1(var float: x, var float: y, var float: p) = + aux_float_lt_zero_if_0(x - y, 1.0 - p); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% + +annotation bool_search(array[int] of var bool: x, ann:a1, ann:a2, ann:a3) = + int_search([bool2int(x[i]) | i in index_set(x)],a1,a2,a3); + +predicate array_int_maximum(var int: m, array[int] of var int: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: ly = lb_array(x), + int: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +predicate array_float_maximum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +predicate array_int_minimum(var int: m, array[int] of var int: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: ly = lb_array(x), + int: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); + +predicate array_float_minimum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); + +mzn_opt_only_range_domains = true; diff --git a/share/minizinc/linear_old/table_int.mzn b/share/minizinc/linear_old/table_int.mzn new file mode 100644 index 0000000..7b49507 --- /dev/null +++ b/share/minizinc/linear_old/table_int.mzn @@ -0,0 +1,21 @@ +%-----------------------------------------------------------------------------% +% A 'table' constraint table(x, T) represents the constraint x in T where we +% consider each row in T to be a tuple and T as a set of tuples. +% +% Linear version. +% +% See also the equality encoding of the 'element' constraint. +%-----------------------------------------------------------------------------% + +predicate table_int(array[int] of var int: x, array[int, int] of int: t) = + assert(index_set_2of2(t) = index_set(x), + "The second dimension of the table must equal the number of " ++ + "variables in the first argument", + let { set of int: it = index_set_1of2(t), + array[it] of var 0..1: lambda } + in + sum(lambda) = 1 + /\ + forall(j in index_set(x))( sum(i in it)( t[i,j]*lambda[i] ) = x[j] ) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_scip/README__SCIP.txt b/share/minizinc/linear_scip/README__SCIP.txt new file mode 100644 index 0000000..8a2e016 --- /dev/null +++ b/share/minizinc/linear_scip/README__SCIP.txt @@ -0,0 +1 @@ +2019-05-06 Currently need to copy the whole linear directory to specialize it for SCIP. diff --git a/share/minizinc/linear_scip/domain_encodings.mzn b/share/minizinc/linear_scip/domain_encodings.mzn new file mode 100644 index 0000000..f2694f7 --- /dev/null +++ b/share/minizinc/linear_scip/domain_encodings.mzn @@ -0,0 +1,111 @@ +/*%-----------------------------------------------------------------------------% +% Domain encodings +%-----------------------------------------------------------------------------% +*/ +% Linear equality encoding + + % Single variable: x = d <-> x_eq_d[d] +predicate equality_encoding(var int: x, array[int] of var int: x_eq_d) = + x in index_set(x_eq_d) + /\ + sum(d in dom(x))( x_eq_d[d] ) = 1 + /\ + sum(d in dom(x))( d * x_eq_d[d] ) = x + /\ +% my_trace( "eq_enc: \(x), index_set(pp)=" ++ show(index_set( x_eq_d )) ++ "\n" ) /\ + if fPostprocessDomains then + equality_encoding__POST(x, x_eq_d) + else true endif + ; + + % Two variables: x = d /\ y = e <-> x_eq_d[d] /\ y_eq_e[e] /\ xy_eq_de[d, e] +predicate equality_encoding(var int: x, var int: y, + array[int] of var int: x_eq_d, + array[int] of var int: y_eq_e, + array[int, int] of var int: xy_eq_de + ) = + x in index_set(x_eq_d) /\ + y in index_set(y_eq_e) /\ + index_set(x_eq_d) == index_set_1of2(xy_eq_de) /\ + index_set(y_eq_e) == index_set_2of2(xy_eq_de) /\ + sum(d in dom(x), e in dom(y))( xy_eq_de[d, e] ) = 1 + /\ + forall(d in dom(x)) (sum(e in dom(y))( xy_eq_de[d, e] ) = x_eq_d[d]) + /\ + forall(e in dom(y)) (sum(d in dom(x))( xy_eq_de[d, e] ) = y_eq_e[e]) + ; + + % Array of variables: x[i] = d <-> x_eq_d[i,d] +predicate equality_encoding(array[int] of var int: x, + array[int, int] of var int: x_eq_d) = + forall(i in index_set(x))( + x[i] in index_set_2of2(x_eq_d) + /\ + sum(d in index_set_2of2(x_eq_d))( x_eq_d[i,d] ) = 1 + /\ + sum(d in index_set_2of2(x_eq_d))( d * x_eq_d[i,d] ) = x[i] + ); + +function var int: eq_new_var(var int: x, int: i) ::promise_total = + if i in dom(x) then + let { + var 0..1: xi; + } in xi + else 0 endif; + +function array[int] of var int: eq_encode(var int: x) ::promise_total = + let { + array[int] of var int: y = array1d(lb(x)..ub(x),[eq_new_var(x,i) | i in lb(x)..ub(x)]); + constraint equality_encoding(x,y); +% constraint +% if card(dom(x))>0 then +% my_trace(" eq_encode: dom(\(x)) = " ++ show(dom(x)) ++ ", card( dom(\(x)) ) = " ++ show(card(dom(x))) ++ "\n") +% else true endif; + %% constraint assert(card(dom(x))>1, " eq_encode: card(dom(\(x))) == " ++ show(card(dom(x)))); + } in y; + +function array[int] of int: eq_encode(int: x) ::promise_total = + array1d(lb(x)..ub(x),[ if i=x then 1 else 0 endif | i in lb(x)..ub(x)]); + +%%% The same for 2 variables: +function var int: eq_new_var(var int: x, int: i, var int: y, int: j) ::promise_total = + if i in dom(x) /\ j in dom(y) then + let { + var 0..1: xi; + } in xi + else 0 endif; + +function array[int, int] of var int: eq_encode(var int: x, var int: y) ::promise_total = + let { + array[int] of var int: pX = eq_encode(x), + array[int] of var int: pY = eq_encode(y), + array[int, int] of var int: pp = array2d(index_set(pX), index_set(pY), + [eq_new_var(x,i,y,j) | i in index_set(pX), j in index_set(pY)]); + constraint equality_encoding(x, y, pX, pY, pp); + } in pp; + +function array[int, int] of int: eq_encode(int: x, int: y) ::promise_total = +% let { +% constraint if card(dom(x))*card(dom(y))>200 then +% my_trace(" eq_encode: dom(\(x)) = " ++ show(dom(x)) ++ ", dom(\(y)) = " ++ show(dom(y)) ++ "\n") +% else true endif; +% } in + array2d(lb(x)..ub(x), lb(y)..ub(y), + [if i==x /\ j==y then 1 else 0 endif | i in lb(x)..ub(x), j in lb(y)..ub(y)]); + +function array[int,int] of var int: eq_encode(array[int] of var int: x) ::promise_total = + let { + array[index_set(x),lb_array(x)..ub_array(x)] of var int: y = + array2d(index_set(x),lb_array(x)..ub_array(x), + [ let { + array[int] of var int: xi = eq_encode(x[i]) + } in if j in index_set(xi) then xi[j] else 0 endif + | i in index_set(x), j in lb_array(x)..ub_array(x)] + ) + } in y; + +function array[int,int] of int: eq_encode(array[int] of int: x) ::promise_total = + array2d(index_set(x),lb_array(x)..ub_array(x),[ if j=x[i] then 1 else 0 endif | i in index_set(x), j in lb_array(x)..ub_array(x)]); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_scip/fzn_all_different_int.mzn b/share/minizinc/linear_scip/fzn_all_different_int.mzn new file mode 100644 index 0000000..58a425e --- /dev/null +++ b/share/minizinc/linear_scip/fzn_all_different_int.mzn @@ -0,0 +1,23 @@ +%-----------------------------------------------------------------------------% +% 'all_different' constrains an array of objects to be all different. +% +% Linear version: equality encoding; see e.g. [Refalo, CP 2000] +% +% For a given d in dom(x), at most one i with x_i = d can exist. +%-----------------------------------------------------------------------------% + +include "domain_encodings.mzn"; + +predicate fzn_all_different_int(array[int] of var int: x) = + if length(x)<=1 then true + else + let { + array[int,int] of var 0..1: x_eq_d = eq_encode(x) + } in ( +% my_trace(" all_different_int: x[" ++ show(index_set(x)) ++ "]\n") /\ + forall(d in index_set_2of2(x_eq_d))( sum(i in index_set_1of2(x_eq_d))( x_eq_d[i,d] ) <= 1 ) + ) + endif; + + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_scip/fzn_alldifferent_except_0.mzn b/share/minizinc/linear_scip/fzn_alldifferent_except_0.mzn new file mode 100644 index 0000000..d6b07df --- /dev/null +++ b/share/minizinc/linear_scip/fzn_alldifferent_except_0.mzn @@ -0,0 +1,17 @@ +/** @group globals.alldifferent + Constrain the array of integers \a vs to be all different except those + elements that are assigned the value 0. +*/ +predicate fzn_alldifferent_except_0(array[int] of var int: vs) = +% forall(i, j in index_set(vs) where i < j) ( +% (vs[i] != 0 /\ vs[j] != 0) -> vs[i] != vs[j] +% ); + if length(vs)<=1 then true + else + let { + array[int,int] of var 0..1: x_eq_d = eq_encode(vs) + } in ( +% my_trace(" alldifferent_except_0: x[" ++ show(index_set(vs)) ++ "]\n") /\ + forall(d in index_set_2of2(x_eq_d) diff {0})( sum(i in index_set_1of2(x_eq_d))( x_eq_d[i,d] ) <= 1 ) + ) + endif; diff --git a/share/minizinc/linear_scip/fzn_circuit.mzn b/share/minizinc/linear_scip/fzn_circuit.mzn new file mode 100644 index 0000000..d409109 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_circuit.mzn @@ -0,0 +1,43 @@ +include "alldifferent.mzn"; + +/** @group globals + Constrains the elements of \a x to define a circuit where \a x[\p i] = \p j means + that \p j is the successor of \p i. +*/ +% Linear version. +predicate fzn_circuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + constraint forall( i in S )( x[i] in S diff {i} ), %% Self-mapping and exclude i->i before alldifferent + } in + alldifferent(x) /\ +% alldifferent(order) /\ + if nMZN__fSECcuts>0 then + let { + array [int, int] of var int: eq_x = eq_encode( x ), + constraint assert( l==min( index_set_2of2( eq_x ) ), "circuit: index set mismatch" ), %% self-mapping + constraint assert( u==max( index_set_2of2( eq_x ) ), "circuit: index set mismatch" ), + } in + circuit__SECcuts( eq_x ) + else true endif /\ + if nMZN__fSECcuts<2 then + %%% MTZ model. Note that INTEGER order vars seem better!: + let { + array[l+1..l+n-1] of var 2..n: order, + } in + forall (i,j in l+1..l+n-1 where i!=j /\ j in dom(x[i])) ( + order[i] - order[j] + (n-1)* bool2int(x[i]==j) + + (n-3)*bool2int(x[j]==i) %% the Desrochers & Laporte '91 term + %%%% --- strangely enough it is much worse on vrp-s2-v2-c7_vrp-v2-c7_det_ADAPT_1_INVERSE.mzn! + <= n-2 ) + else true endif + %% ... but seems improved with this (leaving also for SEC) + /\ if n>2 then forall (i,j in S where i= 0 and \a r[\p i] >= 0 + + Linear version. +*/ + +%% A global cumulative with SCIP +predicate fzn_cumulative(array[int] of var int: s, + array[int] of int: d, + array[int] of int: r, int: b); + + +predicate fzn_cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + if 0==length(s) then true else + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + if mzn_in_redundant_constraint() /\ fMZN__IgnoreRedundantCumulative then + true + else + let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = dom_array( [ s[i] | i in tasks ] ) + } in + if 0==card(tasks) then /*true*/ 0==card(index_set(s)) \/ b>=0 + else + if nMZN__UnarySizeMax_cumul>=card(times)*card(tasks) then %%% -- Mem overflow on rcmsp. PARAMETER? TODO + cumulative_time_decomp(s, d, r, b, times) + else + cumulative_task_decomp(s, d, r, b) + endif + endif + endif + ) endif ); + +%% Can be called with a given set of times: +predicate cumulative_set_times(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b, + set of int: TIMES01) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = dom_array( [ s[i] | i in tasks ] ) + intersect TIMES01 + } in + if false then %%% 100>=card(times) then %% PARAMETER ? + cumulative_time_decomp(s, d, r, b, times) + else + cumulative_task_decomp(s, d, r, b) + endif )); + +predicate cumulative_time_decomp(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b, + set of int: TIMES01) = +let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = + { i | i in min([ lb(s[i]) | i in tasks ]) .. + max([ ub(s[i]) + ub(d[i]) | i in tasks ]) where i in TIMES01 } +} +in +% my_trace(" cumul_time: " ++ show(card(tasks)) ++ " tasks, " ++ show(card(times)) ++ " times\n") /\ + forall( t in times ) ( + b >= sum( i in tasks ) ( + if is_fixed(d[i]) then + bool2int( s[i] in t-fix(d[i])+1..t ) + else + bool2int( s[i] <= t /\ t < s[i] + d[i] ) + endif + * r[i] + ) + ); + +predicate cumulative_task_decomp(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, + var int: b) = +let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 } +} +in +% my_trace(" cumul_tasks: " ++ show(card(tasks)) ++ " tasks\n") /\ + forall( j in tasks) ( + b-r[j] >= sum( i in tasks where i != j + /\ lb(s[i])<=ub(s[j]) /\ lb(s[j])= c[i]) /\ + sum(d)=1 /\ + y = sum (i in index_set(c)) ( d[i]*x[i] ); diff --git a/share/minizinc/linear_scip/fzn_if_then_else_int.mzn b/share/minizinc/linear_scip/fzn_if_then_else_int.mzn new file mode 100644 index 0000000..10b1293 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_if_then_else_int.mzn @@ -0,0 +1,7 @@ +predicate fzn_if_then_else_int(array[int] of var bool: c, array[int] of int: x, var int: y) = + let { + array[index_set(c)] of var 0..1: d; + } in + forall (i in index_set(c)) (sum(j in 1..i-1)(c[j])+d[i] >= c[i]) /\ + sum(d)=1 /\ + y = sum (i in index_set(c)) ( d[i]*x[i] ); diff --git a/share/minizinc/linear_scip/fzn_inverse.mzn b/share/minizinc/linear_scip/fzn_inverse.mzn new file mode 100644 index 0000000..51d81b9 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_inverse.mzn @@ -0,0 +1,27 @@ +%-----------------------------------------------------------------------------% +% Constrains two arrays of int variables to represent inverse functions. +% All the values in each array must be within the index set of the other array. +% +% Linear version. +%-----------------------------------------------------------------------------% + +% include "domain_encodings.mzn"; + +% predicate inverse(array[int] of var int: f, array[int] of var int: g) = +% let { +% array[int,int] of var 0..1: map_f = eq_encode(f); +% array[int,int] of var 0..1: map_g = eq_encode(g); +% } in forall (i in index_set(f), j in index_set(g)) (map_f[i,j] = map_g[j,i]); + +%-----------------------------------------------------------------------------% + + +%% Now copying the std version, seems a little faster on vrp-s2-v2-c7_vrp-v2-c7_det_ADAPT_1_INVERSE.mzn + +predicate fzn_inverse(array[int] of var int: f, + array[int] of var int: invf) = + forall(i in index_set(f)) ( f[i] in index_set(invf) ) /\ + forall(j in index_set(invf)) ( invf[j] in index_set(f) ) /\ + forall(i in index_set(f), j in index_set(invf)) ( + ((j == f[i]) <-> (i == invf[j])) + ); diff --git a/share/minizinc/linear_scip/fzn_lex_less_bool_reif.mzn b/share/minizinc/linear_scip/fzn_lex_less_bool_reif.mzn new file mode 100644 index 0000000..ca137f1 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_lex_less_bool_reif.mzn @@ -0,0 +1,21 @@ +include "../std/fzn_lex_less_bool_reif.mzn"; + +predicate fzn_lex_less_bool_imp(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + (c -> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); diff --git a/share/minizinc/linear_scip/fzn_lex_lesseq_bool.mzn b/share/minizinc/linear_scip/fzn_lex_lesseq_bool.mzn new file mode 100644 index 0000000..16af74a --- /dev/null +++ b/share/minizinc/linear_scip/fzn_lex_lesseq_bool.mzn @@ -0,0 +1,49 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_bool(array[int] of var bool: x, + array[int] of var bool: y) = +% if (min(card(index_set(x)), card(index_set(y))) <= 25) then +% let { int: size = min(card(index_set(x)), card(index_set(y))); +% } in +% sum(i in 0..size-1)(pow(2, (size-1-i)) * bool2int(x[i+min(index_set(x))])) +% <= sum(i in 0..size-1)(pow(2, (size-1-i)) * bool2int(y[i+min(index_set(y))])) +% else +% my_trace ("lex_lesseq_bool(\(x), \(y))") /\ + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = min(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + b[0] + /\ + forall(i in 0..size) ( + b[i] -> ( ( ( x[lx + i] <= y[ly + i] ) ) /\ + % bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) <= 2 /\ + +% ( b[i] -> +( x[lx + i] < y[ly + i] \/ b[i+1] ) ) +% /\ ( bool2int(b[i]) <= bool2int(x[lx + i] < y[ly + i]) + bool2int(b[i+1]) ) /\ + % bool2int(b[i]) + (1-bool2int(x[lx + i])) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + % /\ bool2int(b[i]) + bool2int(x[lx + i]) + bool2int(y[ly + i]) + (1-bool2int(b[i+1])) <= 3 + %% This guy is dominated by the 1st one above but helps: + % /\ bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + ) + /\ b[size+1] = (ux-lx <= uy-ly) +% endif +; + +% forall(i in 0..size) ( +% ( b[i] == ( x[lx + i] <= y[ly + i] ) ) +% /\ +% if i < size then +% ( b[i] == ( x[lx + i] < y[ly + i] \/ b[i+1] +% ) ) else true endif +% ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_scip/fzn_lex_lesseq_bool_reif.mzn b/share/minizinc/linear_scip/fzn_lex_lesseq_bool_reif.mzn new file mode 100644 index 0000000..a4a63f5 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_lex_lesseq_bool_reif.mzn @@ -0,0 +1,50 @@ +predicate fzn_lex_lesseq_bool_reif(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + ( b[i] -> ( x[lx + i] <= y[ly + i] ) ) /\ + bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) <= 2 + /\ + ( b[i] -> ( x[lx + i] < y[ly + i] \/ b[i+1] ) ) /\ + bool2int(b[i]) + (1-bool2int(x[lx + i])) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + bool2int(y[ly + i]) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + ) + /\ b[size+1] = (ux-lx <= uy-ly) +% endif +; + +predicate fzn_lex_lesseq_bool_imp(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c -> b[0]) + /\ + forall(i in 0..size) ( + ( b[i] -> ( x[lx + i] <= y[ly + i] ) ) /\ + bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) <= 2 + /\ + ( b[i] -> ( x[lx + i] < y[ly + i] \/ b[i+1] ) ) /\ + bool2int(b[i]) + (1-bool2int(x[lx + i])) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + bool2int(y[ly + i]) + (1-bool2int(b[i+1])) <= 3 + /\ bool2int(b[i]) + bool2int(x[lx + i]) + (1-bool2int(y[ly + i])) + (1-bool2int(b[i+1])) <= 3 + ) + /\ b[size+1] = (ux-lx <= uy-ly) +; diff --git a/share/minizinc/linear_scip/fzn_regular.mzn b/share/minizinc/linear_scip/fzn_regular.mzn new file mode 100644 index 0000000..02bedf9 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_regular.mzn @@ -0,0 +1,99 @@ +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the range 1..\a S) + is accepted by the DFA of \a Q states with input 1..\a S and transition + function \a d (which maps (1..\a Q, 1..\a S) -> 0..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). We reserve state 0 to be an always failing state. +*/ +predicate fzn_regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F) = +% my_trace(" regular: index_set(x)=" ++ show(index_set(x)) +% ++ ", dom_array(x)=" ++ show(dom_array(x)) +% ++ ", dom_array(a)=" ++ show(1..Q) +% ++ "\n") /\ + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)), + int: n = max(index_set(x)) + 1, + array[m..n] of var 1..Q: a, + constraint a[m] = q0 /\ % Set a[0]. + a[n] in F, % Check the final state is in F. + constraint + forall(i in index_set(x)) ( + x[i] in 1..S % Do this in case it's a var. + /\ %% trying to eliminate non-reachable states: + let { + set of int: va_R = { d[va, vx] | va in dom(a[i]), vx in dom(x[i]) } diff { 0 } %% Bug in MZN 2.0.4 + } in + a[i+1] in va_R + ) + } in + let { + constraint + forall(i in [n-i | i in 1..length(x)]) ( + a[i] in { va | va in dom(a[i]) + where exists(vx in dom(x[i]))(d[va, vx] in dom(a[i+1])) } + /\ + x[i] in { vx | vx in dom(x[i]) + where exists(va in dom(a[i]))(d[va, vx] in dom(a[i+1])) } + ) + } in + forall(i in index_set(x)) ( + let { + set of int: va_R = { d[va, vx] | va in dom(a[i]), vx in dom(x[i]) } diff { 0 } %% Bug in MZN 2.0.4 + } in +% my_trace(" S" ++ show(i) +% ++ ": dom(a[i])=" ++ show(dom(a[i])) +% ++ ", va_R="++show(va_R) +% ++ ", index_set_2of2(eq_a) diff va_R=" ++ show(index_set_2of2(eq_a) diff va_R) +% ++ ", dom(a[i+1])=" ++ show(dom(a[i+1])) +% ) /\ + a[i+1] in va_R + %/\ a[i+1] in min(va_R)..max(va_R) + ) +% /\ my_trace(" regular -- domains after prop: index_set(x)=" ++ show(index_set(x)) +% ++ ", dom_array(x)=" ++ show(dom_array(x)) +% ++ ", dom_array(a)=" ++ show(dom_array(a)) +% ++ "\n") +% /\ my_trace("\n") + /\ + let { + array[int, int] of var int: eq_a=eq_encode(a), + array[int, int] of var int: eq_x=eq_encode(x), + } in + forall(i in index_set(x)) ( + % a[i+1] = d[a[i], x[i]] % Determine a[i+1]. + if card(dom(a[i]))*card(dom(x[i])) > nMZN__UnarySizeMax_1step_regular then + %% Implication decomposition: + forall(va in dom(a[i]), vx in dom(x[i]))( + if d[va, vx] in dom(a[i+1]) then + eq_a[i+1, d[va, vx]] >= eq_a[i, va] + eq_x[i, vx] - 1 %% The only-if part of conj + else + 1 >= eq_a[i, va] + eq_x[i, vx] + endif + ) + else + %% Network-flow decomposition: + %% {regularIP07} M.-C. C{\^o}t{\'e}, B.~Gendron, and L.-M. Rousseau. + %% \newblock Modeling the regular constraint with integer programming. + let { + % array[int, int] of set of int: VX_a12 = %% set of x for given a1 that produce a2 + % array2d(1..S, 1..Q, [ { vx | vx in 1..S where d[va1, vx]==va2 } | va1 in dom(a[i]), va2 in dom(a[i+1]) ]); + array[int, int] of var int: ppAX = eq_encode(a[i], x[i]); + } in + forall (va2 in dom(a[i+1])) ( + eq_a[i+1, va2] = sum(va1 in dom(a[i]), vx in dom(x[i]) where d[va1, vx]==va2) + (ppAX[va1, vx]) + ) + /\ + forall(va1 in dom(a[i]), vx in dom(x[i]))( + if not (d[va1, vx] in dom(a[i+1])) then + ppAX[va1, vx] == 0 + else + true + endif + ) + endif + ); diff --git a/share/minizinc/linear_scip/fzn_sliding_sum.mzn b/share/minizinc/linear_scip/fzn_sliding_sum.mzn new file mode 100644 index 0000000..894f33e --- /dev/null +++ b/share/minizinc/linear_scip/fzn_sliding_sum.mzn @@ -0,0 +1,14 @@ +/** @group globals + Requires that in each subsequence \a vs[\p i], ..., \a vs[\p i + \a seq - 1] the sum of the + values belongs to the interval [\a low, \a up]. +*/ +predicate fzn_sliding_sum(int: low, int: up, int: seq, array[int] of var int: vs) = + let { int: lx = min(index_set(vs)), + int: ux = max(index_set(vs)), + } in + forall (i in lx .. ux - seq + 1) ( + let { + var int: sum_of_l = sum(j in i..i + seq - 1) (vs[j]) + } in + low <= sum_of_l /\ sum_of_l <= up + ); diff --git a/share/minizinc/linear_scip/fzn_subcircuit.mzn b/share/minizinc/linear_scip/fzn_subcircuit.mzn new file mode 100644 index 0000000..6bb40e7 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_subcircuit.mzn @@ -0,0 +1,68 @@ +include "alldifferent.mzn"; + +/** @group globals + Constrains the elements of \a x to define a subcircuit where \a x[\p i] = \p j + means that \p j is the successor of \p i and \a x[\p i] = \p i means that \p i + is not in the circuit. +*/ +%% Linear version +predicate fzn_subcircuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + array[S] of var 1..n: order, + array[S] of var bool: ins = array1d(S,[ x[i] != i | i in S]), + var l..u+1: firstin = min([ u+1 + bool2int(ins[i])*(i-u-1) | i in S]), %% ... + var S: lastin, + var bool: empty = (firstin == u+1), + } in + alldifferent(x) /\ + % NO alldifferent(order) /\ + + % If the subcircuit is empty then each node points at itself. + % + (empty <-> forall(i in S)(not ins[i])) /\ + + % If the subcircuit is non-empty then order numbers the subcircuit. + % + ((not empty) <-> + + %% Another way to express minimum. +% forall(i in l..u+1)( +% i==firstin <-> ins[i] +% /\ forall(j in S where j firstin /\ + + % The lastin node points at firstin. + x[lastin] = firstin /\ + + % And both are in + ins[lastin] /\ ins[firstin] /\ + + % The successor of each node except where it is firstin is + % numbered one more than the predecessor. +% forall(i in S) ( +% (ins[i] /\ x[i] != firstin) -> order[x[i]] = order[i] + 1 +% ) /\ + %%% MTZ model. Note that INTEGER order vars seem better!: + forall (i,j in S where i!=j) ( + order[i] - order[j] + n*bool2int( x[i]==j /\ i!=lastin ) + % + (n-2)*bool2int(x[j]==i) %% the Desrochers & Laporte '91 term + <= n-1 ) /\ + + % Each node that is not in is numbered after the lastin node. + forall(i in S) ( + true +% (not ins[i]) <-> (n == order[i]) + ) + + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_scip/fzn_table_int.mzn b/share/minizinc/linear_scip/fzn_table_int.mzn new file mode 100644 index 0000000..30cfc65 --- /dev/null +++ b/share/minizinc/linear_scip/fzn_table_int.mzn @@ -0,0 +1,18 @@ +%-----------------------------------------------------------------------------% +% A 'table' constraint table(x, T) represents the constraint x in T where we +% consider each row in T to be a tuple and T as a set of tuples. +% +% Linear version. +% +% See also the equality encoding of the 'element' constraint. +%-----------------------------------------------------------------------------% + +predicate fzn_table_int(array[int] of var int: x, array[int, int] of int: t) = + let { set of int: it = index_set_1of2(t), + array[it] of var 0..1: lambda } + in + sum(lambda) = 1 + /\ + forall(j in index_set(x))( sum(i in it)( t[i,j]*lambda[i] ) = x[j] ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/linear_scip/options.mzn b/share/minizinc/linear_scip/options.mzn new file mode 100644 index 0000000..1a8802d --- /dev/null +++ b/share/minizinc/linear_scip/options.mzn @@ -0,0 +1,146 @@ +/* +% Controls +% +*/ + +%-----------------------------------------------------------------------------% +%---------- USER and LAZY CUTS -----------------------------------------------% +/* + PLEASE NOTE: + If you export FZN file with lazy_constraint/user_cut annotations, + their declarations are not exported currently (as of 7.11.17). + WORKAROUND: when solving that fzn, add -G linear, + e.g., as follows: mzn-cplex -G linear model.fzn + * For Gurobi, the constraints marked as MIP_cut and/or MIP_lazy are added + * into the overall model and marked with the foll values of Lazy attribute: + * ::MIP_lazy 1 + * ::MIP_cut ::MIP_lazy 2 + * ::MIP_cut 3 + */ +ann: user_cut; +ann: lazy_constraint; +%%% comment away the below assignments (leaving, e.g., ann: MIP_cut;) to have them as normal constraints +%%% In particular, they may be used by redundant_constraint() and symmetry_breaking_constraint(), see redefs-2.0.2.mzn +ann: MIP_cut = user_cut; %% MIP_cut: make sure no feasible solutions are cut off + %% -- seems better on average but in CPLEX, wrong LB e.g. on carpet-cutting +ann: MIP_lazy = lazy_constraint; + +%-----------------------------------------------------------------------------% +opt bool: fIndConstr; +bool: fMZN__UseIndicators = %% Pass on indicator constraints + if absent( fIndConstr ) then false + else deopt( fIndConstr ) endif; + %% CPLEX 12.6.2 Concert: reifs give wrong result on 2012/amaze, so using implications only + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Subtour elimination in circuit %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +opt int: nSECcuts; %% 0,1: use MTZ formulation +int: nMZN__fSECcuts = %% 1,2: pass on circuit constraints to the MIP_solverinstance's cut gen + if absent( nSECcuts ) then 0 + else deopt( nSECcuts ) endif; + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MIPdomains %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +%% Paper: % Belov, Stuckey, Tack, Wallace. Improved Linearization of Constraint Programming Models. CP 2016 Proceedings. + %%% The below option enables translation of domain constraints into the ...POST predicates. + %%% The code in MIPdomains.cpp processes them and also non-contiguous domains + %%% (only-range-domains is then standardly off). MIPdomains.cpp needs all the required + %%% __POST predicates to be declared to kick in. +opt bool: fMIPDomains; %% unified decomposition constraints (...__POST) to FlatZinc +opt bool: fMIPdomains; %% Can be defined from cmdline: -D "fMIPdomains=false" +bool: fPostprocessDomains = %% True to pass all domain-related + if absent( fMIPdomains ) /\ absent( fMIPDomains ) then false %% seems best for all 28.5.19 + elseif not absent( fMIPdomains ) then deopt( fMIPdomains ) + else deopt( fMIPDomains ) + endif; +opt bool: fMIPdomAux; +bool: fPostproDom_AUX = %% Specialized for aux_ constr + if absent( fMIPdomAux ) then false + else deopt( fMIPdomAux ) endif; +opt bool: fMIPdomDiff; +bool: fPostproDom_DIFF = %% Specialized for differences: x z=x-y<0 + if absent( fMIPdomDiff ) then false %% seems best for Gurobi, worse for CBC + else deopt( fMIPdomDiff ) endif; + +mzn_opt_only_range_domains = not fPostprocessDomains; %% currently unused + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Avoid creating new int vars %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% --------------------------------------------------------------------------------------- % +opt bool: fAvoidNewInts; +bool: fAvoidNI = %% Actually this is only for ..._lin_..., not for just x-y + if absent( fAvoidNewInts ) then false + else deopt( fAvoidNewInts ) endif; +opt bool: fNewVarsInAuxEq; +bool: fAuxIntEqOLD00 = if absent(fNewVarsInAuxEq) then false else deopt(fNewVarsInAuxEq) endif; +bool: fAuxFloatEqOLD00 = if absent(fNewVarsInAuxEq) then false else deopt(fNewVarsInAuxEq) endif; + +%%%%%%%%%%%%%%%%%%%%% Redundant constraints ---------------------------------------------- % +bool: fMZN__IgnoreRedundantCumulative=false; +%% NOT WORKING NOW, use redefs_2.0.2.mzn: +%%%%% bool: fMZN__IgnoreAllUserRedundant=false; %% ignore all user-spec redundant constr + +%%%%%%%%%%%%%%%%%%%%% Element, minimuum convex hull --------------------------------------- % +opt bool: fXBZCuts01; %% orders 0, 1 +opt bool: fXBZCutGen; %% only works if Cuts01 +bool: fElementCutsXZ=false; %% Use simple XZ & XZB cuts for element +bool: fElementCutsXZB = if absent(fXBZCuts01) then false else deopt(fXBZCuts01) endif; +bool: fMinimumCutsXZ=false; %% Use simple XZ & XZB cuts for minimum +bool: fMinimumCutsXZB = if absent(fXBZCuts01) then false else deopt(fXBZCuts01) endif; +bool: fUseXBZCutGen = if absent(fXBZCutGen) then false else deopt(fXBZCutGen) endif; + +% ----------------------------------------------------------------------------------------- % +bool: fIntTimesBool=true; %% Special handling of multiplication with a boolean(*const) + +%-----------------------------------------------------------------------------% +% If not postprocessing domains: For unary encoding: maximal domain length to invoke it + +int: nMZN__UnarySizeMax_intTimes=20; +int: nMZN__UnarySizeMax_cumul=2000; +int: nMZN__UnarySizeMax_1step_regular=20000; %% network-flow decomp in the regular constraint + +int: nMZN__UnaryLenMin__ALL=1; %% can be used by the indiv. cases +int: nMZN__UnaryLenMax__ALL=2000; %% can be used by the indiv. cases +% Some more detailed parameters +int: nMZN__UnaryLenMin_leq = 1; +int: nMZN__UnaryLenMin_neq = nMZN__UnaryLenMin__ALL; +int: nMZN__UnaryLenMin_eq = nMZN__UnaryLenMin__ALL; +int: nMZN__UnaryLenMax_leq = -1; +int: nMZN__UnaryLenMax_neq = nMZN__UnaryLenMax__ALL; +int: nMZN__UnaryLenMax_eq = nMZN__UnaryLenMax__ALL; +int: nMZN__UnaryLenMax_setIn = nMZN__UnaryLenMax__ALL; +int: nMZN__UnaryLenMax_setInReif = nMZN__UnaryLenMax__ALL; + +%-----------------------------------------------------------------------------% +% Strict inequality +% The relative epsilon +%%% Has the problem that when relating to upper bound of various differences, +%%% getting different absolute eps...? +%% float: float_lt_EPS_coef__ = 1e-03; ABANDONED 12.4.18 due to #207 +%%% Absolute one, used everywhere +%%% Might make no sense for floats with smaller domains etc. +opt float: float_EPS; +float: float_lt_EPS = if absent( float_EPS ) then 1e-6 else deopt( float_EPS ) endif; + +%-----------------------------------------------------------------------------% +%%% Set =true to PRINT TRACING messages for some constraints: +opt bool: fMIPTrace; +bool: mzn__my_trace_on = + if absent( fMIPTrace ) then false else deopt( fMIPTrace ) endif; +test my_trace(string: msg) ::promise_total + = if mzn__my_trace_on then trace(msg) + else true endif; +test my_trace(string: msg, bool: bb) ::promise_total + = if mzn__my_trace_on then trace(msg, bb) + else bb endif; +function var bool: my_trace(string: msg, var bool: bb) ::promise_total + = if mzn__my_trace_on then trace(msg, bb) + else bb endif; +%%% Set =true to PRINT TRACING messages for the currently debugged constraints: +opt bool: fMIPTraceDBG; +bool: mzn__my_trace__DBG_on = + if absent( fMIPTraceDBG ) then false else deopt( fMIPTraceDBG ) endif; +test my_trace__DBG(string: msg) ::promise_total + = if mzn__my_trace__DBG_on then trace(msg) + else true endif; + diff --git a/share/minizinc/linear_scip/redefinitions-2.0.2.mzn b/share/minizinc/linear_scip/redefinitions-2.0.2.mzn new file mode 100644 index 0000000..5b0baf6 --- /dev/null +++ b/share/minizinc/linear_scip/redefinitions-2.0.2.mzn @@ -0,0 +1,29 @@ +% This file contains redefinitions of standard builtins for version 2.0.2 +% that can be overridden by solvers. + + +predicate symmetry_breaking_constraint(var bool: b) = + (b) %:: MIP_lazy %:: MIP_cut %% MIP_cut wrong in CPLEX 12.6.3 + %% Symm breaking as lazy is 1% better in Gurobi 6.5.2 on the Challenges 2012-2015 + %% But caused a bug in 7.5.1 - switched off + %% true %% TO omit all symmetry_breaking_constraint's + ; + +%% Make sure no feasible solutions are cut off: +predicate redundant_constraint(var bool: b) = + (b) %:: MIP_cut + % true %% To omit all redundant_constraint's + ; + +%% Linearized element: just call without shifting +predicate array_var_bool_element_nonshifted(var int: idx, array[int] of var bool: x, var bool: c) = + array_var_bool_element(idx,x,c); + +predicate array_var_int_element_nonshifted(var int: idx, array[int] of var int: x, var int: c) = + array_var_int_element(idx,x,c); + +predicate array_var_float_element_nonshifted(var int: idx, array[int] of var float: x, var float: c) = + array_var_float_element(idx,x,c); + +predicate array_var_set_element_nonshifted(var int: idx, array[int] of var set of int: x, var set of int: c) = + array_var_set_element(idx,x,c); diff --git a/share/minizinc/linear_scip/redefinitions-2.0.mzn b/share/minizinc/linear_scip/redefinitions-2.0.mzn new file mode 100644 index 0000000..9ef63b3 --- /dev/null +++ b/share/minizinc/linear_scip/redefinitions-2.0.mzn @@ -0,0 +1,43 @@ +predicate bool_clause_reif(array[int] of var bool: p, array[int] of var bool: n, var bool: c) = + c = ( sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1 + ); + + +predicate array_float_minimum(var float: m, array[int] of var float: x) = + if false then + array_float_minimum__IND(m, x) %% transfer to Concert because of prepro + else + array_float_minimum_I( m, [ x[i] | i in index_set(x)]) + endif; +predicate array_float_maximum(var float: m, array[int] of var float: x) = + if false then + array_float_maximum__IND(m, x) + else + array_float_minimum_I(-m, [-x[i] | i in index_set(x)]) + endif; +predicate array_int_minimum(var int: m, array[int] of var int: x) = + if false then + array_int_minimum__IND(m, x) + else + array_float_minimum_I( int2float(m), [ int2float(x[i]) | i in index_set(x)]) + endif; +predicate array_int_maximum(var int: m, array[int] of var int: x) = + if false then + array_int_maximum__IND(m, x) + else + array_float_minimum_I(-int2float(m), [-int2float(x[i]) | i in index_set(x)]) + endif; + +% predicate array_int_maximum__OLD_SYMMETRIES(var int: m, array[int] of var int: x) = +% let { int: l = min(index_set(x)), +% int: u = max(index_set(x)), +% int: ly = lb_array(x), +% int: uy = ub_array(x), +% array[l..u] of var ly..uy: y } in +% y[l] = x[l] /\ +% m = y[u] /\ +% forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + diff --git a/share/minizinc/linear_scip/redefinitions-2.2.1.mzn b/share/minizinc/linear_scip/redefinitions-2.2.1.mzn new file mode 100644 index 0000000..a9ed844 --- /dev/null +++ b/share/minizinc/linear_scip/redefinitions-2.2.1.mzn @@ -0,0 +1,7 @@ +% This file contains redefinitions of standard builtins for version 2.2.1 +% that can be overridden by solvers. + +/** @group flatzinc.int Constrains \a z = \(\a x ^ {\a y}\) */ +predicate int_pow_fixed(var int: x, int: y, var int: z) = + int_pow( x, y, z ); + diff --git a/share/minizinc/linear_scip/redefinitions.mzn b/share/minizinc/linear_scip/redefinitions.mzn new file mode 100644 index 0000000..e13c421 --- /dev/null +++ b/share/minizinc/linear_scip/redefinitions.mzn @@ -0,0 +1,617 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov (2015-) +% cf. Belov, Stuckey, Tack, Wallace. Improved Linearization of Constraint Programming Models. CP 2016. +*/ + +%----------------------------- BOOL2INT --------------------------------% +function var bool: reverse_map(var int: x) = (x==1); +function bool: reverse_map(int: x) = (x==1); + +predicate mzn_reverse_map_var(var bool: b) = let { + var int: x = bool2int(b) +} in true; + +function var int: bool2int(var bool: x) :: promise_total = + let { var 0..1: b2i; + constraint (x = reverse_map(b2i)) ::is_reverse_map ; + } in + b2i; + +predicate bool_eq(var bool: x, var bool: y) = +%% trace(" bool_eq: \(x), \(y) \n") /\ + bool2int(x)==bool2int(y); + +%---------------------------- BASIC (HALF)REIFS -----------------------------% + +include "options.mzn"; +include "redefs_bool_reifs.mzn"; +include "redefs_bool_imp.mzn"; + +include "domain_encodings.mzn"; +include "redefs_lin_reifs.mzn"; +include "redefs_lin_imp.mzn"; +include "redefs_lin_halfreifs.mzn"; + +include "nosets.mzn"; %% For set_le, set_lt ... Usind std/nosets + %% as long as the linearization is good. + +%-----------------------------------------------------------------------------% +% Strict inequality + +% Uncomment the following redefinition for FlatZinc MIP solver interfaces that +% do not support strict inequality. Note that it does not preserve equivalence +% (some solutions of the original problem may become invalid). + +predicate float_lt(var float: x, var float: y) = +% (x - y) <= (-float_lt_EPS_coef__)*max(abs(ub(x - y)), abs(ub(y-x))); + x <= y - float_lt_EPS; + +predicate float_lin_lt(array[int] of float: c, array[int] of var float: x, + float: d) = + float_lt(sum(i in index_set(x))( c[i]*x[i] ), d); + +%-----------------------------------------------------------------------------% +% Minimum, maximum, absolute value +% Use unary as well? TODO + +predicate int_abs(var int: x, var int: z) = +%% The simplifications seem worse on league.mzn model90-18-20.dzn: +%% but the .lp seem to differ just by order...?? TODO + if lb(x)>=0 then z==x + elseif ub(x)<=0 then z==-x + else + let { var bool: p } + in + z >= x /\ + z >= -x /\ + z >= 0 /\ % This is just for preprocessor + z <= max([ub(x), -lb(x)]) /\ % And this + % z <= x \/ z <= -x %% simple + aux_int_le_if_1(z, x, p) /\ %% even simpler + aux_int_le_if_0(z, -x, p) /\ + int_le_reif(0, x, p) % with reifs + %int_eq_reif(z, x, p) /\ + %int_eq_reif(z, -x, not p) + endif + ; + +predicate int_min(var int: x, var int: y, var int: z) = + array_int_minimum(z, [x, y]); + +predicate int_max(var int: x, var int: y, var int: z) = + array_int_maximum(z, [x, y]); + +predicate float_abs(var float: x, var float: z) = + if lb(x)>=0.0 then z==x + elseif ub(x)<=0.0 then z==-x + else + let { var bool: p } + in + z >= x /\ + z >= -x /\ + z >= 0.0 /\ % This is just for preprocessor + z <= max([ub(x), -lb(x)]) /\ % And this + % z <= x \/ z <= -x + aux_float_le_if_1(z, x, (p)) /\ + aux_float_le_if_0(z, -x, (p)) + % /\ + % float_le_reif(0.0, x, p) % with reifs - no point for floats? TODO + % float_eq_reif(z, x, p) /\ + % float_eq_reif(z, -x, not p) + endif; + +predicate float_min(var float: x, var float: y, var float: z) = + array_float_minimum(z, [x, y]); + +predicate float_max(var float: x, var float: y, var float: z) = + array_float_maximum(z, [x, y]); + +predicate array_float_minimum_I(var float: m, array[int] of var float: x) = + let { int: n = length(x), + constraint assert(1 == min(index_set(x)), " array_float_minimum_I: argument indexed not from 1??"), + int: iMinUB = arg_min([ub(x[i]) | i in 1..n]), + float: MinUB = ub(x[iMinUB]), + set of int: sLBLess = { i | i in 1..n where lb(x[i])0 then sLBLess + else sLBLess union { iMinUB } endif, + } in + if 1==card(sActive) then + m == x[min(sActive)] + else + let { + array[1..n] of var int: p = [ + if i in sActive then let { var 0..1: pi } in pi else 0 endif | i in 1..n ], + constraint 1==sum(p), + constraint m >= lb_array(x), + constraint m <= MinUB, + } in + forall (i in index_set(x)) + ( + if i in sActive %% for at least 1 element + then + m<=x[i] /\ aux_float_ge_if_1(m, x[i], p[i]) + else true endif ) + %% -- exclude too big x[i] + /\ + if card(sActive)>1 /\ fMinimumCutsXZ then + let { + array[int] of float: AL = [ lb(x[i]) | i in 1..n], + array[int] of int: srt = sort_by([i | i in 1..n], AL), + %indices of lb in sorted order + array[int] of float: AL_srt = [AL[srt[i]] | i in 1..n], + array[int] of float: AU_srt = [ub(x[srt[i]]) | i in 1..n], + array[int] of float: AM_srt = AL_srt ++ [MinUB] + %% -- these are z-levels of extreme points + } in + forall (i in 2..n+1 where + AM_srt[i]<=MinUB /\ %% this is a new "start level" + AM_srt[i]!=AM_srt[i-1] )( %% and would produce a new cut + m >= AM_srt[i] + - sum(j in 1..i-1 where AL_srt[j]1 /\ fMinimumCutsXZB then + array_var_float_element__XBZ_lb([ -x[i] | i in sActive ], [ p[i] | i in sActive ], -m) :: MIP_cut + else true endif + endif + ; + +%-----------------------------------------------------------------------------% +% Multiplication and division + +predicate int_div(var int: x, var int: y, var int: q) = + q == aux_int_division_modulo_fn(x,y)[1]; + + +predicate int_mod(var int: x, var int: y, var int: r) = + r == aux_int_division_modulo_fn(x,y)[2]; + + +function array[int] of var int: aux_int_division_modulo_fn(var int: x, var int: y) = + let { + %% Domain of q + set of int: dom_q = + if lb(y)*ub(y)>0 then + let { + set of int: EP = { ub(x) div ub(y), ub(x) div lb(y), lb(x) div ub(y), lb(x) div lb(y) }, + } in min(EP)..max(EP) + else + let { + int: mm = max( abs(lb(x)), abs(ub(x)) ), + } in -mm..mm %% TODO case when -1 or 1 not in dom(x) + endif, + var dom_q: q; + int: by = max(abs(lb(y)), abs(ub(y))); + var -by+1..by-1: r; + constraint x = y * q + r, + constraint 0 <= x -> 0 <= r, %% which is 0 > x \/ 0 <= r + constraint x < 0 -> r <= 0, %% which is x >= 0 \/ r <= 0 + % abs(r) < abs(y) + var 1.. max(abs(lb(y)), abs(ub(y))): w = abs(y), + constraint w > r /\ w > -r, + } in + [ q, r ]; + +%% Can also have int_times(var float, var int) ......... TODO + +predicate int_times(var int: x, var int: y, var int: z) = +if is_fixed(x) then + z==fix(x)*y %%%%% Need to use fix() otherwise added to map & nothing happens +elseif is_fixed(y) then + z==x*fix(y) + else + if 0..1==dom(x) /\ 0..1==dom(y) then bool_and__INT(x,y,z) + elseif card(dom(x))==2 /\ card(dom(y))==2 /\ 0 in dom(x) /\ 0 in dom(y) + then let { + var 0..1: xn; + var 0..1: yn; + var 0..1: zn; + constraint x=xn*max(dom(x) diff {0}); + constraint y=yn*max(dom(y) diff {0}); + constraint z=zn*max(dom(x) diff {0})*max(dom(y) diff {0}); + } in + bool_and__INT(xn,yn,zn) + elseif card(dom(x)) * card(dom(y)) > nMZN__UnarySizeMax_intTimes + \/ ( fIntTimesBool /\ ( + %% Peter's idea for *bool. More optimal but worse values on carpet cutting. + (card(dom(x))==2 /\ 0 in dom(x)) + \/ (card(dom(y))==2 /\ 0 in dom(y)) + ) ) + then %% PARAM + %% ALSO NO POINT IF <=4. TODO + if card(dom(x)) > card(dom(y)) \/ + ( card(dom(x))==card(dom(y)) /\ 0 in dom(y) /\ not (0 in dom(x)) ) + then int_times(y,x,z) + else + let { + set of int: s = lb(x)..ub(x), + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + array[s] of var min(r)..max(r): ady = array1d(s, [ d*y | d in s ]) } + in + ady[x] = z %% use element() + endif + else + int_times_unary(x, { }, y, z) + endif +endif; + +%% domx__ can be used to narrow domain... NOT IMPL. +predicate int_times_unary(var int: x, set of int: domx__, var int: y, var int: z) = + let { + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + %% set of int: domx = if card(domx__)>0 then domx__ else dom(x) endif, + array[int, int] of var int: pp=eq_encode(x, y) + } in + z>=min(r) /\ z<=max(r) /\ + z==sum(i in index_set_1of2(pp), j in index_set_2of2(pp)) + (i * j * pp[i, j]) /\ + forall(i in index_set_1of2(pp), j in index_set_2of2(pp) + where not ((i*j) in dom(z)) + )(pp[i, j]==0) + ; + + +predicate int_times_unary__NOFN(var int: x, set of int: domx__, var int: y, var int: z) = + let { + set of int: r = {lb(x)*lb(y), lb(x)*ub(y), ub(x)*lb(y), ub(x)*ub(y)}, + %% set of int: domx = if card(domx__)>0 then domx__ else dom(x) endif, + array[int] of var int: pX = eq_encode(x), + array[int] of var int: pY = eq_encode(y), + array[int] of int: valX = [ v | v in index_set(pX) ], %% NOT domx. + array[int] of int: valY = [ v | v in index_set(pY) ], %% -- according to eq_encode! + array[index_set(valX), index_set(valY)] of var 0..1: pp %% both dim 1.. + } in + if is_fixed(x) \/ is_fixed(y) then + z==x*y + else + z>=min(r) /\ z<=max(r) /\ + sum(pp)==1 /\ + z==sum(i in index_set(valX), j in index_set(valY)) + (valX[i] * valY[j] * pp[i, j]) /\ + forall(i in index_set(valX)) + ( pX[valX[i]] == sum(j in index_set(valY))( pp[i, j] ) ) /\ + forall(j in index_set(valY)) + ( pY[valY[j]] == sum(i in index_set(valX))( pp[i, j] ) ) + endif; + +predicate float_times(var float: a, var float: b, var float: c) = + abort("Unable to create linear formulation for the `float_times` constraint. This model instance cannot be solved using a linear solver."); + +%%%Define int_pow +predicate int_pow( var int: x, var int: y, var int: r ) = + let { + array[ int, int ] of int: x2y = array2d( lb(x)..ub(x), lb(y)..ub(y), + [ pow( X, Y ) | X in lb(x)..ub(x), Y in lb(y)..ub(y) ] ) + } in + r == x2y[ x, y ]; + +%%% Adding a version returning float for efficiency +/** @group builtins.arithmetic Return \(\a x ^ {\a y}\) */ +function var float: pow_float(var int: x, var int: y) = + let { + int: yy = if is_fixed(y) then fix(y) else -1 endif; + } in + if yy = 0 then 1 + elseif yy = 1 then x else + let { var float: r ::is_defined_var; + constraint int_pow_float(x,y,r) ::defines_var(r); + } in r + endif; +%%%Define int_pow_float +predicate int_pow_float( var int: x, var int: y, var float: r ) = + let { + array[ int, int ] of float: x2y = array2d( lb(x)..ub(x), lb(y)..ub(y), + [ pow( X, Y ) | X in lb(x)..ub(x), Y in lb(y)..ub(y) ] ) + } in + r == x2y[ x, y ]; + + + +%-----------------------------------------------------------------------------% +% Array 'element' constraints + +predicate array_bool_element(var int: x, array[int] of bool: a, var bool: z) = + array_int_element(x, arrayXd(a, [bool2int(a[i]) | i in index_set(a)]), bool2int(z)); + +predicate array_var_bool_element(var int: x, array[int] of var bool: a, + var bool: z) = + array_var_int_element(x, arrayXd(a, [bool2int(a[i]) | i in index_set(a)]), bool2int(z)); + +predicate array_int_element(var int: x, array[int] of int: a, var int: z) = + let { + constraint x in { i | i in index_set(a) where a[i] in dom(z) }, + } in + array_float_element(x, arrayXd(a, [int2float(a[i]) | i in index_set(a)]), int2float(z)); + +predicate array_var_int_element(var int: x, array[int] of var int: a, + var int: z) = + let { + constraint x in { i | i in index_set(a) where 0 < card(dom(a[i]) intersect dom(z)) }, + } in + %%%% Relate to the float version: + array_var_float_element(x, arrayXd(a, [int2float(a[i]) | i in index_set(a)]), int2float(z)); + %%%% Simplistic version: + %%%% Complete binarization: MEMORY FULL. Need exact domains & sparse encoding + + +predicate array_float_element(var int: i00, array[int] of float: a, + var float: z) = + let { set of int: ix = index_set(a), + constraint i00 in { i | i in ix where a[i]>=lb(z) /\ a[i]<=ub(z) }, + } in %%% Finish domain before dMin/dMax + let { + float: dMin = min(i in dom(i00))(a[i]), + float: dMax = max(i in dom(i00))(a[i]), + } in + if dMin==dMax then + z==dMin + else + z >= dMin /\ + z <= dMax /\ + let { + int: nUBi00 = max(dom(i00)), + int: nLBi00 = min(dom(i00)), + float: nMinDist = min(i in nLBi00 .. nUBi00-1)(a[i+1]-a[i]), + float: nMaxDist = max(i in nLBi00 .. nUBi00-1)(a[i+1]-a[i]), + } in + if nMinDist == nMaxDist then %% The linear case + z == a[nLBi00] + nMinDist*(i00-nLBi00) + else + let { + array[int] of var int: p = eq_encode(i00) %% this needs i00 in ix + } + in + assert(dom(i00) subset index_set(p), "", true) /\ + sum(i in dom(i00))( a[i] * int2float(p[i]) ) == z %% add more hull? + endif + endif; + +predicate array_var_float_element(var int: i00, array[int] of var float: a, + var float: z) = + let { set of int: ix = index_set(a), + constraint i00 in { i | i in ix where + lb(a[i])<=ub(z) /\ ub(a[i])>=lb(z) + }, + } in %% finish domain first + let { + float: minLB=min(i in dom(i00))(lb(a[i])), + float: maxUB=max(i in dom(i00))(ub(a[i])) + } in + if minLB==maxUB then + z==minLB + else + z >= minLB /\ + z <= maxUB /\ + if {0,1}==dom(i00) /*ub(i00)-lb(i00)==1*/ /*2==card( dom( i00 ) )*/ then + aux_float_eq_if_1(z, a[lb(i00)], (ub(i00)-i00)) /\ + aux_float_eq_if_1(z, a[ub(i00)], (i00-lb(i00))) + else + let { + array[int] of var int: p = eq_encode(i00), + } in + assert(dom(i00) subset index_set(p), "", true) /\ + %%% The convexified bounds seem slow for ^2 and ^3 equations: + % sum(i in dom(i01))( lb(a[i]) * int2float(p[i]) ) <= z /\ %% convexify lower bounds + % sum(i in dom(i01))( ub(a[i]) * int2float(p[i]) ) >= z /\ %% convexify upper bounds + forall (i in dom(i00))( + aux_float_eq_if_1(z, a[i], p[i]) + ) + %% Cuts: + /\ + if fElementCutsXZ then + array_var_float_element__ROOF([ a[i] | i in dom(i00) ], z) :: MIP_cut %% these 2 better as user cuts - too slow + /\ array_var_float_element__ROOF([ -a[i] | i in dom(i00) ], -z) :: MIP_cut %% or even skip them + else true endif + /\ + if fElementCutsXZB then + array_var_float_element__XBZ_lb([ a[i] | i in dom(i00) ], [ p[i] | i in dom(i00) ], z) :: MIP_cut + /\ array_var_float_element__XBZ_lb([ -a[i] | i in dom(i00) ], [ p[i] | i in dom(i00) ], -z) :: MIP_cut + else true endif + endif + endif; + +%%% Facets on the upper surface of the z-a polytope +%%% Possible parameter: maximal number of first cuts taken only +predicate array_var_float_element__ROOF(array[int] of var float: a, + var float: z) = + let { set of int: ix = index_set(a), + int: n = length(a), + array[int] of float: AU = [ ub(a[i]) | i in 1..n], + array[int] of int: srt_ub = sort_by([i | i in 1..n], AU), + %indices of ub sorted up + array[int] of float: AU_srt_ub = [ub(a[srt_ub[i]]) | i in 1..n], + array[int] of float: AL_srt_ub = [lb(a[srt_ub[i]]) | i in 1..n], + array[int] of float: MaxLBFrom = + [ max(j in index_set(AL_srt_ub) where j>=i)(AL_srt_ub[j]) + | i in 1..n ], %% direct, O(n^2) + array[int] of float: ULB = [ + if 1==i then MaxLBFrom[1] + else max([AU_srt_ub[i-1], MaxLBFrom[i]]) + endif | i in 1..n ] + } in + %%% "ROOF" + forall (i in 1..n where + if i==n then true else ULB[i]!=ULB[i+1] endif %% not the same base bound + )( + z <= ULB[i] + + sum( j in i..n where AU_srt_ub[i] != AL_srt_ub[i] ) %% not a const + ( (AU_srt_ub[j]-ULB[i]) * (a[srt_ub[j]]-AL_srt_ub[j]) / (AU_srt_ub[j]-AL_srt_ub[j]) ) ) + ; + +predicate array_var_float_element__XBZ_lb(array[int] of var float: x, array[int] of var int: b, var float: z) = + if fUseXBZCutGen then + array_var_float_element__XBZ_lb__cutgen(x, b, z) :: MIP_cut + else + %% Adding some cuts a priori, also to make solver extract the variables + let { + int: i1 = min(index_set(x)) + } in + (z <= sum(i in index_set(x))(ub(x[i]) * b[i])) %:: MIP_cut -- does not work to put them here TODO + /\ + forall(i in index_set(x) intersect i1..(i1+19)) %% otherwise too many on amaze2 + ( assert(lb(x[i]) == -ub(-x[i]) /\ ub(x[i]) == -lb(-x[i]), " negated var's bounds should swap " ) /\ + z <= x[i] + sum(j in index_set(x) where i!=j)((ub(x[j])-lb(x[i]))*b[j])) %:: MIP_cut %% (ub_j-lb_i) * b_j + /\ + forall(i in index_set(x) intersect i1..(i1+19)) + ( z <= ub(x[i])*b[i] + sum(j in index_set(x) where i!=j)(x[j]+lb(x[j])*(b[j]-1)) ) %:: MIP_cut + /\ + (z <= sum(i in index_set(x))(x[i] + lb(x[i]) * (b[i]-1))) %:: MIP_cut + endif; + +%-----------------------------------------------------------------------------% +% Set constraints +%% ----------------------------------------------- (NO) SETS ---------------------------------------------- +% XXX only for a fixed set here, general see below. +% Normally not called because all plugged into the domain. +% Might be called instead of set_in(x, var set of int s) if s gets fixed? +predicate set_in(var int: x, set of int: s__) = + let { + set of int: s = if has_bounds(x) then s__ intersect dom(x) else s__ endif, + constraint min(s) <= x, + constraint x <= max(s), + } in + if s = min(s)..max(s) then true + elseif fPostprocessDomains then + set_in__POST(x, s) + else %% Update eq_encode + let { + array[int] of var int: p = eq_encode(x); + } in + forall(i in index_set(p) diff s)(p[i]==0) +% let { +% array[int] of int: sL = [ e | e in s where not (e - 1 in s) ]; +% array[int] of int: sR = [ e | e in s where not (e + 1 in s) ]; +% array [index_set(sR)] of var 0..1: B; +% constraint assert(length(sR)==length(sL), "N of lb and ub of sub-intervals of a set should be equal"); +% } in +% sum(B) = 1 %% use indicators +% /\ +% x >= sum(i in index_set(sL))(B[i]*sL[i]) +% /\ +% x <= sum(i in index_set(sR))(B[i]*sR[i]) + endif; + +%%% for a fixed set +predicate set_in_reif(var int: x, set of int: s__, var bool: b) = + if is_fixed(b) then + if fix(b) then x in s__ else x in dom(x) diff s__ endif + elseif has_bounds(x) /\ not (s__ subset dom(x)) then + b <-> x in s__ intersect dom(x) %% Use CSE + else + let { + set of int: s = if has_bounds(x) then s__ intersect dom(x) else s__ endif, + } in + ( + if dom(x) subset s then b==true + elseif card(dom(x) intersect s)==0 then b==false + elseif fPostprocessDomains then + set_in_reif__POST(x, s, b) + %% Bad. Very much so for CBC. 27.06.2019: elseif s == min(s)..max(s) then + %% b <-> (min(s) <= x /\ x <= max(s)) + else + if card(dom(x))<=nMZN__UnaryLenMax_setInReif then %% PARAM TODO + let { + array[int] of var int: p = eq_encode(x); + } in + sum(i in s intersect dom(x))(p[i]) == bool2int(b) + else + bool2int(b) == fVarInBigSetOfInt(x, s) + endif + endif + ) + endif; + + % Alternative +predicate alt_set_in_reif(var int: x, set of int: s, var bool: b) = + b <-> + exists(i in 1..length([ 0 | e in s where not (e - 1 in s) ]))( + let { int: l = [ e | e in s where not (e - 1 in s) ][i], + int: r = [ e | e in s where not (e + 1 in s) ][i] } + in + l <= x /\ x <= r + ); + +%%% for a fixed set +predicate set_in_imp(var int: x, set of int: s__, var bool: b) = + if is_fixed(b) then + if fix(b) then x in s__ else true endif + elseif has_bounds(x) /\ not (s__ subset dom(x)) then + b -> x in s__ intersect dom(x) %% Use CSE + else + let { + set of int: s = if has_bounds(x) then s__ intersect dom(x) else s__ endif, + } in + ( + if dom(x) subset s then true + elseif card(dom(x) intersect s)==0 then b==false + elseif s == min(s)..max(s) then + (b -> min(s) <= x) /\ (b -> x <= max(s)) + else + if card(dom(x))<=nMZN__UnaryLenMax_setInReif then %% PARAM TODO + let { + array[int] of var int: p = eq_encode(x); + } in + sum(i in s intersect dom(x))(p[i]) >= bool2int(b) + else + bool2int(b) <= fVarInBigSetOfInt(x, s) + endif + endif + ) + endif; + +function var 0..1: fVarInBigSetOfInt(var int: x, set of int: s) + = let { + array[int] of int: sL = [ e | e in s where not (e - 1 in s) ]; + array[int] of int: sR = [ e | e in s where not (e + 1 in s) ]; + constraint assert(length(sR)==length(sL), "N of lb and ub of sub-intervals of a set should be equal"); + } in + sum(i in index_set(sL)) (bool2int(x>=sL[i] /\ x<=sR[i])); %% use indicators + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% OTHER SET STUFF COMING FROM nosets.mzn %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% + +annotation bool_search(array[$X] of var bool: x, ann:a1, ann:a2, ann:a3) = + let { array[int] of var bool: xx = array1d(x) } in + int_search([bool2int(xx[i]) | i in index_set(xx)],a1,a2,a3); + +annotation warm_start( array[int] of var bool: x, array[int] of bool: v ) = + warm_start( [ bool2int(x[i]) | i in index_set(x) ], [ bool2int(v[i]) | i in index_set(v) ] ); + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DOMAIN POSTPROCESSING BUILT-INS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Single variable: x = d <-> x_eq_d[d] +predicate equality_encoding__POST(var int: x, array[int] of var int: x_eq_d); + +%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn +predicate set_in__POST(var int: x, set of int: s__); +predicate set_in_reif__POST(var int: x, set of int: s__, var int: b); + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% LOGICAL CONSTRAINTS TO THE SOLVER %%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn => REPEAT TESTS. TODO +predicate int_lin_eq_reif__IND(array[int] of int: c, array[int] of var int: x, int: d, var int: b); +predicate int_lin_le_reif__IND(array[int] of int: c, array[int] of var int: x, int: d, var int: b); +predicate int_lin_ne__IND(array[int] of int: c, array[int] of var int: x, int: d); +predicate aux_int_le_zero_if_0__IND(var int: x, var int: b); +predicate float_lin_le_reif__IND(array[int] of float: c, array[int] of var float: x, float: d, var int: b); +predicate aux_float_eq_if_1__IND(var float: x, var float: y, var int: b); +predicate aux_float_le_zero_if_0__IND(var float: x, var int: b); + +predicate array_int_minimum__IND(var int: m, array[int] of var int: x); +predicate array_int_maximum__IND(var int: m, array[int] of var int: x); +predicate array_float_minimum__IND(var float: m, array[int] of var float: x); +predicate array_float_maximum__IND(var float: m, array[int] of var float: x); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% XBZ cut generator, currently CPLEX only %%%%%%%%%%%%%%%%%%%%%%%%%% +predicate array_var_float_element__XBZ_lb__cutgen(array[int] of var float: x, array[int] of var int: b, var float: z); + diff --git a/share/minizinc/linear_scip/redefs_bool_imp.mzn b/share/minizinc/linear_scip/redefs_bool_imp.mzn new file mode 100644 index 0000000..3799761 --- /dev/null +++ b/share/minizinc/linear_scip/redefs_bool_imp.mzn @@ -0,0 +1,76 @@ +include "redefs_lin_halfreifs.mzn"; + +% SIMPLE BOOLEAN LOGIC +% TODO: why not check "is_fixed(r)" everywhere?? + +predicate bool_eq_imp(var bool: p, var bool: q, var bool: r) = + if is_fixed(r) then + if fix(r) then p = q else true endif + else + let { + var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) + } in x + z <= y + 1 /\ y + z <= x + 1 + endif; + +predicate bool_ne_imp(var bool: p, var bool: q, var bool: r) = + bool_xor_imp(p, q, r); + +predicate bool_le_imp(var bool: p, var bool: q, var bool: r) = + let { + var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r), + } in 1 - x + y >= z; + +predicate bool_lt_imp(var bool: p, var bool: q, var bool: r) = + bool_and_imp(not p, q, r); + +predicate bool_or_imp(var bool: p, var bool: q, var bool: r) = + array_bool_or_imp([p,q], r); + +predicate bool_and_imp(var bool: p, var bool: q, var bool: r) = + array_bool_and_imp([p,q],r); + +predicate bool_xor_imp(var bool: p, var bool: q, var bool: r) = + let { + var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r), + } in x + y >= z /\ x + y + z <= 2; + +% BOOLEAN ARRAY OPERATIONS +predicate array_bool_or_imp(array[int] of var bool: a, var bool: b) = + if forall( i in index_set( a ) )( is_fixed(a[i]) /\ not fix(a[i]) ) then + not b + elseif exists( i in index_set( a ) )( is_fixed(a[i]) /\ fix(a[i]) ) then + true + else + let { + array[index_set(a)] of var bool: a1; + var int: x = bool2int(b), + } in forall(i in index_set(a)) (a1[i] -> a[i]) /\ sum(a1) = x + endif; + +predicate array_bool_and_imp(array[int] of var bool: a, var bool: b) = + if is_fixed(b) then + if fix(b) then + forall(i in index_set(a))( a[i] ) + else + true + endif + elseif forall( i in index_set( a ) )( is_fixed(a[i]) /\ fix(a[i]) ) then + true + else + let { + var int: x = bool2int(b), + array[index_set(a)] of var int: c = + array1d(index_set(a), [ bool2int(a[i] ) | i in index_set(a) ]) + } in forall(i in index_set(c)) ( c[i] >= x ) + endif; + +%% No var int d, sorry TODO: why not??? +predicate bool_lin_eq_imp(array[int] of int: c, array[int] of var bool: x, + int: d, var bool: b) = + aux_int_eq_if_1(sum(i in index_set(x))( c[i]*bool2int(x[i]) ), d, bool2int(b)); diff --git a/share/minizinc/linear_scip/redefs_bool_reifs.mzn b/share/minizinc/linear_scip/redefs_bool_reifs.mzn new file mode 100644 index 0000000..ce2e8b9 --- /dev/null +++ b/share/minizinc/linear_scip/redefs_bool_reifs.mzn @@ -0,0 +1,188 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov +*/ + +%-----------------------------------------------------------------------------% +% +% Logic operations +% Use indicators for reifs (CPLEX)? Seems weak. +% +%-----------------------------------------------------------------------------% + +predicate bool_not(var bool: p, var bool: q) = + bool2int(p) + bool2int(q) = 1; + +predicate bool_and(var bool: p, var bool: q, var bool: r) = +% my_trace(" bool_and: \(p) /\\ \(q) <-> \(r) \n") /\ + if false then + int_lin_le_reif__IND( [-1, -1], [p, q], -2, r) + else + array_bool_and( [p, q], r ) +% bool_and__INT(bool2int(p), bool2int(q), bool2int(r)) + endif; + +predicate bool_and__INT(var int: x, var int: y, var int: z) = + x + y <= z + 1 /\ + %% x + y >= z * 2; % weak + x >= z /\ y >= z; % strong + + +predicate bool_or(var bool: p, var bool: q, var bool: r) = + if false then + int_lin_le_reif__IND( [-1, -1], [p, q], -1, r) + elseif true then + array_bool_or( [p, q], r ) + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + x + y >= z /\ + % x + y <= z * 2; % weak + x <= z /\ y <= z % strong + endif; + +predicate bool_xor(var bool: p, var bool: q) = + 1==p+q; + +predicate bool_xor(var bool: p, var bool: q, var bool: r) = + if false then +% int_lin_eq_reif__IND( [1, 1], [p, q], 1, r) /\ + true + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + x <= y + z /\ + y <= x + z /\ + z <= x + y /\ + x + y + z <= 2 + endif; + + +predicate bool_eq_reif(var bool: p, var bool: q, var bool: r) = +%% trace(" bool_eq_reif: \(p), \(q), \(r) \n") /\ + if is_fixed(r) then % frequent case + if fix(r) = true then p = q else bool_not(p,q) endif + elseif is_fixed(q) then + if fix(q) = true then p = r + else bool_not(p,r) endif + elseif is_fixed(p) then + if fix(p) = true then q = r + else bool_not(q,r) endif + elseif false then +% int_lin_eq_reif__IND( [1, -1], [p, q], 0, r) /\ + true + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + x + y <= z + 1 /\ + x + z <= y + 1 /\ + y + z <= x + 1 /\ + x + y + z >= 1 + endif; + + +predicate bool_ne_reif(var bool: p, var bool: q, var bool: r) = + bool_xor(p, q, r); + + +predicate bool_le(var bool: p, var bool: q) = + let { var int: x = bool2int(p), + var int: y = bool2int(q) } + in + x <= y; + + +predicate bool_le_reif(var bool: p, var bool: q, var bool: r) = + if false then +% int_lin_le_reif__IND( [1, -1], [p, q], 0, r) /\ + true + else + let { var int: x = bool2int(p), + var int: y = bool2int(q), + var int: z = bool2int(r) } + in + 1 - x + y >= z /\ + %% /\ 1 - x + y <= z * 2 not needed + 1 - x <= z /\ y <= z % strong + endif; + + +predicate bool_lt(var bool: p, var bool: q) = + not p /\ q; + + +predicate bool_lt_reif(var bool: p, var bool: q, var bool: r) = + (not p /\ q) <-> r; + +%-----------------------------------------------------------------------------% + +%% Reified disjunction +predicate array_bool_or(array[int] of var bool: a, var bool: b) = + if exists( i in index_set( a ) )( is_fixed(a[i]) /\ fix(a[i]) ) then + b + elseif is_fixed(b) then % frequent case + if fix(b) = true then + sum(i in index_set(a))( bool2int(a[i]) ) >= 1 %% >=1 seems better for MIPDomains... 5.4.19 + else + forall(i in index_set(a))( not a[i] ) + endif + else + let { var int: x = bool2int(b), + array[1..length(a)] of var int: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + sum(c) >= x /\ + % sum(c) <= x * length(a) % weak + forall (i in index_set(a)) (x >= c[i]) % strong + endif; + +%% Reified conjunction +predicate array_bool_and(array[int] of var bool: a, var bool: b) = + if exists( i in index_set( a ) )( is_fixed(a[i]) /\ not fix(a[i]) ) then + not b + elseif is_fixed(b) then % frequent case + if fix(b) = false then + sum(i in index_set(a))( bool2int(a[i]) ) <= length(a)-1 + else + forall(i in index_set(a))( a[i] ) + endif + else + let { var int: x = bool2int(b), + array[1..length(a)] of var int: c = + [ bool2int(a[i]) | i in index_set(a) ] } + in + length(a) - sum(c) >= 1 - x /\ + % length(a) - sum(c) <= (1 - x) * length(a); % weak + forall (i in index_set(a)) (x <= c[i]) % strong + endif; + +% predicate array_bool_xor(array[int] of var bool: a) = .. sum(a) is odd .. +predicate array_bool_xor(array[int] of var bool: a) = + let { var 0..(length(a)-1) div 2: m, + var 1..((length(a)-1) div 2)*2+1: ss = sum(i in index_set(a))( bool2int(a[i]) ) } + in + ss == 1 + 2 * m; + +predicate bool_clause(array[int] of var bool: p, array[int] of var bool: n) = + sum(i in index_set(p))( bool2int(p[i]) ) + - sum(i in index_set(n))( bool2int(n[i]) ) + + length(n) + >= 1; + +predicate bool_lin_eq(array[int] of int: c, array[int] of var bool: x, var int: d) :: promise_total = + sum(i in index_set(x))( c[i]*bool2int(x[i]) ) == d; + +predicate bool_lin_eq_reif(array[int] of int: c, array[int] of var bool: x, + int: d, var bool: b) = %% No var int d, sorry + aux_int_eq_iff_1(sum(i in index_set(x))( c[i]*bool2int(x[i]) ), d, bool2int(b)); + + diff --git a/share/minizinc/linear_scip/redefs_lin_halfreifs.mzn b/share/minizinc/linear_scip/redefs_lin_halfreifs.mzn new file mode 100644 index 0000000..f96a8a6 --- /dev/null +++ b/share/minizinc/linear_scip/redefs_lin_halfreifs.mzn @@ -0,0 +1,227 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov +*/ + +%-----------------------------------------------------------------------------% +% Auxiliary: indicator constraints +% p -> x # 0 where p is a 0/1 variable and # is a comparison + +% Base cases + +%% used e.g. in element +predicate aux_float_eq_if_1(var float: x, var float: y, var int: p) = + if is_fixed(p) then + if 1==fix(p) then x==y else true endif + elseif is_fixed(x) /\ is_fixed(y) then %%% Needed to avoid constant domain var + if fix(x)!=fix(y) then p==0 else true endif + elseif is_fixed(x-y) then %%% Hypothetically possible to land here + if 0.0!=fix(x-y) then p==0 else true endif + elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + aux_float_eq_zero_if_1__POST(x - y, p, p) + elseif fMZN__UseIndicators then + aux_float_eq_if_1__IND(x, y, p) + else + aux_float_le_if_1(x, y, p) /\ + aux_float_ge_if_1(x, y, p) + endif; + +predicate aux_int_eq_if_1(var int: x, var int: y, var int: p) = + if is_fixed(p) then + if fix(p) = 1 then (x = y) else true endif + elseif is_fixed(x) /\ is_fixed(y) then + if fix(x) != fix(y) then (p = 0) else true endif + % elseif is_fixed(x-y) then % TODO: Necessary for integers?? + % if 0.0!=fix(x-y) then p==0 else true endif + % elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + % % TODO MIPDomains + % elseif fMZN__UseIndicators then % TODO: Necessary for integers?? + % aux_float_eq_if_1__IND(x, y, p) + else + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) + endif; + +predicate aux_float_ne_if_1(var float: x, var float: y, var int: p) = + if is_fixed(p) then + if fix(p) = 1 then (x = y) else true endif + elseif is_fixed(x) /\ is_fixed(y) then %%% Needed to avoid constant domain var + if (fix(x) = fix(y)) then (p = 0) else true endif + elseif is_fixed(x-y) then %%% Hypothetically possible to land here + if (0.0 = fix(x-y)) then (p = 0) else true endif + % TODO: What is necessary for not equals? + % elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + % aux_float_ne_zero_if_1__POST(x - y, p, p) + % elseif fMZN__UseIndicators then + % aux_float_ne_if_1__IND(x, y, p) + else + let { array[1..2] of var bool: q } in + ( q[1] -> (x < y) ) /\ + ( q[2] -> (x > y) ) /\ + (sum(q) = p) + endif; + +predicate aux_int_ne_if_1(var int: x, var int: y, var int: p) = + if is_fixed(p) then + if fix(p) = 1 then (x != y) else true endif + elseif is_fixed(x) /\ is_fixed(y) then + if fix(x) = fix(y) then (p = 0) else true endif + % elseif is_fixed(x-y) then % TODO: Necessary for integers?? + % if 0.0!=fix(x-y) then p==0 else true endif + % elseif fPostprocessDomains /\ fPostproDom_AUX /\ fPostproDom_DIFF then + % % TODO MIPDomains + % elseif fMZN__UseIndicators then % TODO: Necessary for integers?? + % aux_float_eq_if_1__IND(x, y, p) + else + let { array[1..2] of var bool: q } in + ( q[1] -> (x < y) ) /\ + ( q[2] -> (x > y) ) /\ + (sum(q) = p) + endif; + +predicate aux_int_le_zero_if_0(var int: x, var int: p) = + if is_fixed(p) then + if 0==fix(p) then x<=0 else true endif %% 0==fix !! + elseif lb(x)>0 then + p==1 + elseif not (0 in dom(x)) then + let { + constraint assert( ub(x) < infinity, + "aux_int_le_zero_if_0: variable \(x)'s domain: dom(\(x)) = \(dom(x)), should have finite upper bound\n" ), + set of int: sDomNeg = dom(x) intersect -infinity..-1, + constraint assert( card( sDomNeg ) > 0, + "Variable \(x): dom(\(x)) = \(dom(x)), but dom() intersect -inf..-1: \(sDomNeg)\n" ), + } in + aux_int_le_if_0( x, max( sDomNeg ), p ) + elseif fPostprocessDomains /\ fPostproDom_AUX then + aux_int_le_zero_if_1__POST(x, 1-p) + elseif fMZN__UseIndicators then + aux_int_le_zero_if_0__IND(x, p) + else + assert( ub(x)0.0 then + p==1 + elseif fPostprocessDomains /\ fPostproDom_AUX then + aux_float_le_zero_if_1__POST(x, 1-p, 1-p) + elseif fMZN__UseIndicators then + aux_float_le_zero_if_0__IND(x, p) + else + x <= ub(x) * p + endif; + +predicate aux_float_lt_zero_if_0(var float: x, var int: p) = + assert( has_bounds(x), "Variable \(x) needs finite bounds for a big-M constraint" ) /\ + if is_fixed(p) then + if 0==fix(p) then x<0.0 else true endif + elseif lb(x)>=0.0 then + p==1 + elseif fPostprocessDomains /\ fPostproDom_AUX then + aux_float_lt_zero_if_1__POST(x, 1-p, 1-p, float_lt_EPS) + elseif fMZN__UseIndicators then + aux_float_le_zero_if_0__IND(x+float_lt_EPS, p) %% Here just absolute EPS, TODO + else +%% let { float: rho = float_lt_EPS_coef__ * max(abs(ub(x)), abs(lb(x))) } % same order of magnitude as ub(x) + let { float: rho = float_lt_EPS } % absolute eps + in + %%% This one causes 2x- derivation of EPS: + %% x < (ub(x) + rho) * p + %%% Better? + x <= (ub(x) + rho) * p - rho + %%% This just uses absolute eps: + %% x < (ub(x) + float_lt_EPS) * p + endif; + + +% Derived cases +predicate aux_int_le_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, p); + +predicate aux_int_ge_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, p); + +predicate aux_int_le_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y, 1 - p); + +predicate aux_int_ge_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x, 1 - p); + +predicate aux_int_lt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, p); + +predicate aux_int_gt_if_0(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x + 1, p); + +predicate aux_int_lt_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(x - y + 1, 1 - p); + +predicate aux_int_gt_if_1(var int: x, var int: y, var int: p) = + aux_int_le_zero_if_0(y - x + 1, 1 - p); + +%% int: switching differences to float to avoid creating integer vars +/* Used anywhere? +predicate aux_int_le_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, p); + +predicate aux_int_ge_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, p); + +predicate aux_int_le_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, 1 - p); + +predicate aux_int_ge_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, 1 - p); + +predicate aux_int_lt_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y + 1.0, p); + +predicate aux_int_gt_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x + 1.0, p); + +predicate aux_int_lt_if_1(var float: x, float: y, var int: p) = + aux_float_le_zero_if_0(x - y + 1.0, 1 - p); +*/ + +predicate aux_float_le_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, p); + +predicate aux_float_ge_if_0(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, p); + +predicate aux_float_le_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(x - y, 1 - p); + +predicate aux_float_ge_if_1(var float: x, var float: y, var int: p) = + aux_float_le_zero_if_0(y - x, 1 - p); + +predicate aux_float_lt_if_0(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(x - y, p); + +predicate aux_float_gt_if_0(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(y - x, p); + +predicate aux_float_lt_if_1(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(x - y, 1 - p); + +predicate aux_float_gt_if_1(var float: x, var float: y, var int: p) = + aux_float_lt_zero_if_0(y - x, 1 - p); + + +% -------------------------- Domains postpro --------------------------- + %% To avoid looking if an original int var x-y exists and has eq_encode: + %% Passing both int and float version of the indicator for flexibility: +predicate aux_float_eq_zero_if_1__POST(var float: x, var int: pI, var float: p); + +predicate aux_int_le_zero_if_1__POST(var int: x, var int: p); +predicate aux_float_le_zero_if_1__POST(var float: x, var int: pI, var float: p); +predicate aux_float_lt_zero_if_1__POST(var float: x, var int: pI, var float: p, float: eps); diff --git a/share/minizinc/linear_scip/redefs_lin_imp.mzn b/share/minizinc/linear_scip/redefs_lin_imp.mzn new file mode 100644 index 0000000..3f26a35 --- /dev/null +++ b/share/minizinc/linear_scip/redefs_lin_imp.mzn @@ -0,0 +1,261 @@ +include "redefs_lin_halfreifs.mzn"; +include "redefs_lin_reifs.mzn"; + +%% var, var +predicate int_le_imp(var int: x, var int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then x<=y else true endif + elseif ub(x)<=lb(y) then true + elseif lb(x)>ub(y) then (not b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_int_le_if_1(x, y, b) + endif; + +%% var, var +predicate int_lt_imp(var int: x, var int: y, var bool: b) = + if is_fixed(x) then + int_le_imp(x + 1, y, b) + else + int_le_imp(x, y - 1, b) + endif; + +%% var, var +predicate int_eq_imp(var int: x, var int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + elseif card( dom(x) intersect dom(y) ) > 0 then + if is_fixed(x) then + if is_fixed(y) then + b -> (fix(x) = fix(y)) + else + int_eq_imp(y, fix(x), b) + endif + elseif is_fixed(y) then + int_eq_imp(x, fix(y), b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_int_eq_if_1(x, y, b) + endif + else + not b + endif; + +%% var, const +predicate int_eq_imp(var int: x, int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + elseif y in dom(x) then + if is_fixed(x) then + b -> (y = fix(x)) + % elseif fPostprocessDomains then + % % TODO MIPDomains + elseif card(dom(x)) < nMZN__UnaryLenMax_eq then % TODO: Use eq_encode in imp ?? + let { + array[int] of var int: p = eq_encode(x); + } in b <= p[y] + else + aux_int_eq_if_1(x, y, b) + endif + else + not b + endif; + +%% var, var +predicate int_ne_imp(var int: x, var int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + true + else + if is_fixed(x) then + if is_fixed(y) then + b -> (fix(x) != fix(y)) + else + int_ne_imp(y, fix(x), b) + endif + elseif is_fixed(y) then + int_ne_imp(x, fix(y), b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_int_ne_if_1(x, y, b) + endif + endif; + +%% var, const +predicate int_ne_imp(var int: x, int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + true + else + if is_fixed(x) then + b -> (y != fix(x)) + % elseif fPostprocessDomains then + % % TODO MIPDomains + elseif card(dom(x)) < nMZN__UnaryLenMax_eq then + let { + array[int] of var int: p = eq_encode(x); + } in b <= (1 - p[y]) + else + aux_int_ne_if_1(x, y, b) + endif + endif; + +%-----------------------------------------------------------------------------% + +%% lin_expr, const +predicate int_lin_le_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if (d = 0) /\ (length(c) = 2) /\ (abs(c[1]) = 1) /\ (c[1] = -1 * c[2]) then + if (c[1] < 0) then + int_le_imp(x[2], x[1], b) + else + int_le_imp(x[1], x[2], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_le_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + elseif fAvoidNI then + aux_float_le_if_1(sum2float(c, x), d, b) + else + aux_int_le_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate int_lin_lt_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if true then + abort("int_lin_lt_imp not supposed to be called") + else + int_lin_le_imp(c, x, d - 1, b) + endif; + +%% lin_expr, const +predicate int_lin_eq_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if (d = 0) /\ (length(c) = 2) /\ (abs(c[1]) = 1) /\ (c[1] = -1 * c[2]) then + int_ne_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_eq_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + elseif fAvoidNI then + aux_float_eq_if_1(sum2float(c, x), d, b) + else + aux_int_eq_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +%% lin_expr, const +predicate int_lin_ne_imp(array[int] of int: c, array[int] of var int: x, + int: d, var bool: b) = + if (d = 0) /\ (length(c) = 2) /\ (abs(c[1]) = 1) /\ (c[1] = -1 * c[2]) then + int_ne_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_ne_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + elseif fAvoidNI then + aux_float_ne_if_1(sum2float(c, x), d, b) + else + aux_int_ne_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +%-----------------------------------------------------------------------------% + +%% var float, var float +predicate float_le_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then x <= y else true endif + elseif ub(x) <= lb(y) then true + elseif lb(x) > ub(y) then (not b) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_le_if_1(x, y, b) + endif; + +%% var float, var float +predicate float_lt_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x < y) else true endif + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_lt_if_1(x, y, b) + endif; + +%% var float, var float +predicate float_eq_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + not b + elseif is_fixed(x) /\ is_fixed(y) then + b -> (fix(x) == fix(y)) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_eq_if_1(x, y, b) + endif; + +%% var float, var float +predicate float_ne_imp(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + elseif (ub(x) < lb(y)) \/ (lb(x) > ub(y)) then + true + elseif is_fixed(x) /\ is_fixed(y) then + b -> (fix(x) != fix(y)) + % elseif fPostprocessDomains /\ fPostproDom_DIFF then + % % TODO MIPDomains + else + aux_float_ne_if_1(x, y, b) + endif; + +%-----------------------------------------------------------------------------% + +predicate float_lin_eq_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + float_eq_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_eq_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + else + aux_float_eq_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate float_lin_ne_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + float_ne_imp(x[1], x[2], b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_ne_imp(sum(i in index_set(x))(c[i] * x[i]), d, not b) + else + aux_float_ne_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate float_lin_le_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + if (c[1] < 0.0) then + float_le_imp(x[2], x[1], b) + else + float_le_imp(x[1], x[2], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_le_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + else + aux_float_le_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; + +predicate float_lin_lt_imp(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if (d = 0.0) /\ (length(c) = 2) /\ (abs(c[1]) = 1.0) /\ (c[1] = -1.0 * c[2]) then + if (c[1] < 0.0) then + float_lt_imp(x[2], x[1], b) + else + float_lt_imp(x[1], x[2], b) + endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_lt_imp(sum(i in index_set(x))(c[i] * x[i]), d, b) + else + aux_float_lt_if_1(sum(i in index_set(x))(c[i] * x[i]), d, b) + endif; diff --git a/share/minizinc/linear_scip/redefs_lin_reifs.mzn b/share/minizinc/linear_scip/redefs_lin_reifs.mzn new file mode 100644 index 0000000..a1d6294 --- /dev/null +++ b/share/minizinc/linear_scip/redefs_lin_reifs.mzn @@ -0,0 +1,502 @@ +/* +% FlatZinc built-in redefinitions for linear solvers. +% +% AUTHORS +% Sebastian Brand +% Gleb Belov +*/ + +%-----------------------------------------------------------------------------% +% +% Linear equations and inequations +% TODO Use indicators for (half)reifs. +% Otherwise and more using unary encoding for reasonable domains +% +%-----------------------------------------------------------------------------% + +% Domains: reduce all to aux_ stuff? +%% never use Concert's reif feature + +%% var, var +predicate int_le_reif(var int: x, var int: y, var bool: b) = +%% trace(" int_le_reif VV: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_le_reif__POST(x-y, 0, b) + else + int_le_reif__NOPOST(x, y, b) + endif +; + +%% const, var +predicate int_le_reif(int: x, var int: y, var bool: b) = +%% trace(" int_le_reif CV: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif not (x in dom(y)) then %% dom(y) has holes + let { + set of int: sDom2 = dom(y) intersect x+1..infinity, + constraint assert( card( sDom2 ) > 0, + "Variable: dom(\(y)) = \(dom(y)), but dom() intersect \(x)..inf: \(sDom2)\n" ), + } in + b <-> min( sDom2 ) <= y + elseif fPostprocessDomains then + int_ge_reif__POST(y, x, b) + else + int_le_reif(-y, -x, b) + endif +; + +%% var, const +predicate int_le_reif(var int: x, int: y, var bool: b) = +%% trace(" int_le_reif VC: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif not (y in dom(x)) then %% dom(x) has holes + let { + set of int: sDom2 = dom(x) intersect -infinity..y-1, + constraint assert( card( sDom2 ) > 0, + "Variable: dom(\(x)) = \(dom(x)), but dom() intersect -inf..\(y): \(sDom2)\n" ), + } in + b <-> x <= max( sDom2 ) + else + if fPostprocessDomains then + int_le_reif__POST(x, y, b) + else + int_le_reif__NOPOST(x, y, b) + endif + endif +; + +%% var int, var int +predicate int_le_reif__NOPOST(var int: x, var int: y, var bool: b) = + aux_int_le_if_1(x, y, b) /\ %% This can call POSTs... TODO + aux_int_gt_if_0(x, y, b) + ; + +%% var, var +predicate int_lt_reif(var int: x, var int: y, var bool: b) = + %% int_le_reif(x-y, -1, b); %% This would produce a new variable x-y and possibly POST it + %% but it looks like differences should not be + if is_fixed( x ) then + int_le_reif(x+1, y, b) + else + int_le_reif(x, y-1, b) + endif; + +%% var, var +predicate int_ne(var int: x, var int: y) = + if fPostproDom_DIFF then + int_ne(x-y, 0) + else + int_ne__SIMPLE(x-y, 0) + endif; + +%% var, const +predicate int_ne(var int: x, int: y) = + if y in dom(x) then + if y==ub(x) then + x <= y-1 + elseif y==lb(x) then + x >= y+1 + elseif fPostprocessDomains then + int_ne__POST(x, y) + elseif card(dom(x))y) + endif; + +%% var, var +predicate int_eq_reif(var int: x, var int: y, var bool: b) = +%% trace(" int_eq_reif VV: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if fix(b) then x==y else x!=y endif + elseif card(dom(x) intersect dom(y))>0 then + if is_fixed(x) then + if is_fixed(y) then + b <-> fix(x)==fix(y) + else + int_eq_reif(y, fix(x), b) + endif + elseif is_fixed(y) then + int_eq_reif(x, fix(y), b) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + int_eq_reif(x-y, 0, b) + else + aux_int_eq_iff_1(x, y, b) + endif + else + not b + endif; + +%% var, const +predicate int_eq_reif(var int: x, int: y, var bool: b) = +%% trace(" int_eq_reif VC: \(x), \(y), \(b) \n") /\ + if is_fixed(b) then + if fix(b) then x==y else x!=y endif + elseif y in dom(x) then + if is_fixed(x) then + b <-> y==fix(x) + elseif card(dom(x))==2 then + x == max(dom(x) diff {y}) + b*(y - max(dom(x) diff {y})) %% This should directly connect b to var 0..1: x + elseif fPostprocessDomains then + int_eq_reif__POST(x, y, b) +%%% THIS seems pretty complex, especially for binaries, and does not connect to eq_encoding (/ MIPD?) +%% elseif y==lb(x) then +%% int_lt_reif(y, x, not b) +%% elseif y==ub(x) then +%% int_lt_reif(x, y, not b) + elseif card(dom(x))y endif + elseif false then + float_lin_le_reif__IND( [1.0, -1.0], [x, y], 0.0, b) + elseif ub(x) <= y then + b == true + elseif lb(x) > y then + b == false + elseif fPostprocessDomains then + float_le_reif__POST(x, y, b, float_lt_EPS) + else + float_le_reif__NOPOST(x, y, b) + endif; + +%% const, var float +predicate float_le_reif(float: x, var float: y, var bool: b) = + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif false then + float_lin_le_reif__IND( [1.0, -1.0], [x, y], 0.0, b) + elseif ub(x) <= lb(y) then + b == true + elseif lb(x) > ub(y) then + b == false + elseif fPostprocessDomains then + float_ge_reif__POST(y, x, b, float_lt_EPS) + else + float_le_reif(-y, -x, b) + endif; + +%% var float, var float +predicate float_le_reif(var float: x, var float: y, var bool: b) = + if is_fixed(b) then + if true==fix(b) then x<=y else x>y endif + elseif ub(x)<=lb(y) then b==true + elseif lb(x)>ub(y) then b==false + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_le_reif(x-y, 0.0, b) + else + float_le_reif__NOPOST(x-y, 0, b) + endif +; + +%% var float, var float +predicate float_le_reif__NOPOST(var float: x, var float: y, var bool: b) = + aux_float_le_if_1(x, y, (b)) /\ %% Can call __POSTs TODO + aux_float_gt_if_0(x, y, (b)) + ; + +%% TODO +predicate float_lt_reif(var float: x, var float: y, var bool: b) = + %% Actually = float_le_reif(x, y-eps, b). + if is_fixed(b) then + if true==fix(b) then x=y endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + aux_float_lt_zero_iff_1__POST(x - y, b, float_lt_EPS) + else + aux_float_lt_if_1(x, y, (b)) /\ + aux_float_ge_if_0(x, y, (b)) + endif; + +%% var, const +predicate float_ne(var float: x, float: y) = + if fPostprocessDomains then + float_ne__POST(x, y, float_lt_EPS) + else + float_ne__SIMPLE(x, y) + endif; + +predicate float_ne__SIMPLE(var float: x, var float: y) = + if true then + let { var 0..1: p } + in + aux_float_lt_if_1(x, y, (p)) /\ + aux_float_gt_if_0(x, y, (p)) + else %TODO: Why is this not half-reified? + 1 == (x>y) + (xub(y) then + b == false + elseif is_fixed(x) /\ is_fixed(y) then + b == (fix(x) == fix(y)) + elseif fPostprocessDomains /\ fPostproDom_DIFF then + float_eq_reif__POST(x-y, 0, b, float_lt_EPS) + else + aux_float_eq_iff_1(x, y, (bool2int(b))) + endif; + +predicate float_ne_reif(var float: x, var float: y, var bool: b) = + float_eq_reif(x, y, not (b)); + +%-----------------------------------------------------------------------------% + +predicate float_lin_eq_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if fPostprocessDomains /\ fPostproDom_DIFF then + float_eq_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b) + else + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, b) + endif; + +predicate float_lin_ne_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if fPostprocessDomains /\ fPostproDom_DIFF then + float_ne_reif(sum(i in index_set(x))( c[i]*x[i] ), d, not b) + else + aux_float_eq_iff_1(sum(i in index_set(x))( c[i]*x[i] ), d, not b) + endif; + +predicate float_lin_le_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + if fPostprocessDomains /\ fPostproDom_DIFF then + float_le_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b) + else + float_le_reif__NOPOST(sum(i in index_set(x))( c[i]*x[i] ), d, b) + endif; + +predicate float_lin_lt_reif(array[int] of float: c, array[int] of var float: x, + float: d, var bool: b) = + float_lt_reif(sum(i in index_set(x))( c[i]*x[i] ), d, b); + + +%-----------------------------------------------------------------------------% +% Auxiliary: equality reified onto a 0/1 variable + +predicate aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + if is_fixed(p) then + if 1==fix(p) then x==y else x!=y endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + abort(" aux_int_eq_iff_1 should not be used with full domain postprocessing") + elseif false then + true + elseif fAuxIntEqOLD00 then + let { array[1..2] of var 0..1: q } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) /\ + aux_int_lt_if_0(x, y, q[1]) /\ + aux_int_gt_if_0(x, y, q[2]) /\ + sum(q) == p + 1 + else + %% redundant p == (x<=y /\ y<=x) /\ + 1+p == (x<=y) + (y<=x) + endif; + +predicate aux_int_eq_iff_1__float(var float: x, float: y, var int: p) = + if fAuxIntEqOLD00 then + assert( false, "Don't use aux_int_eq_iff_1__float" ) +/* let { array[1..2] of var 0..1: q } + in + aux_int_le_if_1(x, y, p) /\ + aux_int_ge_if_1(x, y, p) /\ + aux_int_lt_if_0(x, y, q[1]) /\ + aux_int_gt_if_0(x, y, q[2]) /\ + sum(q) == p + 1 */ + else + assert( false, "Don't use aux_int_eq_iff_1__float with fAuxIntEqOLD00" ) + endif; + + % Alternative 2 +predicate aux_int_eq_iff_1__WEAK1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q_458 } + in + aux_int_lt_if_0(x - p, y, q_458[1]) /\ + aux_int_gt_if_0(x + p, y, q_458[2]) /\ + sum(q_458) <= 2 - 2*p /\ + sum(q_458) <= 1 + p; + + % Alternative 1 +predicate alt_1_aux_int_eq_iff_1(var int: x, var int: y, var int: p) = + let { array[1..2] of var 0..1: q } + in + aux_int_lt_if_0(x - p, y, q[1]) /\ + aux_int_gt_if_0(x + p, y, q[2]) /\ + q[1] <= 1 - p /\ + q[2] <= 1 - p /\ + sum(q) <= 1 + p; + +predicate aux_float_eq_iff_1(var float: x, var float: y, var int: p) = + if is_fixed(p) then + if 1==fix(p) then x==y else x!=y endif + elseif fPostprocessDomains /\ fPostproDom_DIFF then + abort(" aux_float_eq_iff_1 should not be used with full domain postprocessing") + elseif fAuxFloatEqOLD00 then + let { array[1..2] of var 0..1: q } + in + aux_float_le_if_1(x, y, p) /\ + aux_float_ge_if_1(x, y, p) /\ + aux_float_lt_if_0(x, y, (q[1])) /\ + aux_float_gt_if_0(x, y, (q[2])) /\ + sum(i in 1..2)((q[i])) == 1 + p + else + %% redundant p == (x<=y /\ y<=x) /\ + 1+p == (x<=y) + (y<=x) + endif; + + + +% ----------------------------- Domains postpro --------------------------- + +%%%%%%%%%%%%%%%%%% predicate int_le_reif__POST(var int: x, var int: y, var int: b); +%%%%%%%%%%%%%%%%%% predicate int_le_reif__POST(int: x, var int: y, var int: b); +%%%%%%% var int: b: bool2int is a reverse_map, not passed to .fzn +%% var, const +predicate int_le_reif__POST(var int: x, int: y, var int: b); +%% var, const +predicate int_ge_reif__POST(var int: x, int: y, var int: b); + +%% var, const +predicate int_eq_reif__POST(var int: x, int: y, var int: b); +%% var, const +predicate int_ne__POST(var int: x, int: y); + +%%%%%%%%%%%%%%%%%% predicate float_le_reif__POST(var float: x, var float: y, var int: b); +%%%%%%%%%%%%%%%%%% predicate float_le_reif__POST(float: x, var float: y, var int: b); +%% var, const +predicate float_le_reif__POST(var float: x, float: y, var int: b, float: epsRel); +%% var, const +predicate float_ge_reif__POST(var float: x, float: y, var int: b, float: epsRel); + +%% var, var +predicate aux_float_lt_zero_iff_1__POST(var float: x, var int: b, float: epsRel); + +%% var, const +predicate float_eq_reif__POST(var float: x, float: y, var int: b, float: epsRel); +%% var, const +predicate float_ne__POST(var float: x, float: y, float: epsRel); + + diff --git a/share/minizinc/linear_scip/subcircuit_wDummy.mzn b/share/minizinc/linear_scip/subcircuit_wDummy.mzn new file mode 100644 index 0000000..48fb747 --- /dev/null +++ b/share/minizinc/linear_scip/subcircuit_wDummy.mzn @@ -0,0 +1,86 @@ +/* Linearized version + * Uses a predicate which constructs a subcircuit which always includes an extra dummy vertex + * Is worse than the just slightly adapted standard variant... +*/ +include "alldifferent.mzn"; + +/** @group globals + Constrains the elements of \a x to define a subcircuit where \a x[\p i] = \p j + means that \p j is the successor of \p i and \a x[\p i] = \p i means that \p i + is not in the circuit. +*/ +%% Linear version +predicate subcircuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + constraint forall(i in S)(x[i] in l..u), + array[l..u+1] of var l..u+1: xx, + constraint forall(i in S)(xx[i] in dom(x[i]) union {u+1}), + } in + alldifferent(x) /\ + subcircuit_wDummy(xx) /\ + forall( i in S, j in dom(x[i]) )( %% also when i==j? + eq_encode(x[i])[j] >= 2*eq_encode(xx[i])[j] + + eq_encode(xx[i])[u+1] + eq_encode(xx[u+1])[j] - 1 %% -1 + /\ + eq_encode(x[i])[j] >= eq_encode(xx[i])[j] + /\ + eq_encode(x[i])[j] <= eq_encode(xx[i])[j] + eq_encode(xx[i])[u+1] + /\ + eq_encode(x[i])[j] <= eq_encode(xx[i])[j] + eq_encode(xx[u+1])[j] + ) + /\ + forall( i in S )( + eq_encode(x[i])[i] == eq_encode(xx[i])[i] + ) + ; + +%% Should include at least 2 nodes if >0? +%% xx[n] is dummy +predicate subcircuit_wDummy(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + set of int: S__ = S diff {u}, %% the real nodes + array[S__] of var 2..n: order, +%% constraint order[n]==1, %% fix the dummy +%% var bool: empty = (firstin == u+1), no, break 2-cycles with dummy + } in + alldifferent(x) /\ + % NO alldifferent(order) /\ + + %%% MTZ model. Note that INTEGER order vars seem better!: + forall( i in S__, j in dom(x[i]) where i!=j /\ j!=u )( + order[i] - order[j] + (n-1)*eq_encode(x[i])[j] + % + (n-3)*bool2int(x[j]==i) %% the Desrochers & Laporte '91 term + % --- strangely enough it is much worse on vrp-s2-v2-c7_vrp-v2-c7_det_ADAPT_1_INVERSE.mzn! + <= n-2 ) /\ + + %% Break 2-cycles with dummy: + forall( i in S__ )( + eq_encode(x[i])[u] + eq_encode(x[u])[i] <= 1 + /\ + %% Ensure dummy is in: + if i in dom(x[i]) then + eq_encode(x[i])[i] >= eq_encode(x[u])[u] + else + true + endif + ) + /\ + + % Symmetry? Each node that is not in is numbered after the lastin node. + forall(i in S) ( + true +% (not ins[i]) <-> (n == order[i]) + ) + ; + +predicate subcircuit_reif(array[int] of var int: x, var bool: b) = + abort("Reified subcircuit/1 is not supported."); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/all_different.mzn b/share/minizinc/std/all_different.mzn new file mode 100644 index 0000000..d952281 --- /dev/null +++ b/share/minizinc/std/all_different.mzn @@ -0,0 +1,24 @@ +include "all_different_int.mzn"; +include "all_different_set.mzn"; +include "analyse_all_different.mzn"; + +/** @group globals.alldifferent + Constrain the array of integers \a x to be all different. +*/ +predicate all_different(array[$X] of var int: x) = + analyse_all_different(array1d(x)) /\ + all_different_int(array1d(x)); + +/** @group globals.alldifferent + Constrain the array of sets of integers \a x to be all different. +*/ +predicate all_different(array[$X] of var set of int: x) = + all_different_set(array1d(x)); +% Synonyms for the above. + +predicate alldifferent(array[$X] of var int: x) = + all_different_int(array1d(x)); + +predicate alldifferent(array[$X] of var set of int: x) = + all_different_set(array1d(x)); + diff --git a/share/minizinc/std/all_different_int.mzn b/share/minizinc/std/all_different_int.mzn new file mode 100644 index 0000000..dec50e1 --- /dev/null +++ b/share/minizinc/std/all_different_int.mzn @@ -0,0 +1,12 @@ +include "fzn_all_different_int.mzn"; +include "fzn_all_different_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate all_different_int(array[int] of var int: x) = + fzn_all_different_int(x); + +predicate all_different_int_reif(array[int] of var int: x, var bool: b) = + fzn_all_different_int_reif(x, b); diff --git a/share/minizinc/std/all_different_set.mzn b/share/minizinc/std/all_different_set.mzn new file mode 100644 index 0000000..0660866 --- /dev/null +++ b/share/minizinc/std/all_different_set.mzn @@ -0,0 +1,12 @@ +include "fzn_all_different_set.mzn"; +include "fzn_all_different_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate all_different_set(array[int] of var set of int: x) = + fzn_all_different_set(x); + +predicate all_different_set(array[int] of var set of int: x, var bool: b) = + fzn_all_different_set_reif(x, b); diff --git a/share/minizinc/std/all_disjoint.mzn b/share/minizinc/std/all_disjoint.mzn new file mode 100644 index 0000000..6427f80 --- /dev/null +++ b/share/minizinc/std/all_disjoint.mzn @@ -0,0 +1,11 @@ +include "fzn_all_disjoint.mzn"; +include "fzn_all_disjoint_reif.mzn"; + +/** @group globals.alldifferent + Constrain the array of sets of integers \a S to be pairwise disjoint. +*/ +predicate all_disjoint(array[$X] of var set of int: S) = + fzn_all_disjoint(array1d(S)); + +predicate all_disjoint_reif(array[$X] of var set of int: S, var bool: b) = + fzn_all_disjoint_reif(array1d(S), b); diff --git a/share/minizinc/std/all_equal.mzn b/share/minizinc/std/all_equal.mzn new file mode 100644 index 0000000..88906c4 --- /dev/null +++ b/share/minizinc/std/all_equal.mzn @@ -0,0 +1,14 @@ +include "all_equal_int.mzn"; +include "all_equal_set.mzn"; + +/** @group globals.alldifferent + Constrain the array of integers \a x to be all equal +*/ +predicate all_equal(array[$X] of var int: x) = + all_equal_int(array1d(x)); + +/** @group globals.alldifferent + Constrain the array of sets of integers \a x to be all equal +*/ +predicate all_equal(array[$X] of var set of int: x) = + all_equal_set(array1d(x)); diff --git a/share/minizinc/std/all_equal_int.mzn b/share/minizinc/std/all_equal_int.mzn new file mode 100644 index 0000000..388a313 --- /dev/null +++ b/share/minizinc/std/all_equal_int.mzn @@ -0,0 +1,12 @@ +include "fzn_all_equal_int.mzn"; +include "fzn_all_equal_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all equal. +%-----------------------------------------------------------------------------% + +predicate all_equal_int(array[int] of var int: x) = + fzn_all_equal_int(x); + +predicate all_equal_int_reif(array[int] of var int: x, var bool: b) = + fzn_all_equal_int_reif(x,b); diff --git a/share/minizinc/std/all_equal_set.mzn b/share/minizinc/std/all_equal_set.mzn new file mode 100644 index 0000000..f66022b --- /dev/null +++ b/share/minizinc/std/all_equal_set.mzn @@ -0,0 +1,12 @@ +include "fzn_all_equal_set.mzn"; +include "fzn_all_equal_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all equal. +%-----------------------------------------------------------------------------% + +predicate all_equal_set(array[int] of var set of int: x) = + fzn_all_equal_set(x); + +predicate all_equal_set_reif(array[int] of var set of int: x, var bool: b) = + fzn_all_equal_set_reif(x,b); diff --git a/share/minizinc/std/alldifferent.mzn b/share/minizinc/std/alldifferent.mzn new file mode 100644 index 0000000..cbb1fb1 --- /dev/null +++ b/share/minizinc/std/alldifferent.mzn @@ -0,0 +1,5 @@ +% The actual definitions are in all_different.mzn. +% This file is used to handle the case where users include +% "alldifferent.mzn"; +% +include "all_different.mzn"; diff --git a/share/minizinc/std/alldifferent_except_0.mzn b/share/minizinc/std/alldifferent_except_0.mzn new file mode 100644 index 0000000..fa20485 --- /dev/null +++ b/share/minizinc/std/alldifferent_except_0.mzn @@ -0,0 +1,12 @@ +include "fzn_alldifferent_except_0.mzn"; +include "fzn_alldifferent_except_0_reif.mzn"; + +/** @group globals.alldifferent + Constrain the array of integers \a vs to be all different except those + elements that are assigned the value 0. +*/ +predicate alldifferent_except_0(array[$X] of var int: vs) = + fzn_alldifferent_except_0(array1d(vs)); + +predicate alldifferent_except_0_reif(array[$X] of var int: vs, var bool: b) = + fzn_alldifferent_except_0_reif(array1d(vs),b); diff --git a/share/minizinc/std/alternative.mzn b/share/minizinc/std/alternative.mzn new file mode 100644 index 0000000..a73c458 --- /dev/null +++ b/share/minizinc/std/alternative.mzn @@ -0,0 +1,23 @@ +include "fzn_alternative.mzn"; +include "fzn_alternative_reif.mzn"; + +/** @group globals.scheduling +Alternative constraint for optional tasks. Task (\a s0,\a d0) spans the +optional tasks (\a s[\p i],\a d[\p i]) in the array arguments +and at most one can occur +*/ +predicate alternative(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d) = + assert(index_set(s) = index_set(d), + "alternative: index sets of third and fourth argument must be identical", + fzn_alternative(s0, d0, s, d) + ); + +predicate alternative_reif(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d, var bool: b) = + assert(index_set(s) = index_set(d), + "alternative: index sets of third and fourth argument must be identical", + fzn_alternative_reif(s0, d0, s, d, b) + ); diff --git a/share/minizinc/std/among.mzn b/share/minizinc/std/among.mzn new file mode 100644 index 0000000..550750a --- /dev/null +++ b/share/minizinc/std/among.mzn @@ -0,0 +1,11 @@ +include "fzn_among.mzn"; +include "fzn_among_reif.mzn"; + +/** @group globals.counting + Requires exactly \a n variables in \a x to take one of the values in \a v. +*/ +predicate among(var int: n, array[int] of var int: x, set of int: v) = + fzn_among(n, x, v); + +predicate among_reif(var int: n, array[int] of var int: x, set of int: v, var bool: b) = + fzn_among_reif(n, x, v, b); diff --git a/share/minizinc/std/among_fn.mzn b/share/minizinc/std/among_fn.mzn new file mode 100644 index 0000000..970d3f9 --- /dev/null +++ b/share/minizinc/std/among_fn.mzn @@ -0,0 +1,9 @@ +include "among.mzn"; + +/** @group globals.counting + Returns the number of variables in \a x that take one of the values in \a v. +*/ +function var int: among(array[int] of var int:x, set of int:v) :: promise_total = + let { var 0..length(x): n; + constraint among(n,x,v); } + in n; diff --git a/share/minizinc/std/analyse_all_different.mzn b/share/minizinc/std/analyse_all_different.mzn new file mode 100644 index 0000000..430fe1a --- /dev/null +++ b/share/minizinc/std/analyse_all_different.mzn @@ -0,0 +1 @@ +predicate analyse_all_different(array[int] of var int: x) = true; diff --git a/share/minizinc/std/arg_max.mzn b/share/minizinc/std/arg_max.mzn new file mode 100644 index 0000000..b87b782 --- /dev/null +++ b/share/minizinc/std/arg_max.mzn @@ -0,0 +1,67 @@ +include "arg_max_int.mzn"; +include "arg_max_bool.mzn"; +include "arg_max_float.mzn"; + +/** @group globals + Returns the index of the maximum value in the array \a x. + When breaking ties the least index is returned. +*/ +function var $$E: arg_max(array[$$E] of var int: x) = + let { constraint length(x) > 0; } in arg_max_total(x); + +/** @group globals + Returns the index of the maximum value in the array \a x. + When breaking ties the least index is returned. +*/ +function var $$E: arg_max(array[$$E] of var bool: x) = + let { constraint length(x) > 0; } in arg_max_total(x); + +/** @group globals + Returns the index of the maximum value in the array \a x. + When breaking ties the least index is returned. +*/ +function var $$E: arg_max(array[$$E] of var float: x) = + let { constraint length(x) > 0; } in arg_max_total(x); + +function var $$E: arg_max_total(array[$$E] of var int: x) :: promise_total = + if length(x) = 0 then 0 else + let { var min(index_set(x)) .. max(index_set(x)): i; + constraint maximum_arg_int(x, i); } + in i endif; +function var $$E: arg_max_total(array[$$E] of var bool: x) :: promise_total = + if length(x) = 0 then 0 else + let { var min(index_set(x)) .. max(index_set(x)): i; + constraint maximum_arg_bool(x, i); } + in i endif; +function var $$E: arg_max_total(array[$$E] of var float: x) :: promise_total = + if length(x) = 0 then 0 else + let { var min(index_set(x)) .. max(index_set(x)): i; + constraint maximum_arg_float(x, i); } + in i endif; + +/** @group globals + Constrain \a i to be the index of the maximum value in the array \a x. + When breaking ties the least index is returned. + + Assumption: |\a x| > 0 +*/ +predicate maximum_arg(array[int] of var int: x, var int: i) = + maximum_arg_int(x, i); + +/** @group globals + Constrain \a i to be the index of the maximum value in the array \a x. + When breaking ties the least index is returned. + + Assumption: |\a x| > 0 +*/ +predicate maximum_arg(array[int] of var bool: x, var int: i) = + maximum_arg_bool(x, i); + +/** @group globals + Constrain \a i to be the index of the maximum value in the array \a x. + When breaking ties the least index is returned. + + Assumption: |\a x| > 0 +*/ +predicate maximum_arg(array[int] of var float: x, var int: i) = + maximum_arg_float(x, i); diff --git a/share/minizinc/std/arg_max_bool.mzn b/share/minizinc/std/arg_max_bool.mzn new file mode 100644 index 0000000..05119a6 --- /dev/null +++ b/share/minizinc/std/arg_max_bool.mzn @@ -0,0 +1,7 @@ +include "fzn_arg_max_bool.mzn"; + +predicate maximum_arg_bool(array[int] of var bool: x, var int: i) = + fzn_maximum_arg_bool(x, i); + +predicate maximum_arg_bool_reif(array[int] of var bool: x, var int: i, var bool: b) = + b <-> i=arg_max(x); diff --git a/share/minizinc/std/arg_max_float.mzn b/share/minizinc/std/arg_max_float.mzn new file mode 100644 index 0000000..e3ed700 --- /dev/null +++ b/share/minizinc/std/arg_max_float.mzn @@ -0,0 +1,7 @@ +include "fzn_arg_max_float.mzn"; + +predicate maximum_arg_float(array[int] of var float: x, var int: i) = + fzn_maximum_arg_float(x, i); + +predicate maximum_arg_float_reif(array[int] of var float: x, var int: i, var bool: b) = + b <-> i=arg_max(x); diff --git a/share/minizinc/std/arg_max_int.mzn b/share/minizinc/std/arg_max_int.mzn new file mode 100644 index 0000000..c34538a --- /dev/null +++ b/share/minizinc/std/arg_max_int.mzn @@ -0,0 +1,7 @@ +include "fzn_arg_max_int.mzn"; + +predicate maximum_arg_int(array[int] of var int: x, var int: i) = + fzn_maximum_arg_int(x, i); + +predicate maximum_arg_int_reif(array[int] of var int: x, var int: i, var bool: b) = + b <-> i=arg_max(x); diff --git a/share/minizinc/std/arg_min.mzn b/share/minizinc/std/arg_min.mzn new file mode 100644 index 0000000..4246d3e --- /dev/null +++ b/share/minizinc/std/arg_min.mzn @@ -0,0 +1,69 @@ +include "arg_min_int.mzn"; +include "arg_min_bool.mzn"; +include "arg_min_float.mzn"; + + +/** @group globals + Returns the index of the minimum value in the array \a x. + When breaking ties the least index is returned. +*/ +function var $$E: arg_min(array[$$E] of var int: x) = + let { constraint length(x) > 0; } in arg_min_total(x); + +/** @group globals + Returns the index of the minimum value in the array \a x. + When breaking ties the least index is returned. +*/ +function var $$E: arg_min(array[$$E] of var bool: x) = + let { constraint length(x) > 0; } in arg_min_total(x); + +/** @group globals + Returns the index of the minimum value in the array \a x. + When breaking ties the least index is returned. +*/ +function var $$E: arg_min(array[$$E] of var float: x) = + let { constraint length(x) > 0; } in arg_min_total(x); + +function var $$E: arg_min_total(array[$$E] of var int: x) :: promise_total = + if length(x) = 0 then 0 else + let { var min(index_set(x)) .. max(index_set(x)): i; + constraint minimum_arg_int(x, i); } + in i endif; +function var $$E: arg_min_total(array[$$E] of var bool: x) :: promise_total = + if length(x) = 0 then 0 else + let { var min(index_set(x)) .. max(index_set(x)): i; + constraint minimum_arg_bool(x, i); } + in i endif; +function var $$E: arg_min_total(array[$$E] of var float: x) :: promise_total = + if length(x) = 0 then 0 else + let { var min(index_set(x)) .. max(index_set(x)): i; + constraint minimum_arg_float(x, i); } + in i endif; + +/** @group globals + Constrain \a i to be the index of the minimum value in the array \a x. + When breaking ties the least index is returned. + + Assumption: |\a x| > 0 +*/ +predicate minimum_arg(array[int] of var int: x, var int: i) = + minimum_arg_int(x, i); + +/** @group globals + Constrain \a i to be the index of the minimum value in the array \a x. + When breaking ties the least index is returned. + + Assumption: |\a x| > 0 +*/ +predicate minimum_arg(array[int] of var bool: x, var int: i) = + minimum_arg_bool(x, i); + +/** @group globals + Constrain \a i to be the index of the minimum value in the array \a x. + When breaking ties the least index is returned. + + Assumption: |\a x| > 0 +*/ +predicate minimum_arg(array[int] of var float: x, var int: i) = + minimum_arg_float(x, i); + diff --git a/share/minizinc/std/arg_min_bool.mzn b/share/minizinc/std/arg_min_bool.mzn new file mode 100644 index 0000000..1a8dc83 --- /dev/null +++ b/share/minizinc/std/arg_min_bool.mzn @@ -0,0 +1,7 @@ +include "fzn_arg_min_bool.mzn"; + +predicate minimum_arg_bool(array[int] of var bool: x, var int: i) = + fzn_minimum_arg_bool(x, i); + +predicate minimum_arg_bool_reif(array[int] of var bool: x, var int: i, var bool: b) = + b <-> i=arg_min(x); diff --git a/share/minizinc/std/arg_min_float.mzn b/share/minizinc/std/arg_min_float.mzn new file mode 100644 index 0000000..ec66e18 --- /dev/null +++ b/share/minizinc/std/arg_min_float.mzn @@ -0,0 +1,7 @@ +include "fzn_arg_min_float.mzn"; + +predicate minimum_arg_float(array[int] of var float: x, var int: i) = + fzn_minimum_arg_float(x, i); + +predicate minimum_arg_float_reif(array[int] of var float: x, var int: i, var bool: b) = + b <-> i=arg_min(x); diff --git a/share/minizinc/std/arg_min_int.mzn b/share/minizinc/std/arg_min_int.mzn new file mode 100644 index 0000000..e0d63df --- /dev/null +++ b/share/minizinc/std/arg_min_int.mzn @@ -0,0 +1,7 @@ +include "fzn_arg_min_int.mzn"; + +predicate minimum_arg_int(array[int] of var int: x, var int: i) = + fzn_minimum_arg_int(x, i); + +predicate minimum_arg_int_reif(array[int] of var int: x, var int: i, var bool: b) = + b <-> i=arg_min(x); diff --git a/share/minizinc/std/arg_sort.mzn b/share/minizinc/std/arg_sort.mzn new file mode 100644 index 0000000..471190f --- /dev/null +++ b/share/minizinc/std/arg_sort.mzn @@ -0,0 +1,56 @@ +include "arg_sort_int.mzn"; +include "arg_sort_float.mzn"; +include "analyse_all_different.mzn"; + +/** @group globals.sort +Returns the permutation \a p which causes \a x to be in sorted order hence +\a x[\a p[\p i]] <= \a x[\a p[\p i+1]]. + +The permutation is the stable sort hence \a x[\a p[\p i]] = \a x[\a p[\p i+1]] \(\rightarrow\) \a p[\p i] < \a p[\p i+1]. +*/ +function array[int] of var int: arg_sort(array[int] of var int:x) :: promise_total = + if length(x) = 0 then [] + else + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + array[1..u-l+1] of var l..u: p; + constraint analyse_all_different(p); + constraint arg_sort_int(x,p); } + in p + endif; + + +/** @group globals.sort +Returns the permutation \a p which causes \a x to be in sorted order hence +\a x[\a p[\p i]] <= \a x[\a p[\p i+1]]. + +The permutation is the stable sort hence \a x[\a p[\p i]] = \a x[\a p[\p i+1]] \(\rightarrow\) \a p[\p i] < \a p[\p i+1]. +*/ +function array[int] of var int: arg_sort(array[int] of var float:x) :: promise_total = + if length(x) = 0 then [] + else + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + array[1..u-l+1] of var l..u: p; + constraint analyse_all_different(p); + constraint arg_sort_float(x,p); } + in p + endif; + +/** @group globals.sort +Constrains \a p to be the permutation which causes \a x to be in sorted order hence +\a x[\a p[\p i]] <= \a x[\a p[\p i+1]]. + +The permutation is the stable sort hence \a x[\a p[\p i]] = \a x[\a p[\p i+1]] \(\rightarrow\) \a p[\p i] < \a p[\p i+1]. +*/ +predicate arg_sort(array[int] of var int:x, + array[int] of var int:p) = fzn_arg_sort_int(x,p); + +/** @group globals.sort +Constrains \a p to be the permutation which causes \a x to be in sorted order hence +\a x[\a p[\p i]] <= \a x[\a p[\p i+1]]. + +The permutation is the stable sort hence \a x[\a p[\p i]] = \a x[\a p[\p i+1]] \(\rightarrow\) \a p[\p i] < \a p[\p i+1]. +*/ +predicate arg_sort(array[int] of var float:x, + array[int] of var int:p) = arg_sort_float(x,p); diff --git a/share/minizinc/std/arg_sort_float.mzn b/share/minizinc/std/arg_sort_float.mzn new file mode 100644 index 0000000..3d1d2c7 --- /dev/null +++ b/share/minizinc/std/arg_sort_float.mzn @@ -0,0 +1,17 @@ +include "fzn_arg_sort_float.mzn"; +include "fzn_arg_sort_float_reif.mzn"; + +predicate arg_sort_float(array[int] of var float:x, + array[int] of var int:p) = + assert(index_set(p) = 1..length(x), + "arg_sort_float: second argument must have index 1..length(first argument)", + fzn_arg_sort_float(x, p) + ); + +predicate arg_sort_float_reif(array[int] of var float:x, + array[int] of var int:p, + var bool: b) = + assert(index_set(p) = 1..length(x), + "arg_sort_float: second argument must have index 1..length(first argument)", + fzn_arg_sort_float_reif(x, p, b) + ); diff --git a/share/minizinc/std/arg_sort_int.mzn b/share/minizinc/std/arg_sort_int.mzn new file mode 100644 index 0000000..f83838f --- /dev/null +++ b/share/minizinc/std/arg_sort_int.mzn @@ -0,0 +1,17 @@ +include "fzn_arg_sort_int.mzn"; +include "fzn_arg_sort_int_reif.mzn"; + +predicate arg_sort_int(array[int] of var int:x, + array[int] of var int:p) = + assert(index_set(p) = 1..length(x), + "arg_sort_int: second argument must have index 1..length(first argument)", + fzn_arg_sort_int(x, p) + ); + +predicate arg_sort_int(array[int] of var int:x, + array[int] of var int:p, + var bool:b) = + assert(index_set(p) = 1..length(x), + "arg_sort_int: second argument must have index 1..length(first argument)", + fzn_arg_sort_int_reif(x, p, b) + ); diff --git a/share/minizinc/std/at_least.mzn b/share/minizinc/std/at_least.mzn new file mode 100644 index 0000000..8ffc501 --- /dev/null +++ b/share/minizinc/std/at_least.mzn @@ -0,0 +1,22 @@ +include "at_least_int.mzn"; +include "at_least_set.mzn"; + +/** @group globals.counting + Requires at least \a n variables in \a x to take the value \a v. +*/ +predicate at_least(int: n, array[int] of var int: x, int: v) = + at_least_int(n, x, v); + +/** @group globals.counting + Requires at least \a n variables in \a x to take the value \a v. +*/ +predicate at_least(int: n, array[int] of var set of int: x, set of int: v) = + at_least_set(n, x, v); + +% Synonyms for the above. + +predicate atleast(int: n, array[int] of var int: x, int: v) = + at_least_int(n, x, v); + +predicate atleast(int: n, array[int] of var set of int: x, set of int: v) = + at_least_set(n, x, v); diff --git a/share/minizinc/std/at_least_int.mzn b/share/minizinc/std/at_least_int.mzn new file mode 100644 index 0000000..515117c --- /dev/null +++ b/share/minizinc/std/at_least_int.mzn @@ -0,0 +1,12 @@ +include "fzn_at_least_int.mzn"; +include "fzn_at_least_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at least 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate at_least_int(int: n, array[int] of var int: x, int: v) = + fzn_at_least_int(n, x, v); + +predicate at_least_int_reif(int: n, array[int] of var int: x, int: v, var bool: b) = + fzn_at_least_int_reif(n, x, v, b); diff --git a/share/minizinc/std/at_least_set.mzn b/share/minizinc/std/at_least_set.mzn new file mode 100644 index 0000000..98b5a9f --- /dev/null +++ b/share/minizinc/std/at_least_set.mzn @@ -0,0 +1,12 @@ +include "fzn_at_least_set.mzn"; +include "fzn_at_least_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at least 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate at_least_set(int: n, array[int] of var set of int: x, set of int: v) = + fzn_at_least_set(n, x, v); + +predicate at_least_set_reif(int: n, array[int] of var set of int: x, set of int: v, var bool: b) = + fzn_at_least_set_reif(n, x, v, b); diff --git a/share/minizinc/std/at_most.mzn b/share/minizinc/std/at_most.mzn new file mode 100644 index 0000000..c542755 --- /dev/null +++ b/share/minizinc/std/at_most.mzn @@ -0,0 +1,22 @@ +include "at_most_int.mzn"; +include "at_most_set.mzn"; + +/** @group globals.counting + Requires at most \a n variables in \a x to take the value \a v. +*/ +predicate at_most(int: n, array[int] of var int: x, int: v) = + at_most_int(n, x, v); + +/** @group globals.counting + Requires at most \a n variables in \a x to take the value \a v. +*/ +predicate at_most(int: n, array[int] of var set of int: x, set of int: v) = + at_most_set(n, x, v); + +% Synonyms for the above. + +predicate atmost(int: n, array[int] of var int: x, int: v) = + at_most_int(n, x, v); + +predicate atmost(int: n, array[int] of var set of int: x, set of int: v) = + at_most_set(n, x, v); diff --git a/share/minizinc/std/at_most1.mzn b/share/minizinc/std/at_most1.mzn new file mode 100644 index 0000000..67aaa49 --- /dev/null +++ b/share/minizinc/std/at_most1.mzn @@ -0,0 +1,14 @@ +include "fzn_at_most1.mzn"; +include "fzn_at_most1_reif.mzn"; + +/** @group globals.counting + Requires that each pair of sets in \a s overlap in at most one element. +*/ +predicate at_most1(array[int] of var set of int: s) = + fzn_at_most1(s); + +predicate at_most1_reif(array[int] of var set of int: s, var bool: b) = + fzn_at_most1_reif(s, b); + +% Synonym for the above. +predicate atmost1(array[int] of var set of int: s) = fzn_at_most1(s); diff --git a/share/minizinc/std/at_most_int.mzn b/share/minizinc/std/at_most_int.mzn new file mode 100644 index 0000000..df538ec --- /dev/null +++ b/share/minizinc/std/at_most_int.mzn @@ -0,0 +1,12 @@ +include "fzn_at_most_int.mzn"; +include "fzn_at_most_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at most 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate at_most_int(int: n, array[int] of var int: x, int: v) = + fzn_at_most_int(n, x, v); + +predicate at_most_int_reif(int: n, array[int] of var int: x, int: v, var bool: b) = + fzn_at_most_int_reif(n, x, v, b); diff --git a/share/minizinc/std/at_most_set.mzn b/share/minizinc/std/at_most_set.mzn new file mode 100644 index 0000000..61f339e --- /dev/null +++ b/share/minizinc/std/at_most_set.mzn @@ -0,0 +1,13 @@ +include "fzn_at_most_set.mzn"; +include "fzn_at_most_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at most 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate at_most_set(int: n, array[int] of var set of int: x, set of int: v) = + fzn_at_most_set(n, x, v); + +predicate at_most_set_reif(int: n, array[int] of var set of int: x, set of int: v, var bool: b) = + fzn_at_most_set_reif(n, x, v, b); + diff --git a/share/minizinc/std/atleast.mzn b/share/minizinc/std/atleast.mzn new file mode 100644 index 0000000..7ccf189 --- /dev/null +++ b/share/minizinc/std/atleast.mzn @@ -0,0 +1,5 @@ +% The actual definitions are in at_least.mzn. +% This file is used to handle the case where users include +% "atleast.mzn"; +% +include "at_least.mzn"; diff --git a/share/minizinc/std/atmost.mzn b/share/minizinc/std/atmost.mzn new file mode 100644 index 0000000..b29b9ac --- /dev/null +++ b/share/minizinc/std/atmost.mzn @@ -0,0 +1,5 @@ +% The actual definitions are in atmost.mzn. +% This file is used to handle the case where users include +% "atmost.mzn"; +% +include "at_most.mzn"; diff --git a/share/minizinc/std/atmost1.mzn b/share/minizinc/std/atmost1.mzn new file mode 100644 index 0000000..1697812 --- /dev/null +++ b/share/minizinc/std/atmost1.mzn @@ -0,0 +1,5 @@ +% The actual definitions are in at_most1.mzn. +% This file is used to handle the case where users include +% "atmost1.mzn"; +% +include "at_most1.mzn"; diff --git a/share/minizinc/std/bin_packing.mzn b/share/minizinc/std/bin_packing.mzn new file mode 100644 index 0000000..e0217bc --- /dev/null +++ b/share/minizinc/std/bin_packing.mzn @@ -0,0 +1,36 @@ +include "fzn_bin_packing.mzn"; +include "fzn_bin_packing_reif.mzn"; + +/** @group globals.packing + Requires that each item \p i with weight \a w[\p i], be put into \a bin[\p i] such + that the sum of the weights of the items in each bin does not exceed the + capacity \a c. + + Assumptions: + - forall \p i, \a w[\p i] >=0 + - \a c >=0 +*/ +predicate bin_packing(int: c, + array[int] of var int: bin, + array[int] of int: w) = + assert(index_set(bin) == index_set(w), + "bin_packing: the bin and weight arrays must have identical index sets", + assert(lb_array(w) >= 0, + "bin_packing: the weights must be non-negative", + assert(c >= 0, "bin_packing: capacity must be non-negative", + fzn_bin_packing(c, bin, w) + ))); + +predicate bin_packing_reif(int: c, + array[int] of var int: bin, + array[int] of int: w, + var bool: b) = + assert(index_set(bin) == index_set(w), + "bin_packing: the bin and weight arrays must have identical index sets", + assert(lb_array(w) >= 0, + "bin_packing: the weights must be non-negative", + assert(c >= 0, "bin_packing: capacity must be non-negative", + fzn_bin_packing_reif(c, bin, w, b) + ))); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/bin_packing_capa.mzn b/share/minizinc/std/bin_packing_capa.mzn new file mode 100644 index 0000000..a40373a --- /dev/null +++ b/share/minizinc/std/bin_packing_capa.mzn @@ -0,0 +1,38 @@ +include "fzn_bin_packing_capa.mzn"; +include "fzn_bin_packing_capa_reif.mzn"; + +/** @group globals.packing + Requires that each item \p i with weight \a w[\p i], be put into \a bin[\p i] such + that the sum of the weights of the items in each bin \p b does not exceed the + capacity \a c[\p b]. + + Assumptions: + - forall \p i, \a w[\p i] >=0 + - forall \p b, \a c[\p b] >=0 +*/ +predicate bin_packing_capa(array[int] of int: c, + array[int] of var int: bin, + array[int] of int: w) = + assert(index_set(bin) = index_set(w), + "bin_packing_capa: the bin and weight arrays must have identical index sets", + assert(lb_array(w) >= 0, + "bin_packing_capa: the weights must be non-negative", + assert(lb_array(c) >= 0, + "bin_packing_capa: the capacities must be non-negative", + fzn_bin_packing_capa(c, bin, w) + ))); + +predicate bin_packing_capa_reif(array[int] of int: c, + array[int] of var int: bin, + array[int] of int: w, + var bool: b) = + assert(index_set(bin) = index_set(w), + "bin_packing_capa: the bin and weight arrays must have identical index sets", + assert(lb_array(w) >= 0, + "bin_packing_capa: the weights must be non-negative", + assert(lb_array(c) >= 0, + "bin_packing_capa: the capacities must be non-negative", + fzn_bin_packing_capa_reif(c, bin, w, b) + ))); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/bin_packing_load.mzn b/share/minizinc/std/bin_packing_load.mzn new file mode 100644 index 0000000..3c6f4cd --- /dev/null +++ b/share/minizinc/std/bin_packing_load.mzn @@ -0,0 +1,33 @@ +include "fzn_bin_packing_load.mzn"; +include "fzn_bin_packing_load_reif.mzn"; + +/** @group globals.packing + Requires that each item \p i with weight \a w[\p i], be put into \a bin[\p i] such + that the sum of the weights of the items in each bin \p b is equal to + \a load[\p b]. + + Assumptions: + - forall \p i, \a w[\p i] >=0 +*/ +predicate bin_packing_load(array[int] of var int: load, + array[int] of var int: bin, + array[int] of int: w) = + assert(index_set(bin) == index_set(w), + "bin_packing_load: the bin and weight arrays must have identical index sets", + assert(lb_array(w) >= 0, + "bin_packing_load: the weights must be non-negative", + fzn_bin_packing_load(load, bin, w) + )); + +predicate bin_packing_load_reif(array[int] of var int: load, + array[int] of var int: bin, + array[int] of int: w, + var bool: b) = + assert(index_set(bin) == index_set(w), + "bin_packing_load: the bin and weight arrays must have identical index sets", + assert(lb_array(w) >= 0, + "bin_packing_load: the weights must be non-negative", + fzn_bin_packing_load_reif(load, bin, w, b) + )); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/bin_packing_load_fn.mzn b/share/minizinc/std/bin_packing_load_fn.mzn new file mode 100644 index 0000000..f310e5b --- /dev/null +++ b/share/minizinc/std/bin_packing_load_fn.mzn @@ -0,0 +1,16 @@ +include "bin_packing_load.mzn"; + +/** @group globals.packing + Returns the load of each bin resulting from packing each item \p i with + weight \a w[\p i] into \a bin[\p i], where the load is defined as + the sum of the weights of the items in each bin. + + Assumptions: + + - forall \p i, \a w[\p i] >=0 +*/ +function array[int] of var int: bin_packing_load(array[int] of var int: bin, + array[int] of int: w) :: promise_total = + let { array[dom_bounds_array(bin)] of var 0..sum(w): load; + constraint bin_packing_load(load,bin,w); } + in load; diff --git a/share/minizinc/std/bounded_path.mzn b/share/minizinc/std/bounded_path.mzn new file mode 100644 index 0000000..f5f835f --- /dev/null +++ b/share/minizinc/std/bounded_path.mzn @@ -0,0 +1,101 @@ +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be a path from \a s to \a t of weight \a K. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a w is the weight of each edge + \a s is the source node (which may be variable) + \a t is the dest node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the cost of the path +*/ +predicate bounded_dpath(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + assert(index_set(from) = 1..E,"bounded_dpath: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"bounded_dpath: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"bounded_dpath: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"bounded_dpath: index set of es must be 1..\(E)") /\ + assert(index_set(w) = 1..E,"bounded_dpath: index set of w must be 1..\(E)") /\ + fzn_bounded_dpath(N,E,from,to,w,s,t,ns,es,K); + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be a path from \a s to \a t of weight \a K. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a w is the weight of each edge + \a s is the source node (which may be variable) + \a t is the dest node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the cost of the path +*/ +predicate bounded_dpath(array[int] of $$N: from, array[int] of $$N: to, array[int] of int: w, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var int: K) = + assert(index_set(from) = index_set(to),"bounded_dpath: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"bounded_dpath: index set of from and es must be identical") /\ + assert(index_set(w) = index_set(es),"bounded_dpath: index set of w and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"bounded_dpath: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"bounded_dpath: nodes in to must be in index set of ns") /\ + fzn_bounded_dpath(from,to,w,s,t,ns,es,K); + + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given undirected graph to be a path from \a s to \a t of weight \a K. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a w is the weight of each edge + \a s is the source node (which may be variable) + \a t is the dest node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the cost of the path +*/ +predicate bounded_path(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + assert(index_set(from) = 1..E,"bounded_path: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"bounded_path: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"bounded_path: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"bounded_path: index set of es must be 1..\(E)") /\ + assert(index_set(w) = 1..E,"bounded_path: index set of w must be 1..\(E)") /\ + fzn_bounded_path(N,E,from,to,w,s,t,ns,es,K); + + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given undirected graph to be a path from \a s to \a t of weight \a K. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a w is the weight of each edge + \a s is the source node (which may be variable) + \a t is the dest node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the cost of the path +*/ +predicate bounded_path(array[int] of $$N: from, array[int] of $$N: to, array[int] of int: w, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var int: K) = + assert(index_set(from) = index_set(to),"bounded_path: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"bounded_path: index set of from and es must be identical") /\ + assert(index_set(w) = index_set(es),"bounded_path: index set of w and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"bounded_path: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"bounded_path: nodes in to must be in index set of ns") /\ + fzn_bounded_path(from,to,w,s,t,ns,es,K); + +include "fzn_bounded_path_int.mzn"; +include "fzn_bounded_path_int_reif.mzn"; +include "fzn_bounded_path_enum.mzn"; +include "fzn_bounded_path_enum_reif.mzn"; +include "fzn_bounded_dpath_int.mzn"; +include "fzn_bounded_dpath_int_reif.mzn"; +include "fzn_bounded_dpath_enum.mzn"; +include "fzn_bounded_dpath_enum_reif.mzn"; + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/builtins.mzn b/share/minizinc/std/builtins.mzn new file mode 100644 index 0000000..881a899 --- /dev/null +++ b/share/minizinc/std/builtins.mzn @@ -0,0 +1,2750 @@ +include "flatzinc_builtins.mzn"; +include "mzncc_builtins.mzn"; + +/*** + @groupdef builtins Builtins + + These functions and predicates define built-in operations of the MiniZinc language. + +*/ + +/*** + @groupdef builtins.compare Comparison Builtins + + These builtins implement comparison operations. +*/ + +/** @group builtins.compare Return if \a x is less than \a y */ +function bool: '<'( $T: x, $T: y); +/** @group builtins.compare Return if \a x is less than \a y */ +function var bool: '<'(var $T: x,var $T: y); +/** @group builtins.compare Return if \a x is greater than \a y */ +function bool: '>'( $T: x, $T: y); +/** @group builtins.compare Return if \a x is greater than \a y */ +function var bool: '>'(var $T: x,var $T: y); +/** @group builtins.compare Return if \a x is less than or equal to \a y */ +function bool: '<='( $T: x, $T: y); +/** @group builtins.compare Return if \a x is less than or equal to \a y */ +function var bool: '<='(var $T: x, var $T: y); +/** @group builtins.compare Return if \a x is greater than or equal to \a y */ +function bool: '>='( $T: x, $T: y); +/** @group builtins.compare Return if \a x is greater than or equal to \a y */ +function var bool: '>='(var $T: x,var $T: y); +/** @group builtins.compare Return if \a x is equal to \a y */ +function bool: '='( $T: x, $T: y); +/** @group builtins.compare Return if \a x is equal to \a y */ +function bool: '='(opt $T: x, opt $T: y); +/** @group builtins.compare Return if \a x is equal to \a y */ +function var bool: '='(var $T: x,var $T: y); +/** @group builtins.compare Return if \a x is equal to \a y */ +function var bool: '='(var opt $T: x,var opt $T: y); + +/** @group builtins.compare Return if \a x is not equal to \a y */ +function bool: '!='( $T: x, $T: y); +/** @group builtins.compare Return if \a x is not equal to \a y */ +function var bool: '!='(var $T: x, var $T: y); + +% Special case comparison operators for integer variable and float constant +function var bool: '<='(var int: x, float: y) = (x <= floor(y)); +function var bool: '>='(var int: x, float: y) = (x >= ceil(y)); +function var bool: '<='(float: x, var int: y) = (y >= ceil(x)); +function var bool: '>='(float: x, var int: y) = (y <= floor(x)); + +function var bool: '<'(var int: x, float: y) = (x <= ceil(y)-1); +function var bool: '>'(float: x, var int: y) = (y <= ceil(x)-1); +function var bool: '>'(var int: x, float: y) = (x >= floor(y)+1); +function var bool: '<'(float: x, var int: y) = (y >= floor(x)+1); + +function var bool: '='(var int: x, float: y) = + if ceil(y)=floor(y) then x=ceil(y) else false endif; +function var bool: '='(float: x, var int: y) = + if ceil(x)=floor(x) then y=ceil(x) else false endif; + +function var bool: '!='(var int: x, float: y) = + if ceil(y)=floor(y) then x != ceil(y) else true endif; +function var bool: '!='(float: x, var int: y) = + if ceil(x)=floor(x) then y != ceil(x) else true endif; + +function bool: '<='(int: x, float: y) = (x <= floor(y)); +function bool: '>='(int: x, float: y) = (x >= ceil(y)); +function bool: '<='(float: x, int: y) = (y >= ceil(x)); +function bool: '>='(float: x, int: y) = (y <= floor(x)); + +function bool: '<'(int: x, float: y) = (x <= ceil(y)-1); +function bool: '>'(float: x, int: y) = (y <= ceil(x)-1); +function bool: '>'(int: x, float: y) = (x >= floor(y)+1); +function bool: '<'(float: x, int: y) = (y >= floor(x)+1); + +function bool: '='(int: x, float: y) = + if ceil(y)=floor(y) then x=ceil(y) else false endif; +function bool: '='(float: x, int: y) = + if ceil(x)=floor(x) then y=ceil(x) else false endif; + +function bool: '!='(int: x, float: y) = + if ceil(y)=floor(y) then x != ceil(y) else true endif; +function bool: '!='(float: x, int: y) = + if ceil(x)=floor(x) then y != ceil(x) else true endif; + + +/** @group builtins.compare Return if array \a x is lexicographically smaller than array \a y */ +function bool: '<'(array[$U] of $T: x,array[$U] of $T: y); +/** @group builtins.compare Return if array \a x is lexicographically smaller than array \a y */ +function var bool: '<'(array[$U] of var $T: x,array[$U] of var $T: y); +/** @group builtins.compare Return if array \a x is lexicographically greater than array \a y */ +function bool: '>'(array[$U] of $T: x,array[$U] of $T: y); +/** @group builtins.compare Return if array \a x is lexicographically greater than array \a y */ +function var bool: '>'(array[$U] of var $T: x,array[$U] of var $T: y); +/** @group builtins.compare Return if array \a x is lexicographically smaller than or equal to array \a y */ +function bool: '<='(array[$U] of $T: x,array[$U] of $T: y); +/** @group builtins.compare Return if array \a x is lexicographically smaller than or equal to array \a y */ +function var bool: '<='(array[$U] of var $T: x,array[$U] of var $T: y); +/** @group builtins.compare Return if array \a x is lexicographically greater than or equal to array \a y */ +function bool: '>='(array[$U] of $T: x,array[$U] of $T: y); +function var bool: '>='(array[$U] of var $T: x,array[$U] of var $T: y); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function bool: '='(array[$T] of int: x,array[$T] of int: y) = + let { + array[int] of int: xx = array1d(x); + array[int] of int: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function var bool: '='(array[$T] of var int: x,array[$T] of var int: y) = + let { + array[int] of var int: xx = array1d(x); + array[int] of var int: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function bool: '='(array[$T] of bool: x,array[$T] of bool: y) = + let { + array[int] of bool: xx = array1d(x); + array[int] of bool: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function var bool: '='(array[$T] of var bool: x,array[$T] of var bool: y) = + let { + array[int] of var bool: xx = array1d(x); + array[int] of var bool: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function bool: '='(array[$T] of set of int: x,array[$T] of set of int: y) = + let { + array[int] of set of int: xx = array1d(x); + array[int] of set of int: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function var bool: '='(array[$T] of var set of int: x,array[$T] of var set of int: y) = + let { + array[int] of var set of int: xx = array1d(x); + array[int] of var set of int: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function bool: '='(array[$T] of float: x,array[$T] of float: y) = + let { + array[int] of float: xx = array1d(x); + array[int] of float: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function var bool: '='(array[$T] of var float: x,array[$T] of var float: y) = + let { + array[int] of var float: xx = array1d(x); + array[int] of var float: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is not equal to array \a y */ +function bool: '!='(array[$U] of $T: x,array[$U] of $T: y); +/** @group builtins.compare Return if array \a x is not equal to array \a y */ +function var bool: '!='(array[$U] of var $T: x,array[$U] of var $T: y); + +/*** + @groupdef builtins.arithmetic Arithmetic Builtins + + These builtins implement arithmetic operations. +*/ + +/** @group builtins.arithmetic Return \a x + \a y */ +function int: '+'( int: x, int: y); +/** @group builtins.arithmetic Return \a x + \a y */ +function var int: '+'(var int: x, var int: y); +/** @group builtins.arithmetic Return \a x + \a y */ +function float: '+'( float: x, float: y); +/** @group builtins.arithmetic Return \a x + \a y */ +function var float: '+'(var float: x,var float: y); +/** @group builtins.arithmetic Return \a x - \a y */ +function int: '-'( int: x, int: y); +/** @group builtins.arithmetic Return \a x - \a y */ +function var int: '-'(var int: x, var int: y); +/** @group builtins.arithmetic Return \a x - \a y */ +function float: '-'( float: x, float: y); +/** @group builtins.arithmetic Return \a x - \a y */ +function var float: '-'(var float: x,var float: y); +/** @group builtins.arithmetic Return \a x * \a y */ +function int: '*'( int: x, int: y); +/** @group builtins.arithmetic Return \a x * \a y */ +function var int: '*'(var int: x, var int: y); +/** @group builtins.arithmetic Return \a x ^ \a y */ +function int: '^'( int: x, int: y); +/** @group builtins.arithmetic Return \a x ^ \a y */ +function var int: '^'(var int: x, var int: y); +/** @group builtins.arithmetic Return \a x * \a y */ +function float: '*'( float: x, float: y); +/** @group builtins.arithmetic Return \a x * \a y */ +function var float: '*'(var float: x,var float: y); +/** @group builtins.arithmetic Return \a x ^ \a y */ +function float: '^'( float: x, float: y); +/** @group builtins.arithmetic Return \a x ^ \a y */ +function var float: '^'(var float: x,var float: y); +/** @group builtins.arithmetic Return negative \a x */ +function int: '-'( int: x); +/** @group builtins.arithmetic Return negative \a x */ +function var int: '-'(var int: x); +/** @group builtins.arithmetic Return negative \a x */ +function float: '-'( float: x); +/** @group builtins.arithmetic Return negative \a x */ +function var float: '-'(var float: x); + +/** @group builtins.arithmetic Return result of integer division \a x / \a y */ +function int: 'div'(int: x,int: y); + +/** @group builtins.arithmetic Return result of integer division \a x / \a y */ +function var int: 'div'(var int: x,var int: y) = + if mzn_in_root_context(y) \/ not (0 in dom(y)) then div_t(x,y) else + let { constraint y != 0 } in div_mt(x,y) endif; + +/** @group builtins.arithmetic Return remainder of integer division \a x % \a y */ +function int: 'mod'(int: x,int: y); + +/** @group builtins.arithmetic Return remainder of integer division \a x % \a y */ +function var int: 'mod'(var int: x,var int: y) = + if mzn_in_root_context(y) \/ not (0 in dom(y)) then mod_t(x,y) else + let { constraint y != 0 } in mod_mt(x,y) endif; + +/** @group builtins.arithmetic Return result of floating point division \a x / \a y */ +function float: '/'( float: x, float: y); +/** @group builtins.arithmetic Return result of floating point division \a x / \a y */ +function var float: '/'(var float: x,var float: y) = + if mzn_in_root_context(y) \/ lb(y) > 0.0 \/ ub(y) < 0.0 then fldiv_t(x,y) else + let { constraint y != 0.0 } in fldiv_mt(x,y) endif; + +/** @group builtins.arithmetic Return sum of elements in array \a x */ +function int: sum(array[$T] of int: x); +/** @group builtins.arithmetic Return sum of elements in array \a x */ +function var int: sum(array[$T] of var int: x); +/** @group builtins.arithmetic Return sum of elements in array \a x */ +function float: sum(array[$T] of float: x); +/** @group builtins.arithmetic Return sum of elements in array \a x */ +function var float: sum(array[$T] of var float: x); + +/** @group builtins.arithmetic Return product of elements in array \a x */ +function int: product(array[$T] of int: x); +/** @group builtins.arithmetic Return product of elements in array \a x */ +function var int: product(array[$T] of var int: x) = + product_rec(array1d(x)); + +/** @group builtins.arithmetic Return product of elements in array \a x */ +function float: product(array[$T] of float: x); +/** @group builtins.arithmetic Return product of elements in array \a x */ +function var float: product(array[$T] of var float: x) = + product_rec(array1d(x)); + +% /** @group builtins.arithmetic Return minimum of \a x and \a y */ +% function $T: min( $T: x, $T: y); +% /** @group builtins.arithmetic Return minimum of elements in array \a x */ +% function $T: min(array[$U] of par $T: x); +% /** @group builtins.arithmetic Return maximum of \a x and \a y */ +% function $T: max( $T: x, $T: y); +% /** @group builtins.arithmetic Return maximum of elements in array \a x */ +% function $T: max(array[$U] of $T: x); +% /** @group builtins.arithmetic Return minimum of elements in set \a x */ +% function $$E: min(set of $$E: x); +% /** @group builtins.arithmetic Return maximum of elements in set \a x */ +% function $$E: max(set of $$E: x); + +/** @group builtins.arithmetic Return maximum of \a x and \a y */ +function var int: max(var int: x, var int: y) :: promise_total = + let { var max(lb(x),lb(y))..max(ub(x),ub(y)): m ::is_defined_var; + constraint int_max(x,y,m) ::defines_var(m); + } in m; + +/** @group builtins.arithmetic Return maximum of elements in array \a x */ +function var int: max(array[$U] of var int: x) = + let { + array[int] of var int: xx = array1d(x); + constraint length(x) >= 1; + } in max_t(xx); + +/** @group builtins.arithmetic Return minimum of \a x and \a y */ +function var int: min(var int: x, var int: y) :: promise_total = + let { var min(lb(x),lb(y))..min(ub(x),ub(y)): m ::is_defined_var; + constraint int_min(x,y,m) ::defines_var(m); + } in m; + +/** @group builtins.arithmetic Return minimum of elements in array \a x */ +function var int: min(array[$U] of var int: x) = + let { + array[int] of var int: xx = array1d(x); + constraint length(x) >= 1; + } in min_t(xx); + +% Floating point min and max +% TODO: add bounds reasoning + +/** @group builtins.arithmetic Return maximum of \a x and \a y */ +function var float: max(var float: x, var float: y) :: promise_total = + let { var float: m ::is_defined_var; + constraint float_max(x,y,m) ::defines_var(m); + } in m; + +/** @group builtins.arithmetic Return maximum of elements in array \a x */ +function var float: max(array[$U] of var float: x) = + let { + array[int] of var float: xx = array1d(x); + constraint length(x) >= 1; + } in max_t(xx); + +/** @group builtins.arithmetic Return minimum of \a x and \a y */ +function var float: min(var float: x, var float: y) :: promise_total = + let { var float: m ::is_defined_var; + constraint float_min(x,y,m) ::defines_var(m); + } in m; + +/** @group builtins.arithmetic Return minimum of elements in array \a x */ +function var float: min(array[$U] of var float: x) = + let { + array[int] of var float: xx = array1d(x); + constraint length(x) >= 1; + } in min_t(xx); + + +/** @group builtins.arithmetic Return index of minimum of elements in array \a x */ +function $$E: arg_min(array[$$E] of int: x); +/** @group builtins.arithmetic Return index of minimum of elements in array \a x */ +function $$E: arg_min(array[$$E] of float: x); +/** @group builtins.arithmetic Return index of maximum of elements in array \a x */ +function $$E: arg_max(array[$$E] of int: x); +/** @group builtins.arithmetic Return index of maximum of elements in array \a x */ +function $$E: arg_max(array[$$E] of float: x); + +/** @group builtins.arithmetic Return absolute value of \a x */ +function int: abs(int: x); + +/** @group builtins.arithmetic Return absolute value of \a x */ +function var int: abs(var int: x) :: promise_total = + if has_bounds(x) /\ lb(x) >= 0 then x + elseif has_bounds(x) /\ ub(x) < 0 then -x else + let { var 0..max(-lb(x),ub(x)): m ::is_defined_var; + constraint int_abs(x,m) ::defines_var(m); + } in m + endif; + +/** @group builtins.arithmetic Return absolute value of \a x */ +function float: abs(float: x); +/** @group builtins.arithmetic Return absolute value of \a x */ +function var float: abs(var float: x) :: promise_total = + if has_bounds(x) then + if lb(x) >= 0.0 then x else + let { var 0.0..max(-lb(x),ub(x)): m ::is_defined_var; + constraint float_abs(x,m) ::defines_var(m); + } in m + endif + else + let { var float: m ::is_defined_var; + constraint m >= 0.0; + constraint float_abs(x,m) ::defines_var(m); + } in m + endif; + +/** @group builtins.arithmetic Return \(\sqrt{\a x}\) */ +function float: sqrt(float: x); +/** @group builtins.arithmetic Return \(\sqrt{\a x}\) */ +function var float: sqrt(var float: x) = + let { + constraint x >= 0.0; + } in sqrt_t(x); + +function var float: sqrt_t(var float: x) ::promise_total = + let { + var float: r; + var float: xx; + constraint x < 0.0 -> xx = 1.0; + constraint x < 0.0 \/ xx = x; + constraint float_sqrt(xx,r); + } in r; + +/** @group builtins.arithmetic Return \(\a x ^ {\a y}\) */ +function int: pow(int: x, int: y); + +/** @group builtins.arithmetic Return \(\a x ^ {\a y}\) */ +function var int: pow(var int: x, var int: y) = + let { + int: yy = if is_fixed(y) then fix(y) else -1 endif; + } in + if yy = 0 then 1 + elseif yy = 1 then x else + let { var int: r ::is_defined_var; + constraint if is_fixed(y) then int_pow_fixed(x,fix(y),r) ::defines_var(r) else int_pow(x,y,r) ::defines_var(r) endif; + } in r + endif; + + +/** @group builtins.arithmetic Return \(\a x ^ {\a y}\) */ +function float: pow(float: x, float: y); + +/** @group builtins.arithmetic Return \(\a x ^ {\a y}\) */ +function var float: pow(var float: x, var float: y) = + let { + float: yy = if is_fixed(y) then fix(y) else -1.0 endif + } in + if yy = 0.0 then 1.0 + elseif yy = 1.0 then x else + let { var float: r ::is_defined_var; + constraint float_pow(x,y,r) ::defines_var(r); + } in r + endif; + +/*** + @groupdef builtins.explog Exponential and logarithmic builtins + + These builtins implement exponential and logarithmic functions. +*/ + +/** @group builtins.explog Return \(e ^ {\a x}\) */ +function float: exp(float: x); +/** @group builtins.explog Return \(e ^ {\a x}\) */ +function var float: exp(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_exp(x,r) ::defines_var(r); + } in r; + +/** @group builtins.explog Return \(\ln \a x\) */ +function float: ln(float: x); +/** @group builtins.explog Return \(\ln \a x\) */ +function var float: ln(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_ln(x,r) ::defines_var(r); + } in r; + +/** @group builtins.explog Return \(\log_{10} \a x\) */ +function float: log10(float: x); +/** @group builtins.explog Return \(\log_{10} \a x\) */ +function var float: log10(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_log10(x,r) ::defines_var(r); + } in r; + +/** @group builtins.explog Return \(\log_{2} \a x\) */ +function float: log2(float: x); +/** @group builtins.explog Return \(\log_{2} \a x\) */ +function var float: log2(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_log2(x,r) ::defines_var(r); + } in r; + +/** @group builtins.explog Return \(\log_{\a x} \a y\) */ +function float: log(float: x, float: y); + +/*** + @groupdef builtins.trigonometric Trigonometric functions + + These builtins implement the standard trigonometric functions. +*/ + +/** @group builtins.trigonometric Return \(\sin \a x\) */ +function float: sin(float: x); +/** @group builtins.trigonometric Return \(\sin \a x\) */ +function var float: sin(var float: x) ::promise_total = + let { + var -1.0..1.0: r ::is_defined_var; + constraint float_sin(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\cos \a x\) */ +function float: cos(float: x); +/** @group builtins.trigonometric Return \(\cos \a x\) */ +function var float: cos(var float: x) ::promise_total = + let { + var -1.0..1.0: r ::is_defined_var; + constraint float_cos(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\tan \a x\) */ +function float: tan(float: x); +/** @group builtins.trigonometric Return \(\tan \a x\) */ +function var float: tan(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_tan(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\mbox{asin}\ \a x\) */ +function float: asin(float: x); +/** @group builtins.trigonometric Return \(\mbox{asin}\ \a x\) */ +function var float: asin(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_asin(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\mbox{acos}\ \a x\) */ +function float: acos(float: x); +/** @group builtins.trigonometric Return \(\mbox{acos}\ \a x\) */ +function var float: acos(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_acos(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\mbox{atan}\ \a x\) */ +function float: atan(float: x); +/** @group builtins.trigonometric Return \(\mbox{atan}\ \a x\) */ +function var float: atan(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_atan(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\sinh \a x\) */ +function float: sinh(float: x); +/** @group builtins.trigonometric Return \(\sinh \a x\) */ +function var float: sinh(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_sinh(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\cosh \a x\) */ +function float: cosh(float: x); +/** @group builtins.trigonometric Return \(\cosh \a x\) */ +function var float: cosh(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_cosh(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\tanh \a x\) */ +function float: tanh(float: x); +/** @group builtins.trigonometric Return \(\tanh \a x\) */ +function var float: tanh(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_tanh(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\mbox{asinh}\ \a x\) */ +function float: asinh(float: x); +/** @group builtins.trigonometric Return \(\mbox{asinh}\ \a x\) */ +function var float: asinh(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_asinh(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\mbox{acosh}\ \a x\) */ +function float: acosh(float: x); +/** @group builtins.trigonometric Return \(\mbox{acosh}\ \a x\) */ +function var float: acosh(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_acosh(x,r) ::defines_var(r); + } in r; + +/** @group builtins.trigonometric Return \(\mbox{atanh}\ \a x\) */ +function float: atanh(float: x); +/** @group builtins.trigonometric Return \(\mbox{atanh}\ \a x\) */ +function var float: atanh(var float: x) ::promise_total = + let { + var float: r ::is_defined_var; + constraint float_atanh(x,r) ::defines_var(r); + } in r; + + +/*** + @groupdef builtins.logic Logical operations + + Logical operations are the standard operators of Boolean logic. +*/ + +/** @group builtins.logic Return truth value of \a x ∧ \a y */ +function bool: '/\'( bool: x, bool: y); +/** @group builtins.logic Return truth value of \a x ∧ \a y */ +function var bool: '/\'(var bool: x, var bool: y); +/** @group builtins.logic Return truth value of \a x ∨ \a y */ +function bool: '\/'( bool: x, bool: y); +/** @group builtins.logic Return truth value of \a x ∨ \a y */ +function var bool: '\/'(var bool: x, var bool: y); +/** @group builtins.logic Return truth value of \a x implies \a y */ +function bool: '->'( bool: x, bool: y); +/** @group builtins.logic Return truth value of \a x implies \a y */ +function var bool: '->'(var bool: x, var bool: y); +/** @group builtins.logic Return truth value of \a y implies \a x */ +function bool: '<-'( bool: x, bool: y); +/** @group builtins.logic Return truth value of \a y implies \a x */ +function var bool: '<-'(var bool: x, var bool: y); +/** @group builtins.logic Return truth value of \a x if-and-only-if \a y */ +function bool: '<->'( bool: x, bool: y); +/** @group builtins.logic Return truth value of \a x if-and-only-if \a y */ +function var bool: '<->'(var bool: x, var bool: y); +/** @group builtins.logic Return truth value of \a x xor \a y */ +function bool: 'xor'( bool: x, bool: y); +/** @group builtins.logic Return truth value of \a x xor \a y */ +function var bool: 'xor'(var bool: x, var bool: y); +/** @group builtins.logic Return truth value of the negation of \a x */ +function bool: 'not'( bool: x); +/** @group builtins.logic Return truth value of the negation of \a x */ +function var bool: 'not'(var bool: x); + +/** @group builtins.logic Return truth value of \(\bigwedge_i \a x[i]\) */ +function bool: forall(array[$T] of bool: x); +/** @group builtins.logic Return truth value of \(\bigwedge_i \a x[i]\) */ +function var bool: forall(array[$T] of var bool: x); +/** @group builtins.logic Return truth value of \(\bigvee_i \a x[i]\) */ +function bool: exists(array[$T] of bool: x); +/** @group builtins.logic Return truth value of \(\bigvee_i \a x[i]\) */ +function var bool: exists(array[$T] of var bool: x); +/** @group builtins.logic Return truth value of \(\oplus_i \a x[i]\) */ +function bool: xorall(array[$T] of bool: x); +/** @group builtins.logic Return truth value of \(\oplus_i \a x[i]\) */ +function var bool: xorall(array[$T] of var bool: x) = + array_bool_xor(array1d(x)); +/** @group builtins.logic Return truth value of \(\text{true}\oplus (\oplus_i \a x[i])\) */ +function bool: iffall(array[$T] of bool: x); +/** @group builtins.logic Return truth value of \(\text{true}\oplus (\oplus_i \a x[i])\) */ +function var bool: iffall(array[$T] of var bool: x) = + array_bool_xor(array1d(x)++[true]); +/** @group builtins.logic Return truth value of \((\bigvee_i \a x[i]) \lor (\bigvee_j \lnot \a y[j])\) */ +function var bool: clause(array[$T] of var bool: x, array[$T] of var bool: y); +/** @group builtins.logic Return truth value of \((\bigvee_i \a x[i]) \lor (\bigvee_j \lnot \a y[j])\) */ +function var bool: clause(array[$T] of bool: x, array[$T] of bool: y); + +/*** + @groupdef builtins.set Set operations + + These functions implement the basic operations on sets. +*/ + +/** @group builtins.set Test if \a x is an element of the set \a y */ +function bool: 'in'( int: x, set of int: y); +/** @group builtins.set \a x is an element of the set \a y */ +function var bool: 'in'(var int: x, var set of int: y); + +/** @group builtins.set Test if \a x is an element of the set \a y */ +function bool: 'in'( float: x, set of float: y); +/** @group builtins.set Test if \a x is an element of the set \a y */ +function var bool: 'in'(var float: x, set of float: y); + +/** @group builtins.set Test if \a x is a subset of \a y */ +function bool: 'subset'( set of $T: x, set of $T: y); +/** @group builtins.set \a x is a subset of \a y */ +function var bool: 'subset'(var set of int: x, var set of int: y); +/** @group builtins.set Test if \a x is a superset of \a y */ +function bool: 'superset'( set of $T: x, set of $T: y); +/** @group builtins.set \a x is a superset of \a y */ +function var bool: 'superset'(var set of int: x, var set of int: y); + +/** @group builtins.set Return the union of sets \a x and \a y */ +function set of $T: 'union'( set of $T: x, set of $T: y); +/** @group builtins.set Return the union of sets \a x and \a y */ +function var set of $$T: 'union'(var set of $$T: x, var set of $$T: y); +/** @group builtins.set Return the intersection of sets \a x and \a y */ +function set of $T: 'intersect'( set of $T: x, set of $T: y); +/** @group builtins.set Return the intersection of sets \a x and \a y */ +function var set of $$T: 'intersect'(var set of $$T: x, var set of $$T: y); +/** @group builtins.set Return the set difference of sets \a x − \a y */ +function set of $T: 'diff'( set of $T: x, set of $T: y); +/** @group builtins.set Return the set difference of sets \a x − \a y */ +function var set of $$T: 'diff'(var set of $$T: x, var set of $$T: y); +/** @group builtins.set Return the symmetric set difference of sets \a x and \a y */ +function set of $T: 'symdiff'( set of $T: x, set of $T: y); +/** @group builtins.set Return the symmetric set difference of sets \a x and \a y */ +function var set of $$T: 'symdiff'(var set of $$T: x, var set of $$T: y); + +/** @group builtins.set Return the set \(\{\a a,\ldots,\a b\}\) */ +function set of $$E: '..'($$E: a,$$E: b); +/** @group builtins.set Return the set \(\{\a a,\ldots,\a b\}\) */ +function set of float: '..'(float: a,float: b); + +function var set of int: '..'(var int: a, var int: b) ::promise_total = + let { + var set of min(lb(a),lb(b))..max(ub(a),ub(b)): s; + constraint forall (i in ub(s)) (i in s <-> (a <= i /\ i <= b)); + } in s; + +/** @group builtins.set Return the cardinality of the set \a x */ +function int: card( set of $T: x); +/** @group builtins.set Return the cardinality of the set \a x */ +function var int: card(var set of int: x); + +/** @group builtins.set Return the union of the sets in array \a x */ +function set of $U: array_union(array[$T] of set of $U: x); +/** @group builtins.set Return the union of the sets in array \a x */ +function var set of int: array_union(array[int] of var set of int: x) ::promise_total = + if length(x)=0 then {} + elseif length(x)=1 then x[min(index_set(x))] + else + let { + int: l=min(index_set(x)); + int: u=max(index_set(x)); + array[l..u-1] of var set of ub_array(x): y; + constraint y[l]=x[l] union x[l+1]; + constraint forall (i in l+2..u) (y[i-1]=y[i-2] union x[i]); + } in y[u-1] + endif; + + +/** @group builtins.set Return the intersection of the sets in array \a x */ +function set of $U: array_intersect(array[$T] of set of $U: x); + +/** @group builtins.set Return the intersection of the sets in array \a x */ +function var set of int: array_intersect(array[int] of var set of int: x) ::promise_total = + if length(x)=0 then assert(false,"can't be!",-infinity..infinity) + elseif length(x)=1 then x[min(index_set(x))] + else + let { + int: l=min(index_set(x)); + int: u=max(index_set(x)); + array[l..u-1] of var set of ub_array(x): y; + constraint y[l]=x[l] intersect x[l+1]; + constraint forall (i in l+2..u) (y[i-1]=y[i-2] intersect x[i]); + } in y[u-1] + endif; + +/** @group builtins.set Return the minimum of the set \a s */ +function var $$E: min(var set of $$E: s); + +/** @group builtins.set Return the maximum of the set \a s */ +function var $$E: max(var set of $$E: s); + +/*** + @groupdef builtins.array Array operations + + These functions implement the basic operations on arrays. +*/ + +/** @group builtins.array Return the concatenation of arrays \a x and \a y */ +function array[int] of $T: '++'(array[int] of $T: x, array[int] of $T: y); +/** @group builtins.array Return the concatenation of arrays \a x and \a y */ +function array[int] of opt $T: '++'(array[int] of opt $T: x, array[int] of opt $T: y); +/** @group builtins.array Return the concatenation of arrays \a x and \a y */ +function array[int] of var $T: '++'(array[int] of var $T: x, array[int] of var $T: y); +/** @group builtins.array Return the concatenation of arrays \a x and \a y */ +function array[int] of var opt $T: '++'(array[int] of var opt $T: x, array[int] of var opt $T: y); + +/** @group builtins.array Return the length of array \a x + + Note that the length is defined as the number of elements in the + array, regardless of its dimensionality. +*/ +function int: length(array[$T] of var opt $U: x); + +/** @group builtins.array Return the array \a x in reverse order + + The resulting array has the same index set as \a x. +*/ +function array[$$E] of $T: reverse(array[$$E] of $T: x) = + let { int: l = max(index_set(x))+min(index_set(x)) } in + array1d(index_set(x),[x[l-i] | i in index_set(x)]); + +/** @group builtins.array Return the array \a x in reverse order + + The resulting array has the same index set as \a x. +*/ +function array[$$E] of opt $T: reverse(array[$$E] of opt $T: x) = + let { int: l = max(index_set(x))+min(index_set(x)) } in + array1d(index_set(x),[x[l-i] | i in index_set(x)]); + +/** @group builtins.array Return the array \a x in reverse order + + The resulting array has the same index set as \a x. +*/ +function array[$$E] of var $T: reverse(array[$$E] of var $T: x) = + let { int: l = max(index_set(x))+min(index_set(x)) } in + array1d(index_set(x),[x[l-i] | i in index_set(x)]); + +/** @group builtins.array Return the array \a x in reverse order + + The resulting array has the same index set as \a x. +*/ +function array[$$E] of var opt $T: reverse(array[$$E] of var opt $T: x) = + let { int: l = max(index_set(x))+min(index_set(x)) } in + array1d(index_set(x),[x[l-i] | i in index_set(x)]); + +/** @group builtins.array Test if \a x and \a y have the same index sets */ +test index_sets_agree(array[$T] of var opt $U: x, array[$T] of var opt $W: y); + +/** @group builtins.array Return index set of one-dimensional array \a x */ +function set of $$E: index_set(array[$$E] of var opt $U: x); +/** @group builtins.array Return index set of first dimension of two-dimensional array \a x */ +function set of $$E: index_set_1of2(array[$$E,int] of var opt $U: x); +/** @group builtins.array Return index set of second dimension of two-dimensional array \a x */ +function set of $$E: index_set_2of2(array[int,$$E] of var opt $U: x); +/** @group builtins.array Return index set of first dimension of 3-dimensional array \a x */ +function set of $$E: index_set_1of3(array[$$E,int,int] of var opt $U: x); +/** @group builtins.array Return index set of second dimension of 3-dimensional array \a x */ +function set of $$E: index_set_2of3(array[int,$$E,int] of var opt $U: x); +/** @group builtins.array Return index set of third dimension of 3-dimensional array \a x */ +function set of $$E: index_set_3of3(array[int,int,$$E] of var opt $U: x); +/** @group builtins.array Return index set of first dimension of 4-dimensional array \a x */ +function set of $$E: index_set_1of4(array[$$E,int,int,int] of var opt $U: x); +/** @group builtins.array Return index set of second dimension of 4-dimensional array \a x */ +function set of $$E: index_set_2of4(array[int,$$E,int,int] of var opt $U: x); +/** @group builtins.array Return index set of third dimension of 4-dimensional array \a x */ +function set of $$E: index_set_3of4(array[int,int,$$E,int] of var opt $U: x); +/** @group builtins.array Return index set of fourth dimension of 4-dimensional array \a x */ +function set of $$E: index_set_4of4(array[int,int,int,$$E] of var opt $U: x); +/** @group builtins.array Return index set of first dimension of 5-dimensional array \a x */ +function set of $$E: index_set_1of5(array[$$E,int,int,int,int] of var opt $U: x); +/** @group builtins.array Return index set of second dimension of 5-dimensional array \a x */ +function set of $$E: index_set_2of5(array[int,$$E,int,int,int] of var opt $U: x); +/** @group builtins.array Return index set of third dimension of 5-dimensional array \a x */ +function set of $$E: index_set_3of5(array[int,int,$$E,int,int] of var opt $U: x); +/** @group builtins.array Return index set of fourth dimension of 5-dimensional array \a x */ +function set of $$E: index_set_4of5(array[int,int,int,$$E,int] of var opt $U: x); +/** @group builtins.array Return index set of fifth dimension of 5-dimensional array \a x */ +function set of $$E: index_set_5of5(array[int,int,int,int,$$E] of var opt $U: x); +/** @group builtins.array Return index set of first dimension of 6-dimensional array \a x */ +function set of $$E: index_set_1of6(array[$$E,int,int,int,int,int] of var opt $U: x); +/** @group builtins.array Return index set of second dimension of 6-dimensional array \a x */ +function set of $$E: index_set_2of6(array[int,$$E,int,int,int,int] of var opt $U: x); +/** @group builtins.array Return index set of third dimension of 6-dimensional array \a x */ +function set of $$E: index_set_3of6(array[int,int,$$E,int,int,int] of var opt $U: x); +/** @group builtins.array Return index set of fourth dimension of 6-dimensional array \a x */ +function set of $$E: index_set_4of6(array[int,int,int,$$E,int,int] of var opt $U: x); +/** @group builtins.array Return index set of fifth dimension of 6-dimensional array \a x */ +function set of $$E: index_set_5of6(array[int,int,int,int,$$E,int] of var opt $U: x); +/** @group builtins.array Return index set of sixth dimension of 6-dimensional array \a x */ +function set of $$E: index_set_6of6(array[int,int,int,int,int,$$E] of var opt $U: x); + +/** @group builtins.array Return array \a x coerced to index set 1..length(\a x). + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[int] of $V: array1d(array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to index set 1..length(\a x). + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[int] of opt $V: array1d(array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to index set 1..length(\a x). + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[int] of var $V: array1d(array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to index set 1..length(\a x). + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[int] of var opt $V: array1d(array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E] of $V: array1d(set of $$E: S, + array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E] of opt $V: array1d(set of $$E: S, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E] of var $V: array1d(set of $$E: S, + array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to one-dimensional array with index set \a S. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E] of var opt $V: array1d(set of $$E: S, + array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E,$$F] of $V: array2d(set of $$E: S1, set of $$F: S2, + array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E,$$F] of opt $V: array2d(set of $$E: S1, set of $$F: S2, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E,$$F] of var $V: array2d(set of $$E: S1, set of $$F: S2, + array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to two-dimensional array with index sets \a S1 and \a S2. + Coercions are perfomed by considering the array \a x in row-major order. */ +function array[$$E,$$F] of var opt $V: array2d(set of $$E: S1, set of $$F: S2, + array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a x coerced to three-dimensional array with + index sets \a S1, \a S2 and \a S3. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G] of $V: array3d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to three-dimensional array with + index sets \a S1, \a S2 and \a S3. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G] of opt $V: array3d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to three-dimensional array with + index sets \a S1, \a S2 and \a S3. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G] of var $V: array3d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to three-dimensional array with + index sets \a S1, \a S2 and \a S3. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G] of var opt $V: array3d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a x coerced to 4-dimensional array with + index sets \a S1, \a S2, \a S3 and \a S4. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H] of $V: array4d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to 4-dimensional array with + index sets \a S1, \a S2, \a S3 and \a S4. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H] of opt $V: array4d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to 4-dimensional array with + index sets \a S1, \a S2, \a S3 and \a S4. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H] of var $V: array4d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to 4-dimensional array with + index sets \a S1, \a S2, \a S3 and \a S4. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H] of var opt $V: array4d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a x coerced to 5-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4 and \a S5. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I] of $V: array5d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to 5-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4 and \a S5. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I] of opt $V: array5d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to 5-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4 and \a S5. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I] of var $V: array5d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to 5-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4 and \a S5. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I] of var opt $V: array5d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a x coerced to 6-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4, \a S5 and \a S6. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I,$$J] of $V: array6d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + set of $$J: S6, + array[$U] of $V: x); +/** @group builtins.array Return array \a x coerced to 6-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4, \a S5 and \a S6. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I,$$J] of opt $V: array6d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + set of $$J: S6, + array[$U] of opt $V: x); +/** @group builtins.array Return array \a x coerced to 6-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4, \a S5 and \a S6. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I,$$J] of var $V: array6d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + set of $$J: S6, + array[$U] of var $V: x); +/** @group builtins.array Return array \a x coerced to 6-dimensional array with + index sets \a S1, \a S2, \a S3, \a S4, \a S5 and \a S6. + Coercions are perfomed by considering the array \a x in row-major order. +*/ +function array[$$E,$$F,$$G,$$H,$$I,$$J] of var opt $V: array6d(set of $$E: S1, + set of $$F: S2, + set of $$G: S3, + set of $$H: S4, + set of $$I: S5, + set of $$J: S6, + array[$U] of var opt $V: x); + +/** @group builtins.array Return array \a y coerced to array with same number of + dimensions and same index sets as array \a x. + Coercions are perfomed by considering the array \a y in row-major order. +*/ +function array[$T] of $V: arrayXd(array[$T] of var opt $X: x, array[$U] of $V: y); +/** @group builtins.array Return array \a y coerced to array with same number of + dimensions and same index sets as array \a x. + Coercions are perfomed by considering the array \a y in row-major order. +*/ +function array[$T] of opt $V: arrayXd(array[$T] of var opt $X: x, array[$U] of opt $V: y); +/** @group builtins.array Return array \a y coerced to array with same number of + dimensions and same index sets as array \a x. + Coercions are perfomed by considering the array \a y in row-major order. +*/ +function array[$T] of var $V: arrayXd(array[$T] of var opt $X: x, array[$U] of var $V: y); +/** @group builtins.array Return array \a y coerced to array with same number of + dimensions and same index sets as array \a x. + Coercions are perfomed by considering the array \a y in row-major order. +*/ +function array[$T] of var opt $V: arrayXd(array[$T] of var opt $X: x, array[$U] of var opt $V: y); + +/** @group builtins.array Return row \a r of array \a x */ +function array[$$E] of $T: row(array[int, $$E] of $T: x, int: r) = x[r,..]; +/** @group builtins.array Return row \a r of array \a x */ +function array[$$E] of opt $T: row(array[int, $$E] of opt $T: x, int: r) = x[r,..]; +/** @group builtins.array Return row \a r of array \a x */ +function array[$$E] of var $T: row(array[int, $$E] of var $T: x, int: r) = x[r,..]; +/** @group builtins.array Return row \a r of array \a x */ +function array[$$E] of var opt $T: row(array[int, $$E] of var opt $T: x, int: r) = x[r,..]; + +/** @group builtins.array Return column \a c of array \a x */ +function array[$$E] of $T: col(array[$$E,int] of $T: x, int: c) = x[..,c]; +/** @group builtins.array Return column \a c of array \a x */ +function array[$$E] of opt $T: col(array[$$E,int] of opt $T: x, int: c) = x[..,c]; +/** @group builtins.array Return column \a c of array \a x */ +function array[$$E] of var $T: col(array[$$E,int] of var $T: x, int: c) = x[..,c]; +/** @group builtins.array Return column \a c of array \a x */ +function array[$$E] of var opt $T: col(array[$$E,int] of var opt $T: x, int: c) = x[..,c]; + +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new array with index sets \a dims */ +function array[int] of $T: slice_Xd(array[$E] of $T: x, array[int] of set of int: s, array[int] of set of int: dims); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 1d array with index set \a dims1 */ +function array[int] of $T: slice_1d(array[$E] of $T: x, array[int] of set of int: s, set of int: dims1); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 2d array with index sets \a dims1 and \a dims2 */ +function array[int,int] of $T: slice_2d(array[$E] of $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2 and \a dims3 */ +function array[int,int,int] of $T: slice_3d(array[$E] of $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4 */ +function array[int,int,int] of $T: slice_4d(array[$E] of $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5 */ +function array[int,int,int] of $T: slice_5d(array[$E] of $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5, \a dims6 */ +function array[int,int,int] of $T: slice_6d(array[$E] of $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5, set of int: dims6); + +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new array with index sets \a dims */ +function array[int] of opt $T: slice_Xd(array[$E] of opt $T: x, array[int] of set of int: s, array[int] of set of int: dims); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 1d array with index set \a dims1 */ +function array[int] of opt $T: slice_1d(array[$E] of opt $T: x, array[int] of set of int: s, set of int: dims1); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 2d array with index sets \a dims1 and \a dims2 */ +function array[int,int] of opt $T: slice_2d(array[$E] of opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2 and \a dims3 */ +function array[int,int,int] of opt $T: slice_3d(array[$E] of opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4 */ +function array[int,int,int] of opt $T: slice_4d(array[$E] of opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5 */ +function array[int,int,int] of opt $T: slice_5d(array[$E] of opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5, \a dims6 */ +function array[int,int,int] of opt $T: slice_6d(array[$E] of opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5, set of int: dims6); + +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new array with index sets \a dims */ +function array[int] of var $T: slice_Xd(array[$E] of var $T: x, array[int] of set of int: s, array[int] of set of int: dims); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 1d array with index set \a dims1 */ +function array[int] of var $T: slice_1d(array[$E] of var $T: x, array[int] of set of int: s, set of int: dims1); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 2d array with index sets \a dims1 and \a dims2 */ +function array[int,int] of var $T: slice_2d(array[$E] of var $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2 and \a dims3 */ +function array[int,int,int] of var $T: slice_3d(array[$E] of var $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4 */ +function array[int,int,int] of var $T: slice_4d(array[$E] of var $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5 */ +function array[int,int,int] of var $T: slice_5d(array[$E] of var $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5, \a dims6 */ +function array[int,int,int] of var $T: slice_6d(array[$E] of var $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5, set of int: dims6); + +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new array with index sets \a dims */ +function array[int] of var opt $T: slice_Xd(array[$E] of var opt $T: x, array[int] of set of int: s, array[int] of set of int: dims); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 1d array with index set \a dims1 */ +function array[int] of var opt $T: slice_1d(array[$E] of var opt $T: x, array[int] of set of int: s, set of int: dims1); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 2d array with index sets \a dims1 and \a dims2 */ +function array[int,int] of var opt $T: slice_2d(array[$E] of var opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2 and \a dims3 */ +function array[int,int,int] of var opt $T: slice_3d(array[$E] of var opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4 */ +function array[int,int,int] of var opt $T: slice_4d(array[$E] of var opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5 */ +function array[int,int,int] of var opt $T: slice_5d(array[$E] of var opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5); +/** @group builtins.array Return slice of array \a x specified by sets \a s, coerced to new 3d array with index sets \a dims1, \a dims2, \a dims3, \a dims4, \a dims5, \a dims6 */ +function array[int,int,int] of var opt $T: slice_6d(array[$E] of var opt $T: x, array[int] of set of int: s, set of int: dims1, set of int: dims2, set of int: dims3, set of int: dims4, set of int: dims5, set of int: dims6); + + +/** @group builtins.array Test if \a i is in the index set of \a x */ +test has_index(int: i, array[int] of var opt $T: x) = i in index_set(x); +/** @group builtins.array Test if \a e is an element of array \a x */ +test has_element($T: e, array[int] of $T: x) = exists (i in index_set(x)) (x[i]=e); +/** @group builtins.array Test if \a e is an element of array \a x */ +test has_element($T: e, array[int] of opt $T: x) = exists (i in index_set(x)) (x[i]=e); +/** @group builtins.array Test if \a e is an element of array \a x */ +predicate has_element($T: e, array[$$E] of var opt $T: x) = exists (i in index_set(x)) (x[i]=e); + +/*** + @groupdef builtins.sort Array sorting operations +*/ + +/** @group builtins.sort Return array \a x sorted by the values in \a y in non-decreasing order + +The sort is stable, i.e. if \a y[\p i] = \a y[\p j] with \p i < \p j, then +\a x[\p i] will appear in the output before \a x[\p j]. +*/ +function array[$$E] of var opt $T: sort_by(array[$$E] of var opt $T: x, array[$$E] of int: y); +/** @group builtins.sort Return array \a x sorted by the values in \a y in non-decreasing order + +The sort is stable, i.e. if \a y[\p i] = \a y[\p j] with \p i < \p j, then +\a x[\p i] will appear in the output before \a x[\p j]. +*/ +function array[$$E] of var $T: sort_by(array[$$E] of var $T: x, array[$$E] of int: y); +/** @group builtins.sort Return array \a x sorted by the values in \a y in non-decreasing order + +The sort is stable, i.e. if \a y[\p i] = \a y[\p j] with \p i < \p j, then +\a x[\p i] will appear in the output before \a x[\p j]. +*/ +function array[$$E] of $T: sort_by(array[$$E] of $T: x, array[$$E] of int: y); + +/** @group builtins.sort Return array \a x sorted by the values in \a y in non-decreasing order + +The sort is stable, i.e. if \a y[\p i] = \a y[\p j] with \p i < \p j, then +\a x[\p i] will appear in the output before \a x[\p j]. +*/ +function array[$$E] of var opt $T: sort_by(array[$$E] of var opt $T: x, array[$$E] of float: y); +/** @group builtins.sort Return array \a x sorted by the values in \a y in non-decreasing order + +The sort is stable, i.e. if \a y[\p i] = \a y[\p j] with \p i < \p j, then +\a x[\p i] will appear in the output before \a x[\p j]. +*/ +function array[$$E] of var $T: sort_by(array[$$E] of var $T: x, array[$$E] of float: y); +/** @group builtins.sort Return array \a x sorted by the values in \a y in non-decreasing order + +The sort is stable, i.e. if \a y[\p i] = \a y[\p j] with \p i < \p j, then +\a x[\p i] will appear in the output before \a x[\p j]. +*/ +function array[$$E] of $T: sort_by(array[$$E] of $T: x, array[$$E] of float: y); + +function array[$$E] of bool: internal_sort(array[$$E] of bool: x); +function array[$$E] of int: internal_sort(array[$$E] of int: x); +/** @group builtins.sort Return values from array \a x sorted in non-decreasing order */ +function array[$$E] of int: sort(array[$$E] of int: x) = internal_sort(x); +/** @group builtins.sort Return values from array \a x sorted in non-decreasing order */ +function array[$$E] of float: sort(array[$$E] of float: x); +/** @group builtins.sort Return values from array \a x sorted in non-decreasing order */ +function array[$$E] of bool: sort(array[$$E] of bool: x) = internal_sort(x); + +/** @group builtins.sort +Returns the permutation \a p which causes \a x to be in sorted order hence +\a x[\a p[\p i]] <= \a x[\a p[\p i+1]]. + +The permutation is the stable sort hence \a x[\a p[\p i]] = \a x[\a p[\p i+1]] \(\rightarrow\) \a p[\p i] < \a p[\p i+1]. +*/ +function array[int] of $$E: arg_sort(array[$$E] of int:x) = + sort_by([i | i in index_set(x)], x); + +/** @group builtins.sort +Returns the permutation \a p which causes \a x to be in sorted order hence +\a x[\a p[\p i]] <= \a x[\a p[\p i+1]]. + +The permutation is the stable sort hence \a x[\a p[\p i]] = \a x[\a p[\p i+1]] \(\rightarrow\) \a p[\p i] < \a p[\p i+1]. +*/ +function array[int] of $$E: arg_sort(array[$$E] of float:x) = + sort_by([i | i in index_set(x)], x); + +/*** + @groupdef builtins.coercion Coercions + + These functions implement coercions, or channeling, between different types. +*/ + +/** @group builtins.coercion Return \( \lceil{ \a x} \rceil \) */ +function int: ceil(float: x); +/** @group builtins.coercion Return \( \lfloor{ \a x} \rfloor \) */ +function int: floor(float: x); +/** @group builtins.coercion Return \a x rounded to nearest integer */ +function int: round(float: x); + +/** @group builtins.coercion Return Boolean \a b coerced to an integer */ +function int: bool2int(bool: b); +/** @group builtins.coercion Return Boolean \a b coerced to a float */ +function float: bool2float(bool: b) = if b then 1.0 else 0.0 endif; +/** @group builtins.coercion Return array of Booleans \a x coerced to an array of floats */ +function array[$T] of float: bool2float(array[$T] of bool: x) ::promise_total = + let { + array[int] of bool: xx = array1d(x) + } in arrayXd(x,[bool2float(xx[i]) | i in index_set(xx)]); +/** @group builtins.coercion Return array of Booleans \a x coerced to an array of floats */ +function array[$T] of var float: bool2float(array[$T] of var bool: x) ::promise_total = + let { + array[int] of var bool: xx = array1d(x) + } in arrayXd(x,[bool2float(xx[i]) | i in index_set(xx)]); +/** @group builtins.coercion Return Boolean \a b coerced to an integer */ +function var int: bool2int(var bool: b); +/** @group builtins.coercion Return array of Booleans \a b coerced to an array of integers */ +function array[$$E] of var int: bool2int(array[$$E] of var bool: b); +/** @group builtins.coercion Return Boolean \a b coerced to a float */ +function var float: bool2float(var bool: b) = int2float(bool2int(b)); +/** @group builtins.coercion Return integer \a x coerced to a float */ +function float: int2float(int: x); +/** @group builtins.coercion Return integer \a x coerced to a float */ +function var float: int2float(var int: x) ::promise_total; + +function set of int: bool2int(set of bool: b) = + if b={false,true} then {0,1} + elseif b={false} then {0} + elseif b={true} then {1} + else {} endif; + +/** @group builtins.coercion Return array of Booleans \a x coerced to an array of integers */ +function array[$T] of int: bool2int(array[$T] of bool: x) ::promise_total = + let { + array[int] of bool: xx = array1d(x) + } in arrayXd(x,[bool2int(xx[i]) | i in index_set(xx)]); + +/** @group builtins.coercion Return array of sets of Booleans \a x coerced to an array of sets of integers */ +function array[$T] of set of int: bool2int(array[$T] of set of bool: x) ::promise_total = + let { + array[int] of set of bool: xx = array1d(x) + } in arrayXd(x,[bool2int(xx[i]) | i in index_set(xx)]); + +/** @group builtins.coercion Return array of Booleans \a x coerced to an array of integers */ +function array[$T] of var int: bool2int(array[$T] of var bool: x) ::promise_total = + let { + array[int] of var bool: xx = array1d(x) + } in arrayXd(x,[bool2int(xx[i]) | i in index_set(xx)]); + +/** @group builtins.coercion Return array of Booleans \a x coerced to an array of integers */ +function array[$T] of var opt int: bool2int(array[$T] of var opt bool: x) ::promise_total = + let { + array[int] of var opt bool: xx = array1d(x) + } in arrayXd(x,[bool2int(xx[i]) | i in index_set(xx)]); + +/** @group builtins.coercion Return array of integers \a x coerced to an array of floats */ +function array[$T] of float: int2float(array[$T] of int: x) ::promise_total = + let { + array[int] of int: xx = array1d(x) + } in arrayXd(x,[int2float(xx[i]) | i in index_set(xx)]); + +/** @group builtins.coercion Return array of integers \a x coerced to an array of floats */ +function array[$T] of var float: int2float(array[$T] of var int: x) ::promise_total = + let { + array[int] of var int: xx = array1d(x) + } in arrayXd(x,[int2float(xx[i]) | i in index_set(xx)]); + +% Only supported for set of int: +% function array[int] of $T: set2array(set of $T); +/** @group builtins.coercion Return a set of integers \a x coerced to an array of integers */ +function array[int] of $$E: set2array(set of $$E: x); + + +/*** + @groupdef builtins.string String operations + + These functions implement operations on strings. +*/ + +/** @group builtins.string Convert \a x into a string */ +function string: show(var opt set of $T: x); +/** @group builtins.string Convert \a x into a string */ +function string: show(var opt $T: x); +/** @group builtins.string Convert \a x into a string */ +function string: show(array[$U] of var opt $T: x); + +function string: showDzn(var opt set of $T: x); +function string: showDzn(var opt $T: x); +function string: showDzn(array[$U] of var opt $T: x); +function string: showDznId(string: x); + + +/** @group builtins.string Formatted to-string conversion for integers + +Converts the integer \a x into a string right +justified by the number of characters given by \a w, or left +justified if \a w is negative. +*/ +function string: show_int(int: w, var int: x); +/** @group builtins.string Formatted to-string conversion for floats. + +Converts the float \a x into a string right justified +by the number of characters given by \a w, or left justified if +\a w is negative. The number of digits to appear after the decimal +point is given by \a p. It is a run-time error for \a p to be negative. +*/ +function string: show_float(int: w, int: p, var float: x); + +/** @group builtins.string Convert two-dimensional array \a x into a string */ +function string: show2d(array[int,int] of var opt $T: x) = + let { int: rows=card(index_set_1of2(x)); + int: cols=card(index_set_2of2(x)); + array[int] of string: s = [show(x[i,j]) | i in index_set_1of2(x), j in index_set_2of2(x)]; + int: max_length = max([string_length(s[i]) | i in index_set(s)]) + } in + "[| "++ + concat([format_justify_string(max_length,s[(i-1)*cols+j])++ + if j min(e) } in x-1; + +/** @group builtins.enum Convert \a x to enum type \a X */ +function $$E: to_enum(set of $$E: X, int: x); +/** @group builtins.enum Convert \a x to enum type \a X */ +function var $$E: to_enum(set of $$E: X, var int: x) = + let { constraint x in X } in x; + +/** @group builtins.enum Convert \a x to enum type \a X */ +function array[$U] of $$E: to_enum(set of $$E: X, array[$U] of int: x) = + let { array[int] of int: xx = array1d(x) } in + arrayXd(x, [ to_enum(X,xx[i]) | i in index_set(xx)]); + +/** @group builtins.enum Convert \a x to enum type \a X */ +function array[$U] of var $$E: to_enum(set of $$E: X, array[$U] of var int: x) = + let { array[int] of var int: xx = array1d(x) } in + arrayXd(x, [ to_enum(X,xx[i]) | i in index_set(xx)]); + +/** @group builtins.enum Convert \a x to enum type \a X */ +function set of $$E: to_enum(set of $$E: X, set of int: x) = { to_enum(X,i) | i in x }; + +%/** @group builtins.enum Convert \a x to enum type \a X */ +function var set of $$E: to_enum(set of $$E: X, var set of int: x) = + let { var set of X: y; constraint x subset X; constraint forall (i in X) (i in x <-> i in y); } in y; + + + +%-----------------------------------------------------------------------------% +% +% Internal compiler functions +% +% These functions are used internally by the compiler. +% + +% domain constraints +predicate var_dom(var int:x, set of int: s) = + if has_bounds(x) /\ dom(x) subset s then true + else x in s + endif; +predicate var_dom(var set of int: x, set of int: s) = + if has_ub_set(x) /\ ub(x) subset s then true + else set_subset(x,s) + endif; +predicate var_dom(var float:x, float: l, float: u) = + if has_bounds(x) /\ lb(x) >= l /\ ub(x) <= u then true + else x >= l /\ x <= u + endif; +predicate var_dom(var float:x, set of float: d) = + x in d; +test var_dom(float:x, float: l, float: u) = + x >= l /\ x <= u; +test var_dom(float:x, set of float: d) = + x in d; + +predicate var_dom(array[$T] of var set of int: x, set of int: d) = + let { array[int] of var set of int: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],d)); +predicate var_dom(array[$T] of var int: x, set of int: d) = + let { array[int] of var int: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],d)); +predicate var_dom(array[$T] of var float: x, float: l, float: u) = + let { array[int] of var float: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],l,u)); +predicate var_dom(array[$T] of var float: x, set of float: d) = + let { array[int] of var float: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],d)); + +test var_dom(array[$T] of set of int: x, set of int: d) = + let { array[int] of set of int: xx = array1d(x) } + in forall (i in index_set(xx)) (xx[i] subset d); +test var_dom(array[$T] of int: x, set of int: d) = + let { array[int] of int: xx = array1d(x) } + in forall (i in index_set(xx)) (xx[i] in d); +test var_dom(array[$T] of float: x, float: l, float: u) = + let { array[int] of float: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],l,u)); +test var_dom(array[$T] of float: x, set of float: d) = + let { array[int] of float: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],d)); + +function var set of int: array2set(array[int] of var int: x) ::promise_total = + let { + var set of int: y = array_union([ + let { var set of dom(x[i]): s; + constraint x[i] in s /\ card(s)=1; + } in s | i in index_set(x)]); + } in y; + +function set of $$T: array2set(array[int] of $$T: x) = + { x[i] | i in index_set(x) }; + +predicate array_var_int_element(var int: x, array[int] of int: y, var int: z) = + array_int_element(x,y,z); +predicate array_var_bool_element(var int: x, array[int] of bool: y, var bool: z) = + array_bool_element(x,y,z); +predicate array_var_float_element(var int: x, array[int] of float: y, var float: z) = + array_float_element(x,y,z); +predicate array_var_set_element(var int: x, array[int] of set of int: y, var set of int: z) = + array_set_element(x,y,z); + +predicate bool_xor_reif(var bool: a, var bool: b, var bool: c) = + bool_xor(a,b,c); + +predicate xorall_reif(array[int] of var bool: b, var bool: c) = + let { var bool: nc ::is_defined_var; constraint xorall([nc]++b) ::defines_var(nc); } in c = not nc; + +function var int: lin_exp(array[int] of int, array[int] of var int, int); +function var float: lin_exp(array[int] of float, array[int] of var float, float); + +test mzn_in_root_context(var $T); + +test mzn_in_redundant_constraint(); + +%-----------------------------------------------------------------------------% +% +% Element constraint implementations +% +% MiniZinc compiles element constraints using a series of intermediate +% functions that test whether the constraint is total and perform array slicing +% for multi-dimensional element constraints. +% + +%%%%%%%%%%%%%%%%%%% +% Element on ints + +function var int: element_t(var int: idx, array[int] of var int: x) :: promise_total = + let { + var dom_bounds_array(x): r ::is_defined_var; + constraint idx in index_set(x); + constraint array_var_int_element_nonshifted(idx,x,r) ::defines_var(r); + } in r; + +function var int: element_mt(var int: idx, array[int] of var int: x) :: promise_total = + let { + var lb_array(x)..ub_array(x): r ::is_defined_var; + var min(index_set(x))..max(index_set(x)): idx2; + constraint idx in index_set(x) -> idx2=idx; + constraint idx in index_set(x) \/ idx2=min(index_set(x)); + constraint array_var_int_element_nonshifted(idx2,x,r) ::defines_var(r); + } in r; + +function var int: element(var int: idx, array[int] of var int: x) = + if is_fixed(idx) then + x[fix(idx)] + elseif mzn_in_root_context(idx) then let { + constraint idx in index_set(x) + } in element_t(idx,x) + elseif (has_bounds(idx) /\ lb(idx) >= min(index_set(x)) /\ ub(idx) <= max(index_set(x))) then + element_t(idx,x) + else let { + constraint idx in index_set(x) + } in element_mt(idx,x) + endif; + +function var int: element(var int: idx1, var int: idx2, + array[int,int] of var int: x) = + if is_fixed(idx1) /\ is_fixed(idx2) then + x[fix(idx1), fix(idx2)] + elseif is_fixed(idx1) then + element(idx2, x[fix(idx1),..]) + elseif is_fixed(idx2) then + element(idx1, x[.., fix(idx2)]) + else + let { + int: dim = card(index_set_2of2(x)); + int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + elseif + ((has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of2(x)) /\ ub(idx1) <= max(index_set_1of2(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of2(x)) /\ ub(idx2) <= max(index_set_2of2(x)))) + then + element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + else + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_mt( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + endif + endif; + +function var int: element(var int: idx1, var int: idx2, var int: idx3, + array[int,int,int] of var int: x) = + let { + int: dim2 = card(index_set_2of3(x)); + int: dim3 = card(index_set_3of3(x)); + + int: min = min(index_set_1of3(x))*dim2*dim3+ + min(index_set_2of3(x))*dim3+ + min(index_set_3of3(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of3(x)) /\ ub(idx1) <= max(index_set_1of3(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of3(x)) /\ ub(idx2) <= max(index_set_2of3(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of3(x)) /\ ub(idx3) <= max(index_set_3of3(x)))) + then + element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_mt( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + endif; + +function var int: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, + array[int,int,int,int] of var int: x) = + let { + int: dim2 = card(index_set_2of4(x)); + int: dim3 = card(index_set_3of4(x)); + int: dim4 = card(index_set_4of4(x)); + + int: min = min(index_set_1of4(x))*dim2*dim3*dim4+ + min(index_set_2of4(x))*dim3*dim4+ + min(index_set_3of4(x))*dim4+ + min(index_set_4of4(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of4(x)) /\ ub(idx1) <= max(index_set_1of4(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of4(x)) /\ ub(idx2) <= max(index_set_2of4(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of4(x)) /\ ub(idx3) <= max(index_set_3of4(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of4(x)) /\ ub(idx4) <= max(index_set_4of4(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_mt( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + endif; + +function var int: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, + array[int,int,int,int,int] of var int: x) = + let { + int: dim2 = card(index_set_2of5(x)); + int: dim3 = card(index_set_3of5(x)); + int: dim4 = card(index_set_4of5(x)); + int: dim5 = card(index_set_5of5(x)); + + int: min = min(index_set_1of5(x))*dim2*dim3*dim4*dim5+ + min(index_set_2of5(x))*dim3*dim4*dim5+ + min(index_set_3of5(x))*dim4*dim5+ + min(index_set_4of5(x))*dim5+ + min(index_set_5of5(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of5(x)) /\ ub(idx1) <= max(index_set_1of5(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of5(x)) /\ ub(idx2) <= max(index_set_2of5(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of5(x)) /\ ub(idx3) <= max(index_set_3of5(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of5(x)) /\ ub(idx4) <= max(index_set_4of5(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of5(x)) /\ ub(idx5) <= max(index_set_5of5(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + endif; + +function var int: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, var int: idx6, + array[int,int,int,int,int,int] of var int: x) = + let { + int: dim2 = card(index_set_2of6(x)); + int: dim3 = card(index_set_3of6(x)); + int: dim4 = card(index_set_4of6(x)); + int: dim5 = card(index_set_5of6(x)); + int: dim6 = card(index_set_6of6(x)); + + int: min = min(index_set_1of6(x))*dim2*dim3*dim4*dim5*dim6+ + min(index_set_2of6(x))*dim3*dim4*dim5*dim6+ + min(index_set_3of6(x))*dim4*dim5*dim6+ + min(index_set_4of6(x))*dim5*dim6+ + min(index_set_5of6(x))*dim6+ + min(index_set_6of6(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of6(x)) /\ ub(idx1) <= max(index_set_1of6(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of6(x)) /\ ub(idx2) <= max(index_set_2of6(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of6(x)) /\ ub(idx3) <= max(index_set_3of6(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of6(x)) /\ ub(idx4) <= max(index_set_4of6(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of6(x)) /\ ub(idx5) <= max(index_set_5of6(x))) + /\ (has_bounds(idx6) /\ lb(idx6) >= min(index_set_6of6(x)) /\ ub(idx6) <= max(index_set_6of6(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + endif; + +%%%%%%%%%%%%%%%%%%% +% Element on floats + +function var float: element_t(var int: idx, array[int] of var float: x) :: promise_total = + let { + var lb_array(x)..ub_array(x): r ::is_defined_var; + constraint idx in index_set(x); + constraint array_var_float_element_nonshifted(idx,x,r) ::defines_var(r); + } in r; + +function var float: element_mt(var int: idx, array[int] of var float: x) :: promise_total = + let { + var lb_array(x)..ub_array(x): r ::is_defined_var; + var min(index_set(x))..max(index_set(x)): idx2; + constraint idx in index_set(x) -> idx2=idx; + constraint idx in index_set(x) \/ idx2=min(index_set(x)); + constraint array_var_float_element_nonshifted(idx2,x,r) ::defines_var(r); + } in r; + +function var float: element(var int: idx, array[int] of var float: x) = + if mzn_in_root_context(idx) then let { + constraint idx in index_set(x) + } in element_t(idx,x) + elseif (has_bounds(idx) /\ lb(idx) >= min(index_set(x)) /\ ub(idx) <= max(index_set(x))) then + element_t(idx,x) + else let { + constraint idx in index_set(x) + } in element_mt(idx,x) + endif; + +function var float: element(var int: idx1, var int: idx2, + array[int,int] of var float: x) = + let { + int: dim = card(index_set_2of2(x)); + int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of2(x)) /\ ub(idx1) <= max(index_set_1of2(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of2(x)) /\ ub(idx2) <= max(index_set_2of2(x))) ) + then + element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + else + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_mt( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + endif; + +function var float: element(var int: idx1, var int: idx2, var int: idx3, + array[int,int,int] of var float: x) = + let { + int: dim2 = card(index_set_2of3(x)); + int: dim3 = card(index_set_3of3(x)); + + int: min = min(index_set_1of3(x))*dim2*dim3+ + min(index_set_2of3(x))*dim3+ + min(index_set_3of3(x))-1; + } in if mzn_in_root_context(idx1) then let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of3(x)) /\ ub(idx1) <= max(index_set_1of3(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of3(x)) /\ ub(idx2) <= max(index_set_2of3(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of3(x)) /\ ub(idx3) <= max(index_set_3of3(x))) ) + then + element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_mt( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + endif; + +function var float: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, + array[int,int,int,int] of var float: x) = + let { + int: dim2 = card(index_set_2of4(x)); + int: dim3 = card(index_set_3of4(x)); + int: dim4 = card(index_set_4of4(x)); + + int: min = min(index_set_1of4(x))*dim2*dim3*dim4+ + min(index_set_2of4(x))*dim3*dim4+ + min(index_set_3of4(x))*dim4+ + min(index_set_4of4(x))-1; + } in if mzn_in_root_context(idx1) then let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of4(x)) /\ ub(idx1) <= max(index_set_1of4(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of4(x)) /\ ub(idx2) <= max(index_set_2of4(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of4(x)) /\ ub(idx3) <= max(index_set_3of4(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of4(x)) /\ ub(idx4) <= max(index_set_4of4(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_mt( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + endif; + +function var float: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, + array[int,int,int,int,int] of var float: x) = + let { + int: dim2 = card(index_set_2of5(x)); + int: dim3 = card(index_set_3of5(x)); + int: dim4 = card(index_set_4of5(x)); + int: dim5 = card(index_set_5of5(x)); + + int: min = min(index_set_1of5(x))*dim2*dim3*dim4*dim5+ + min(index_set_2of5(x))*dim3*dim4*dim5+ + min(index_set_3of5(x))*dim4*dim5+ + min(index_set_4of5(x))*dim5+ + min(index_set_5of5(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of5(x)) /\ ub(idx1) <= max(index_set_1of5(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of5(x)) /\ ub(idx2) <= max(index_set_2of5(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of5(x)) /\ ub(idx3) <= max(index_set_3of5(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of5(x)) /\ ub(idx4) <= max(index_set_4of5(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of5(x)) /\ ub(idx5) <= max(index_set_5of5(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + endif; + +function var float: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, var int: idx6, + array[int,int,int,int,int,int] of var float: x) = + let { + int: dim2 = card(index_set_2of6(x)); + int: dim3 = card(index_set_3of6(x)); + int: dim4 = card(index_set_4of6(x)); + int: dim5 = card(index_set_5of6(x)); + int: dim6 = card(index_set_6of6(x)); + + int: min = min(index_set_1of6(x))*dim2*dim3*dim4*dim5*dim6+ + min(index_set_2of6(x))*dim3*dim4*dim5*dim6+ + min(index_set_3of6(x))*dim4*dim5*dim6+ + min(index_set_4of6(x))*dim5*dim6+ + min(index_set_5of6(x))*dim6+ + min(index_set_6of6(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of6(x)) /\ ub(idx1) <= max(index_set_1of6(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of6(x)) /\ ub(idx2) <= max(index_set_2of6(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of6(x)) /\ ub(idx3) <= max(index_set_3of6(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of6(x)) /\ ub(idx4) <= max(index_set_4of6(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of6(x)) /\ ub(idx5) <= max(index_set_5of6(x))) + /\ (has_bounds(idx6) /\ lb(idx6) >= min(index_set_6of6(x)) /\ ub(idx6) <= max(index_set_6of6(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + endif; + +%%%%%%%%%%%%%%%%% +% Element on sets + +function var set of int: element_t(var int: idx, array[int] of var set of int: x) :: promise_total = + let { + var set of min(ub_array(x))..max(ub_array(x)): r ::is_defined_var; + constraint idx in index_set(x); + constraint array_var_set_element_nonshifted(idx,x,r) ::defines_var(r); + } in r; + +function var set of int: element_mt(var int: idx, array[int] of var set of int: x) :: promise_total = + let { + var set of min(ub_array(x))..max(ub_array(x)): r ::is_defined_var; + var min(index_set(x))..max(index_set(x)): idx2; + constraint idx in index_set(x) -> idx2=idx; + constraint idx in index_set(x) \/ idx2=min(index_set(x)); + constraint array_var_set_element_nonshifted(idx2,x,r) ::defines_var(r); + } in r; + +function var set of int: element(var int: idx, array[int] of var set of int: x) = + if mzn_in_root_context(idx) then let { + constraint idx in index_set(x) + } in element_t(idx,x) + elseif (has_bounds(idx) /\ lb(idx) >= min(index_set(x)) /\ ub(idx) <= max(index_set(x))) then + element_t(idx,x) + else let { + constraint idx in index_set(x) + } in element_mt(idx,x) + endif; + +function var set of int: element(var int: idx1, var int: idx2, + array[int,int] of var set of int: x) = + let { + int: dim = card(index_set_2of2(x)); + int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of2(x)) /\ ub(idx1) <= max(index_set_1of2(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of2(x)) /\ ub(idx2) <= max(index_set_2of2(x))) ) + then + element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + else + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_mt( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + endif; + +function var set of int: element(var int: idx1, var int: idx2, var int: idx3, + array[int,int,int] of var set of int: x) = + let { + int: dim2 = card(index_set_2of3(x)); + int: dim3 = card(index_set_3of3(x)); + + int: min = min(index_set_1of3(x))*dim2*dim3+ + min(index_set_2of3(x))*dim3+ + min(index_set_3of3(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of3(x)) /\ ub(idx1) <= max(index_set_1of3(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of3(x)) /\ ub(idx2) <= max(index_set_2of3(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of3(x)) /\ ub(idx3) <= max(index_set_3of3(x))) ) + then + element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_mt( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + endif; + +function var set of int: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, + array[int,int,int,int] of var set of int: x) = + let { + int: dim2 = card(index_set_2of4(x)); + int: dim3 = card(index_set_3of4(x)); + int: dim4 = card(index_set_4of4(x)); + + int: min = min(index_set_1of4(x))*dim2*dim3*dim4+ + min(index_set_2of4(x))*dim3*dim4+ + min(index_set_3of4(x))*dim4+ + min(index_set_4of4(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of4(x)) /\ ub(idx1) <= max(index_set_1of4(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of4(x)) /\ ub(idx2) <= max(index_set_2of4(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of4(x)) /\ ub(idx3) <= max(index_set_3of4(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of4(x)) /\ ub(idx4) <= max(index_set_4of4(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_mt( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + endif; + +function var set of int: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, + array[int,int,int,int,int] of var set of int: x) = + let { + int: dim2 = card(index_set_2of5(x)); + int: dim3 = card(index_set_3of5(x)); + int: dim4 = card(index_set_4of5(x)); + int: dim5 = card(index_set_5of5(x)); + + int: min = min(index_set_1of5(x))*dim2*dim3*dim4*dim5+ + min(index_set_2of5(x))*dim3*dim4*dim5+ + min(index_set_3of5(x))*dim4*dim5+ + min(index_set_4of5(x))*dim5+ + min(index_set_5of5(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of5(x)) /\ ub(idx1) <= max(index_set_1of5(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of5(x)) /\ ub(idx2) <= max(index_set_2of5(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of5(x)) /\ ub(idx3) <= max(index_set_3of5(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of5(x)) /\ ub(idx4) <= max(index_set_4of5(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of5(x)) /\ ub(idx5) <= max(index_set_5of5(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + endif; + +function var set of int: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, var int: idx6, + array[int,int,int,int,int,int] of var set of int: x) = + let { + int: dim2 = card(index_set_2of6(x)); + int: dim3 = card(index_set_3of6(x)); + int: dim4 = card(index_set_4of6(x)); + int: dim5 = card(index_set_5of6(x)); + int: dim6 = card(index_set_6of6(x)); + + int: min = min(index_set_1of6(x))*dim2*dim3*dim4*dim5*dim6+ + min(index_set_2of6(x))*dim3*dim4*dim5*dim6+ + min(index_set_3of6(x))*dim4*dim5*dim6+ + min(index_set_4of6(x))*dim5*dim6+ + min(index_set_5of6(x))*dim6+ + min(index_set_6of6(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of6(x)) /\ ub(idx1) <= max(index_set_1of6(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of6(x)) /\ ub(idx2) <= max(index_set_2of6(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of6(x)) /\ ub(idx3) <= max(index_set_3of6(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of6(x)) /\ ub(idx4) <= max(index_set_4of6(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of6(x)) /\ ub(idx5) <= max(index_set_5of6(x))) + /\ (has_bounds(idx6) /\ lb(idx6) >= min(index_set_6of6(x)) /\ ub(idx6) <= max(index_set_6of6(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + endif; + +%%%%%%%%%%%%%%%%%% +% Element on bools + +function var bool: element_t(var int: idx, array[int] of var bool: x) :: promise_total = + let { + var bool: r ::is_defined_var; + constraint idx in index_set(x); + constraint array_var_bool_element_nonshifted(idx,x,r) ::defines_var(r); + } in r; + +function var bool: element_mt(var int: idx, array[int] of var bool: x) :: promise_total = + let { + var bool: r ::is_defined_var; + var min(index_set(x))..max(index_set(x)): idx2; + constraint idx in index_set(x) -> idx2=idx; + constraint idx in index_set(x) \/ idx2=min(index_set(x)); + constraint array_var_bool_element_nonshifted(idx2,x,r) ::defines_var(r); + } in r; + +function var bool: element(var int: idx, array[int] of var bool: x) = + if mzn_in_root_context(idx) then + idx in index_set(x) /\ element_t(idx,x) + elseif (has_bounds(idx) /\ lb(idx) >= min(index_set(x)) /\ ub(idx) <= max(index_set(x))) then + element_t(idx,x) + else idx in index_set(x) /\ element_mt(idx,x) + endif; + +function var bool: element(var int: idx1, var int: idx2, + array[int,int] of var bool: x) = + let { + int: dim = card(index_set_2of2(x)); + int: min_flat = min(index_set_1of2(x))*dim+min(index_set_2of2(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of2(x)) /\ ub(idx1) <= max(index_set_1of2(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of2(x)) /\ ub(idx2) <= max(index_set_2of2(x))) ) + then + element_t( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + else + let { + constraint idx1 in index_set_1of2(x); + constraint idx2 in index_set_2of2(x); + } in element_mt( (idx1*dim+idx2-min_flat)::domain, array1d(x)) + endif; + +function var bool: element(var int: idx1, var int: idx2, var int: idx3, + array[int,int,int] of var bool: x) = + let { + int: dim2 = card(index_set_2of3(x)); + int: dim3 = card(index_set_3of3(x)); + + int: min = min(index_set_1of3(x))*dim2*dim3+ + min(index_set_2of3(x))*dim3+ + min(index_set_3of3(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of3(x)) /\ ub(idx1) <= max(index_set_1of3(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of3(x)) /\ ub(idx2) <= max(index_set_2of3(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of3(x)) /\ ub(idx3) <= max(index_set_3of3(x))) ) + then + element_t( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of3(x); + constraint idx2 in index_set_2of3(x); + constraint idx3 in index_set_3of3(x); + } in element_mt( (idx1*(dim2*dim3)+idx2*dim3+idx3-min)::domain, array1d(x)) + endif; + +function var bool: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, + array[int,int,int,int] of var bool: x) = + let { + int: dim2 = card(index_set_2of4(x)); + int: dim3 = card(index_set_3of4(x)); + int: dim4 = card(index_set_4of4(x)); + + int: min = min(index_set_1of4(x))*dim2*dim3*dim4+ + min(index_set_2of4(x))*dim3*dim4+ + min(index_set_3of4(x))*dim4+ + min(index_set_4of4(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of4(x)) /\ ub(idx1) <= max(index_set_1of4(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of4(x)) /\ ub(idx2) <= max(index_set_2of4(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of4(x)) /\ ub(idx3) <= max(index_set_3of4(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of4(x)) /\ ub(idx4) <= max(index_set_4of4(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of4(x); + constraint idx2 in index_set_2of4(x); + constraint idx3 in index_set_3of4(x); + constraint idx4 in index_set_4of4(x); + } in element_mt( (idx1*(dim2*dim3*dim4)+idx2*(dim3*dim4)+idx3*dim4+idx4-min)::domain, array1d(x)) + endif; + +function var bool: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, + array[int,int,int,int,int] of var bool: x) = + let { + int: dim2 = card(index_set_2of5(x)); + int: dim3 = card(index_set_3of5(x)); + int: dim4 = card(index_set_4of5(x)); + int: dim5 = card(index_set_5of5(x)); + + int: min = min(index_set_1of5(x))*dim2*dim3*dim4*dim5+ + min(index_set_2of5(x))*dim3*dim4*dim5+ + min(index_set_3of5(x))*dim4*dim5+ + min(index_set_4of5(x))*dim5+ + min(index_set_5of5(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of5(x)) /\ ub(idx1) <= max(index_set_1of5(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of5(x)) /\ ub(idx2) <= max(index_set_2of5(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of5(x)) /\ ub(idx3) <= max(index_set_3of5(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of5(x)) /\ ub(idx4) <= max(index_set_4of5(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of5(x)) /\ ub(idx5) <= max(index_set_5of5(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of5(x); + constraint idx2 in index_set_2of5(x); + constraint idx3 in index_set_3of5(x); + constraint idx4 in index_set_4of5(x); + constraint idx5 in index_set_5of5(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5)+idx2*(dim3*dim4*dim5)+idx3*(dim4*dim5)+idx4*dim5+idx5-min)::domain, array1d(x)) + endif; + +function var bool: element(var int: idx1, var int: idx2, var int: idx3, var int: idx4, var int: idx5, var int: idx6, + array[int,int,int,int,int,int] of var bool: x) = + let { + int: dim2 = card(index_set_2of6(x)); + int: dim3 = card(index_set_3of6(x)); + int: dim4 = card(index_set_4of6(x)); + int: dim5 = card(index_set_5of6(x)); + int: dim6 = card(index_set_6of6(x)); + + int: min = min(index_set_1of6(x))*dim2*dim3*dim4*dim5*dim6+ + min(index_set_2of6(x))*dim3*dim4*dim5*dim6+ + min(index_set_3of6(x))*dim4*dim5*dim6+ + min(index_set_4of6(x))*dim5*dim6+ + min(index_set_5of6(x))*dim6+ + min(index_set_6of6(x))-1; + } in if mzn_in_root_context(idx1) then + let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + elseif ( + (has_bounds(idx1) /\ lb(idx1) >= min(index_set_1of6(x)) /\ ub(idx1) <= max(index_set_1of6(x))) + /\ (has_bounds(idx2) /\ lb(idx2) >= min(index_set_2of6(x)) /\ ub(idx2) <= max(index_set_2of6(x))) + /\ (has_bounds(idx3) /\ lb(idx3) >= min(index_set_3of6(x)) /\ ub(idx3) <= max(index_set_3of6(x))) + /\ (has_bounds(idx4) /\ lb(idx4) >= min(index_set_4of6(x)) /\ ub(idx4) <= max(index_set_4of6(x))) + /\ (has_bounds(idx5) /\ lb(idx5) >= min(index_set_5of6(x)) /\ ub(idx5) <= max(index_set_5of6(x))) + /\ (has_bounds(idx6) /\ lb(idx6) >= min(index_set_6of6(x)) /\ ub(idx6) <= max(index_set_6of6(x))) ) + then + element_t( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + else let { + constraint idx1 in index_set_1of6(x); + constraint idx2 in index_set_2of6(x); + constraint idx3 in index_set_3of6(x); + constraint idx4 in index_set_4of6(x); + constraint idx5 in index_set_5of6(x); + constraint idx6 in index_set_6of6(x); + } in element_mt( (idx1*(dim2*dim3*dim4*dim5*dim6)+idx2*(dim3*dim4*dim5*dim6)+idx3*(dim4*dim5*dim6)+idx4*(dim5*dim6)+idx5*dim6+idx6-min)::domain, array1d(x)) + endif; + +%-----------------------------------------------------------------------------% +% +% Internal functions for implementing div, mod etc + +function set of int:compute_div_bounds(var int: x, var int: y); + +function var int: div_t(var int: x, var int: y) :: promise_total = + let { + var (compute_div_bounds(x,y)): z ::is_defined_var; + constraint y != 0; + constraint int_div(x,y,z) ::defines_var(z); } in z; + +function var int: div_mt(var int: x, var int: y) :: promise_total = + let { + var ((dom(y) diff {0}) union {1}): yy = if y=0 then 1 else y endif; + } in div_t(x,yy); + +function var float: fldiv_t(var float: x, var float: y) :: promise_total = + let { + var float: z ::is_defined_var; % TODO: Compute division boundaries + constraint y != 0.0; + constraint float_div(x, y, z) ::defines_var(z); } in z; + +function var float: fldiv_mt(var float: x, var float: y) :: promise_total = + let { + var (lb(y)..ub(y) diff {0.0}) union {1.0}: yy = if (y = 0.0) then 1.0 else y endif; + } in fldiv_t(x, yy); + +function var int: mod_t(var int: x, var int: y) :: promise_total = + let { var -(max(ub(y),-lb(y)))..max(ub(y),-lb(y)): z; + constraint y != 0; + constraint int_mod(x,y,z); } in z; + +function var int: mod_mt(var int: x, var int: y) :: promise_total = + let { + var {1} union dom(y): yy = if y=0 then 1 else y endif; + } in mod_t(x,yy); + +function var int: product_rec(array[int] of var int: x) = + if length(x)=0 then 1 + elseif length(x)=1 then x[min(index_set(x))] + else x[min(index_set(x))]* + product_rec([x[i] | i in min(index_set(x))+1..max(index_set(x))]) + endif; + +function var float: product_rec(array[int] of var float: x) = + if length(x)=0 then 1.0 + elseif length(x)=1 then x[min(index_set(x))] + else x[min(index_set(x))]* + product_rec([x[i] | i in min(index_set(x))+1..max(index_set(x))]) + endif; + +function var int: max_t(array[int] of var int: x) :: promise_total = + if length(x)=0 then 0 + elseif length(x)=1 then x[min(index_set(x))] + elseif length(x)=2 then max(x[1],x[2]) + else let { + var lb_array(x)..ub_array(x): m ::is_defined_var; + constraint array_int_maximum(m,x) ::defines_var(m); + } in m + endif; + +function var int: min_t(array[int] of var int: x) :: promise_total = + if length(x)=0 then 0 + elseif length(x)=1 then x[1] + elseif length(x)=2 then min(x[1],x[2]) + else let { + var lb_array(x)..ub_array(x): m ::is_defined_var; + constraint array_int_minimum(m,x) ::defines_var(m); + } in m + endif; + +function var float: max_t(array[int] of var float: x) :: promise_total = + if length(x)=0 then 0.0 + elseif length(x)=1 then x[min(index_set(x))] + elseif length(x)=2 then max(x[1],x[2]) + else let { + var lb_array(x)..ub_array(x): m ::is_defined_var; + constraint array_float_maximum(m,x) ::defines_var(m); + } in m + endif; + +function var float: min_t(array[int] of var float: x) :: promise_total = + if length(x)=0 then 0.0 + elseif length(x)=1 then x[1] + elseif length(x)=2 then min(x[1],x[2]) + else let { + var lb_array(x)..ub_array(x): m ::is_defined_var; + constraint array_float_minimum(m,x) ::defines_var(m); + } in m + endif; + +/*** + @groupdef builtins.random Random Number Generator builtins + + These functions implement random number generators from different + probability distributions. +*/ + +/** @group builtins.random Return a sample from the normal distribution defined by \(\a mean, \a std\) */ +function float: normal(float: mean, float: std); + +/** @group builtins.random Return a sample from the normal distribution defined by \(\a mean, \a std\) */ +function float: normal(int: mean, float: std); + +/** @group builtins.random Return a sample from the uniform distribution defined by \(\a lowerbound, \a upperbound\) */ +function float: uniform(float: lowerbound, float: upperbound); + +/** @group builtins.random Return a sample from the uniform distribution defined by \(\a lowerbound, \a upperbound\) */ +function int: uniform(int: lowerbound, int: upperbound); + +/** @group builtins.random Return a sample from the poisson distribution defined by \a mean */ +function int: poisson(float: mean); + +/** @group builtins.random Return a sample from the poisson distribution defined by an integer \a mean */ +function int: poisson(int: mean); + +/** @group builtins.random Return a sample from the gamma distribution defined by \(\a alpha, \a beta\) */ +function float: gamma(float: alpha, float: beta); + +/** @group builtins.random Return a sample from the gamma distribution defined by \(\a alpha, \a beta\) */ +function float: gamma(int: alpha, float: beta); + +/** @group builtins.random Return a sample from the Weibull distribution defined by \(\a shape, \a scale\) */ +function float: weibull(float: shape, float: scale); + +/** @group builtins.random Return a sample from the Weibull distribution defined by \(\a shape, \a scale\) */ +function float: weibull(int: shape, float: scale); + +/** @group builtins.random Return a sample from the exponential distribution defined by \(\a lambda\) */ +function float: exponential(int: lambda); + +/** @group builtins.random Return a sample from the exponential distribution defined by \(\a lambda\) */ +function float: exponential(float: lambda); + +/** @group builtins.random Return a sample from the lognormal distribution defined by \(\a mean, \a std\) */ +function float: lognormal(float: mean, float: std); + +/** @group builtins.random Return a sample from the lognormal distribution defined by \(\a mean, \a std\) */ +function float: lognormal(int: mean, float: std); + +/** @group builtins.random Return a sample from the chi-squared distribution defined by the degree of freedom \(\a n\) */ +function float: chisquared(int: n); + +/** @group builtins.random Return a sample from the chi-squared distribution defined by the degree of freedom \(\a n\) */ +function float: chisquared(float: n); + +/** @group builtins.random Return a sample from the cauchy distribution defined by \(\a mean, \a scale\) */ +function float: cauchy(float: mean, float: scale); + +/** @group builtins.random Return a sample from the cauchy distribution defined by \(\a mean, \a scale\) */ +function float: cauchy(int: mean, float: scale); + +/** @group builtins.random Return a sample from the Fisher-Snedecor F-distribution defined by the degrees of freedom \(\a d1, \a d2\) */ +function float: fdistribution(float: d1, float: d2); + +/** @group builtins.random Return a sample from the Fisher-Snedecor F-distribution defined by the degrees of freedom \(\a d1, \a d2\) */ +function float: fdistribution(int: d1, int: d2); + +/** @group builtins.random Return a sample from the student's t-distribution defined by the sample size \(\a n\) */ +function float: tdistribution(float: n); + +/** @group builtins.random Return a sample from the student's t-distribution defined by the sample size \(\a n\) */ +function float: tdistribution(int: n); + +/** @group builtins.random Return a sample from the discrete distribution defined by the array of weights \(\a weights\) that assigns a weight to each integer starting from zero */ +function int: discrete_distribution(array[int] of int: weights); + +/** @group builtins.random Return a boolean sample from the Bernoulli distribution defined by probability \(\a p\) */ +function bool: bernoulli(float: p); + +/** @group builtins.random Return a sample from the binomial distribution defined by sample number \a t and probability \a p */ +function int: binomial(int: t, float: p); + +/*** + @groupdef builtins.special Special constraints + + These predicates allow users to mark constraints as e.g. symmetry breaking + or redundant, so that solvers can choose to implement them differently. + + We cannot easily use annotations for this purpose, since annotations are + propagated to all constraints in a decomposition, which may be incorrect + for redundant or symmetry breaking constraints in the presence of + common subexpression elimination (CSE). +*/ + +/** @group builtins.special Mark \a b as a symmetry breaking constraint */ +predicate symmetry_breaking_constraint(var bool: b); + +/** @group builtins.special Mark \a b as a redundant constraint */ +predicate redundant_constraint(var bool: b); + +/** @group builtins.special Mark \a b as an implied constraint (synonym for redundant_constraint) */ +predicate implied_constraint(var bool: b) = redundant_constraint(b); + +function set of int: anon_enum(int: n) = 1..n; + +function set of int: anon_enum(array[int] of string: x); + +/*** + @groupdef builtins.language Language information + + These functions return information about the MiniZinc system. +*/ + +/** @group builtins.language Return MiniZinc version encoded as an integer (major*10000+minor*1000+patch). */ +function int: mzn_compiler_version(); + +/** @group builtins.language Return string representation of \a v given an integer major*10000+minor*1000+patch +*/ +function string: mzn_version_to_string(int: v) = + show(v div 10000)++"."++show((v div 1000) mod 10)++"."++show(v mod 100); + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% + +% Include solver-specific redefinitions for any FlatZinc built-ins. +% +include "redefinitions.mzn"; +include "redefinitions-2.0.mzn"; +include "redefinitions-2.0.2.mzn"; +include "redefinitions-2.1.mzn"; +include "redefinitions-2.1.1.mzn"; +include "redefinitions-2.2.1.mzn"; + +%-----------------------------------------------------------------------------% +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/circuit.mzn b/share/minizinc/std/circuit.mzn new file mode 100644 index 0000000..87a9a1f --- /dev/null +++ b/share/minizinc/std/circuit.mzn @@ -0,0 +1,14 @@ +include "fzn_circuit.mzn"; +include "fzn_circuit_reif.mzn"; + +/** @group globals + Constrains the elements of \a x to define a circuit where \a x[\p i] = \p j means + that \p j is the successor of \p i. +*/ +predicate circuit(array[int] of var int: x) = + fzn_circuit(x); + +predicate circuit_reif(array[int] of var int: x, var bool: b) = + fzn_circuit_reif(x, b); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/comparison_rel_array.mzn b/share/minizinc/std/comparison_rel_array.mzn new file mode 100644 index 0000000..9d22def --- /dev/null +++ b/share/minizinc/std/comparison_rel_array.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Reflect an array of comparison values onto a comparison value variable using +% a lexicographic interpretation of the array. The comparison values are +% encoded as follows: > | = | < as -1 | 0 | +1. +% Uses of this constraint are generated by Cadmium transformations that +% simplify ordering constraints on expressions of complex types. +%-----------------------------------------------------------------------------% +predicate comparison_rel_array(array[int] of var -1..1: rels, var -1..1: rel) = + let { int: l = min(index_set(rels)), + int: u = max(index_set(rels)), + array[l-1..u] of var -1..1: r } + in + r[l-1] = 0 % initial state (before first array position) is 'equal' + /\ + forall (i in l..u) ( + % new state: as given array at current position if + % previous state is 'equal', otherwise previous state + % + % r[i] = (if r[i-1] = 0 then rels[i] else r[i-1] endif) + (r[i-1] = 0 -> r[i] = rels[i]) + /\ + (r[i-1] != 0 -> r[i] = r[i-1]) + ) + /\ + r[u] = rel; % final state (at last array position) + diff --git a/share/minizinc/std/connected.mzn b/share/minizinc/std/connected.mzn new file mode 100644 index 0000000..7c640f9 --- /dev/null +++ b/share/minizinc/std/connected.mzn @@ -0,0 +1,39 @@ +include "fzn_connected.mzn"; +include "fzn_connected_reif.mzn"; +include "fzn_dconnected.mzn"; +include "fzn_dconnected_reif.mzn"; + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be connected. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate dconnected(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = index_set(to),"dconnected: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"dconnected: index set of from and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"dconnected: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"dconnected: nodes in to must be in index set of ns") /\ + fzn_dconnected(from,to,ns,es); + +%-----------------------------------------------------------------------------% + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given undirected graph to be connected. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate connected(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = index_set(to),"connected: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"connected: index set of from and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"connected: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"connected: nodes in to must be in index set of ns") /\ + fzn_connected(from,to,ns,es); + \ No newline at end of file diff --git a/share/minizinc/std/cost_mdd.mzn b/share/minizinc/std/cost_mdd.mzn new file mode 100644 index 0000000..30f4313 --- /dev/null +++ b/share/minizinc/std/cost_mdd.mzn @@ -0,0 +1,83 @@ +include "fzn_cost_mdd.mzn"; +include "fzn_cost_mdd_reif.mzn"; + +/** @group globals.extensional + Requires that \a x defines a path in the cost MDD with total edge weight \a totalcost. + + \a N is the number of nodes, the root node is node 1 + \a level is the level of each node, the root is level 1, T is level \a length(x)+1 + \a E is the number of edges + \a from is the leaving node (1..\a N)for each edge + \a label is the set of value of the x variable for each edge + \a cost is the cost for each edge + \a to is the entering node for each edge, where 0 = T node + \a totalcost is the total cost of the path defined by \a x + +*/ +predicate cost_mdd(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % values of variable on edge + array[int] of int: cost, % cost of using edge + array[int] of int: to, % edge entering node 0..N where 0 = T node + var int: totalcost % total cost of path + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of int: levele = array1d(0..N,[L+1]++level); + } in + assert(index_set(level) = NODE, + "cost_mdd: level argument must be of length N = \(N)") /\ + assert(level[1] = 1, "cost_mdd: level of root (1) must be 1") /\ + forall(n in 2..N)(assert(level[n] != 1, "cost_mdd: level of non root node (\(n)) must not be 1")) /\ + assert(index_set(from) = EDGE, + "cost_mdd: from argument must be of length E = \(E)") /\ + assert(index_set(to) = EDGE, + "cost_mdd: to argument must be of length E = \(E)") /\ + assert(index_set(label) = EDGE, + "cost_mdd: label argument must be of length E = \(E)") /\ + assert(index_set(cost) = EDGE, + "cost_mdd: cost argument must be of length E = \(E)") /\ + forall(e in EDGE)(assert(from[e] in NODE, + "cost_mdd: from[\(e)] must be in \(NODE)")) /\ + forall(e in EDGE)(assert(to[e] in 0..N, + "cost_mdd: to[\(e)] must be in 0..\(N)")) /\ + forall(e in EDGE)(assert(level[from[e]]+1 = levele[to[e]], + "cost_mdd: mdd level of from[\(e)] = \(level[from[e]])" ++ + "must be 1 less than level of to[\(e)] = \(levele[to[e]])")) /\ + forall(e1,e2 in EDGE where e1 < e2 /\ from[e1] = from[e2]) + (assert(label[e1] intersect label[e2] = {}, + "cost_mdd: Two edges \(e1) and \(e2) leaving node \(from[e1]) with overlapping labels")) /\ + fzn_cost_mdd(x,N,level,E,from,label,cost,to,totalcost); + +predicate cost_mdd_reif(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % values of variable on edge + array[int] of int: cost, % cost of using edge + array[int] of int: to, % edge entering node 0..N where 0 = T node + var int: totalcost, % total cost of path + var bool: b % reification variable + ) = + fzn_cost_mdd_reif(x, N, level, E, from, label, cost, to, totalcost, b); + +% Example consider an MDD over 3 variables +% 5 nodes and 8 edges +% level 1 root = 1 +% level 2 2 3 +% level 3 4 5 +% level 4 T +% with edges (from,label,cost,to) given by +% (1,{1,3},3,2), (1,{2},1,3), +% (2,{2},4,4), (2,{3},2,5), +% (3,{3},3,4), (3,{2},5,5), +% (4,{1,5},2,0), +% (5,{2,4,6},4,0) +% this is defined by the call +% cost_mdd([x1,x2,x3],5,[1,2,2,3,3],8,[1,1,2,2,3,3,4,5], +% [{1,3},{2},{2},{3},{3},{2},{1,5},{2,4,6}],[3,1,4,2,3,5,2,4],[2,3,4,5,4,5,0,0],tc) \ No newline at end of file diff --git a/share/minizinc/std/cost_regular.mzn b/share/minizinc/std/cost_regular.mzn new file mode 100644 index 0000000..d189eef --- /dev/null +++ b/share/minizinc/std/cost_regular.mzn @@ -0,0 +1,40 @@ +include "fzn_cost_regular.mzn"; +include "fzn_cost_regular_reif.mzn"; + +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the range 1..\a S) + is accepted by the DFA of \a Q states with input 1..\a S and transition + function \a d (which maps (1..\a Q, 1..\a S) -> 0..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). We reserve state 0 to be an always failing state. Each edge has an associated cost \a c, + and \a C is the sum of costs taken on the accepting path for \a x. +*/ +predicate cost_regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F, + array[int,int] of int: c, var int: C) = + assert(Q > 0, + "cost_regular: 'Q' must be greater than zero", + + assert(S > 0, + "cost_regular: 'S' must be greater than zero", + + assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) == 1..S, + "cost_regular: the transition function 'd' must be [1..Q,1..S]", + + assert(index_set_1of2(c) = 1..Q /\ index_set_2of2(c) == 1..S, + "cost_regular: the transition cost function 'c' must be [1..Q,1..S]", + + assert(forall([d[i, j] in 0..Q | i in 1..Q, j in 1..S]), + "cost_regular: transition function 'd' points to states outside 0..Q", + + % Nb: we need the parentheses around the expression otherwise the + % parser thinks it's a generator call! + assert((q0 in 1..Q), + "cost_regular: start state 'q0' not in 1..Q", + + assert(F subset 1..Q, + "cost_regular: final states in 'F' contain states outside 1..Q", + + fzn_cost_regular(x,Q,S,d,q0,F,c,C) + + ))))))); diff --git a/share/minizinc/std/count.mzn b/share/minizinc/std/count.mzn new file mode 100644 index 0000000..5fb728a --- /dev/null +++ b/share/minizinc/std/count.mzn @@ -0,0 +1,7 @@ +include "count_eq.mzn"; + +/** @group globals.counting + Constrains \a c to be the number of occurrences of \a y in \a x. +*/ +predicate count(array[int] of var int: x, var int: y, var int: c) = + count_eq(x, y, c); diff --git a/share/minizinc/std/count_eq.mzn b/share/minizinc/std/count_eq.mzn new file mode 100644 index 0000000..f8024b5 --- /dev/null +++ b/share/minizinc/std/count_eq.mzn @@ -0,0 +1,22 @@ +include "fzn_count_eq.mzn"; +include "fzn_count_eq_reif.mzn"; + +/** @group globals.counting + Constrains \a c to be the number of occurrences of \a y in \a x. +*/ +predicate count_eq(array[int] of var int: x, var int: y, var int: c) = + fzn_count_eq(x,y,c); + +/** @group globals.counting + Returns the number of occurrences of \a y in \a x. +*/ +function var int: count_eq(array[int] of var int: x, var int: y) ::promise_total = + let { var 0..length(x): c; constraint fzn_count_eq(x,y,c); } in c; + +function int: count_eq(array[int] of int: x, int: y) = + sum(i in index_set(x))(x[i] = y); + +predicate count_eq_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + fzn_count_eq_reif(x, y, c, b); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/count_fn.mzn b/share/minizinc/std/count_fn.mzn new file mode 100644 index 0000000..5704513 --- /dev/null +++ b/share/minizinc/std/count_fn.mzn @@ -0,0 +1,7 @@ +include "count.mzn"; + +/** @group globals.counting + Returns the number of occurrences of \a y in \a x. +*/ +function var int: count(array[int] of var int: x, var int: y) ::promise_total = + let { var 0..length(x): c; constraint count(x,y,c); } in c; diff --git a/share/minizinc/std/count_geq.mzn b/share/minizinc/std/count_geq.mzn new file mode 100644 index 0000000..9039f08 --- /dev/null +++ b/share/minizinc/std/count_geq.mzn @@ -0,0 +1,14 @@ +include "fzn_count_geq.mzn"; +include "count_fn.mzn"; + +/** @group globals.counting + Constrains \a c to be greater than or equal to the number of occurrences + of \a y in \a x. +*/ +predicate count_geq(array[int] of var int: x, var int: y, var int: c) = + fzn_count_geq(x,y,c); + +predicate count_geq_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + b <-> count(x,y) <= c; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/count_gt.mzn b/share/minizinc/std/count_gt.mzn new file mode 100644 index 0000000..6bef720 --- /dev/null +++ b/share/minizinc/std/count_gt.mzn @@ -0,0 +1,14 @@ +include "fzn_count_gt.mzn"; +include "count_fn.mzn"; + +/** @group globals.counting + Constrains \a c to be strictly greater than the number of occurrences + of \a y in \a x. +*/ +predicate count_gt(array[int] of var int: x, var int: y, var int: c) = + fzn_count_gt(x,y,c); + +predicate count_gt_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + b <-> count(x,y) < c; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/count_leq.mzn b/share/minizinc/std/count_leq.mzn new file mode 100644 index 0000000..ebe731e --- /dev/null +++ b/share/minizinc/std/count_leq.mzn @@ -0,0 +1,14 @@ +include "fzn_count_leq.mzn"; +include "count_fn.mzn"; + +/** @group globals.counting + Constrains \a c to be less than or equal to the number of occurrences + of \a y in \a x. +*/ +predicate count_leq(array[int] of var int: x, var int: y, var int: c) = + fzn_count_leq(x,y,c); + +predicate count_leq_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + b <-> count(x,y) >= c; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/count_lt.mzn b/share/minizinc/std/count_lt.mzn new file mode 100644 index 0000000..5f91494 --- /dev/null +++ b/share/minizinc/std/count_lt.mzn @@ -0,0 +1,14 @@ +include "fzn_count_lt.mzn"; +include "count_fn.mzn"; + +/** @group globals.counting + Constrains \a c to be strictly less than the number of occurrences + of \a y in \a x. +*/ +predicate count_lt(array[int] of var int: x, var int: y, var int: c) = + fzn_count_lt(x,y,c); + +predicate count_lt_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + b <-> count(x,y) > c; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/count_neq.mzn b/share/minizinc/std/count_neq.mzn new file mode 100644 index 0000000..23a014b --- /dev/null +++ b/share/minizinc/std/count_neq.mzn @@ -0,0 +1,14 @@ +include "fzn_count_neq.mzn"; +include "count_fn.mzn"; + +/** @group globals.counting + Constrains \a c to be not equal to the number of occurrences + of \a y in \a x. +*/ +predicate count_neq(array[int] of var int: x, var int: y, var int: c) = + fzn_count_neq(x,y,c); + +predicate count_neq_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + b <-> count(x,y) != c; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/cumulative.mzn b/share/minizinc/std/cumulative.mzn new file mode 100644 index 0000000..6da41a3 --- /dev/null +++ b/share/minizinc/std/cumulative.mzn @@ -0,0 +1,34 @@ +include "all_different.mzn"; +include "disjunctive.mzn"; +include "fzn_cumulative.mzn"; +include "fzn_cumulative_reif.mzn"; + +/** @group globals.scheduling + Requires that a set of tasks given by start times \a s, durations \a d, and + resource requirements \a r, never require more than a global resource bound + \a b at any one time. + + Assumptions: + - forall \p i, \a d[\p i] >= 0 and \a r[\p i] >= 0 +*/ +predicate cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + if is_fixed(b) /\ + forall(i in index_set(r)) + (is_fixed(r[i]) /\ fix(r[i]) + lb_array(r) > ub(b)) + then + if forall(i in index_set(d))(is_fixed(d[i]) /\ fix(d[i]) == 1) + then + all_different(s) + else + disjunctive(s, d) + endif + else + fzn_cumulative(s, d, r, b) + endif + )); diff --git a/share/minizinc/std/cumulative_opt.mzn b/share/minizinc/std/cumulative_opt.mzn new file mode 100644 index 0000000..99162af --- /dev/null +++ b/share/minizinc/std/cumulative_opt.mzn @@ -0,0 +1,22 @@ +include "fzn_cumulative_opt.mzn"; +include "fzn_cumulative_opt_reif.mzn"; + +/** @group globals.scheduling + Requires that a set of tasks given by start times \a s, durations \a d, and + resource requirements \a r, never require more than a global resource bound + \a b at any one time. Start times are optional variables, so + that absent tasks do not need to be scheduled. + + Assumptions: + - forall \p i, \a d[\p i] >= 0 and \a r[\p i] >= 0 +*/ +predicate cumulative(array[int] of var opt int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + assert(index_set(s) == index_set(d) /\ index_set(s) == index_set(r), + "cumulative: the 3 array arguments must have identical index sets", + assert(lb_array(d) >= 0 /\ lb_array(r) >= 0, + "cumulative: durations and resource usages must be non-negative", + fzn_cumulative_opt(s,d,r,b) + ) + ); diff --git a/share/minizinc/std/dag.mzn b/share/minizinc/std/dag.mzn new file mode 100644 index 0000000..afdfbf6 --- /dev/null +++ b/share/minizinc/std/dag.mzn @@ -0,0 +1,18 @@ +include "fzn_dag.mzn"; +include "fzn_dag_reif.mzn"; + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be a DAG. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate dag(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = index_set(to),"dreachable: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"dreachable: index set of from and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"dreachable: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"dreachable: nodes in to must be in index set of ns") /\ + fzn_dag(from,to,ns,es); diff --git a/share/minizinc/std/decreasing.mzn b/share/minizinc/std/decreasing.mzn new file mode 100644 index 0000000..a2099dc --- /dev/null +++ b/share/minizinc/std/decreasing.mzn @@ -0,0 +1,20 @@ +include "decreasing_bool.mzn"; +include "decreasing_float.mzn"; +include "decreasing_int.mzn"; +include "decreasing_set.mzn"; + +/** @group globals.sort Requires that the array \a x is in decreasing order (duplicates are allowed). */ +predicate decreasing(array[int] of var bool: x) = + decreasing_bool(x); + +/** @group globals.sort Requires that the array \a x is in decreasing order (duplicates are allowed). */ +predicate decreasing(array[int] of var float: x) = + decreasing_float(x); + +/** @group globals.sort Requires that the array \a x is in decreasing order (duplicates are allowed). */ +predicate decreasing(array[int] of var int: x) = + decreasing_int(x); + +/** @group globals.sort Requires that the array \a x is in decreasing order (duplicates are allowed). */ +predicate decreasing(array[int] of var set of int: x) = + decreasing_set(x); diff --git a/share/minizinc/std/decreasing_bool.mzn b/share/minizinc/std/decreasing_bool.mzn new file mode 100644 index 0000000..c96d133 --- /dev/null +++ b/share/minizinc/std/decreasing_bool.mzn @@ -0,0 +1,9 @@ +include "fzn_decreasing_bool.mzn"; +include "fzn_decreasing_bool_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate decreasing_bool(array[int] of var bool: x) = + fzn_decreasing_bool(x); diff --git a/share/minizinc/std/decreasing_float.mzn b/share/minizinc/std/decreasing_float.mzn new file mode 100644 index 0000000..aae0381 --- /dev/null +++ b/share/minizinc/std/decreasing_float.mzn @@ -0,0 +1,8 @@ +include "fzn_decreasing_float.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate decreasing_float(array[int] of var float: x) = + fzn_decreasing_float(x); diff --git a/share/minizinc/std/decreasing_int.mzn b/share/minizinc/std/decreasing_int.mzn new file mode 100644 index 0000000..009e732 --- /dev/null +++ b/share/minizinc/std/decreasing_int.mzn @@ -0,0 +1,9 @@ +include "fzn_decreasing_int.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate decreasing_int(array[int] of var int: x) = + fzn_decreasing_int(x); + diff --git a/share/minizinc/std/decreasing_set.mzn b/share/minizinc/std/decreasing_set.mzn new file mode 100644 index 0000000..2fd6b88 --- /dev/null +++ b/share/minizinc/std/decreasing_set.mzn @@ -0,0 +1,9 @@ +include "fzn_decreasing_set.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate decreasing_set(array[int] of var set of int: x) = + fzn_decreasing_set(x); + diff --git a/share/minizinc/std/diffn.mzn b/share/minizinc/std/diffn.mzn new file mode 100644 index 0000000..bb9f935 --- /dev/null +++ b/share/minizinc/std/diffn.mzn @@ -0,0 +1,21 @@ +include "fzn_diffn.mzn"; +include "fzn_diffn_reif.mzn"; + +/** @group globals.packing + Constrains rectangles \p i, given by their origins (\a x[\p i], \a y[\p i]) + and sizes (\a dx[\p i], \a dy[\p i]), to be non-overlapping. Zero-width + rectangles can still not overlap with any other rectangle. +*/ +predicate diffn(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy) = + assert( + index_set(x) = index_set(y) /\ + index_set(x) = index_set(dx) /\ + index_set(x) = index_set(dy), + "diffn: index set mismatch", + fzn_diffn(x,y,dx,dy) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/diffn_k.mzn b/share/minizinc/std/diffn_k.mzn new file mode 100644 index 0000000..1c342ae --- /dev/null +++ b/share/minizinc/std/diffn_k.mzn @@ -0,0 +1,17 @@ +include "fzn_diffn_k.mzn"; +include "fzn_diffn_k_reif.mzn"; + +/** @group globals.packing + Constrains \p k-dimensional boxes to be non-overlapping. For each box \p i + and dimension \p j, \a box_posn[\p i, \p j] is the base position of the box + in dimension \p j, and \a box_size[\p i, \p j] is the size in that dimension. + Boxes whose size is 0 in any dimension still cannot overlap with any other box. +*/ +predicate diffn_k(array[int,int] of var int: box_posn, + array[int,int] of var int: box_size) = + let { set of int: DIMS= index_set_2of2(box_posn) } in + assert(index_set_2of2(box_size) = DIMS /\ + index_set_1of2(box_posn) = index_set_1of2(box_size), + "diffn: index sets of arguments are incorrect", + fzn_diffn_k(box_posn, box_size) + ); diff --git a/share/minizinc/std/diffn_nonstrict.mzn b/share/minizinc/std/diffn_nonstrict.mzn new file mode 100644 index 0000000..67343a7 --- /dev/null +++ b/share/minizinc/std/diffn_nonstrict.mzn @@ -0,0 +1,19 @@ +include "fzn_diffn_nonstrict.mzn"; +include "fzn_diffn_nonstrict_reif.mzn"; + +/** @group globals.packing + Constrains rectangles \p i, given by their origins (\a x[\p i], \a y[\p i]) + and sizes (\a dx[\p i], \a dy[\p i]), to be non-overlapping. Zero-width + rectangles can be packed anywhere. +*/ +predicate diffn_nonstrict(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy) = + assert( + index_set(x) = index_set(y) /\ + index_set(x) = index_set(dx) /\ + index_set(x) = index_set(dy), + "diffn: index set mismatch", + fzn_diffn_nonstrict(x,y,dx,dy) + ); diff --git a/share/minizinc/std/diffn_nonstrict_k.mzn b/share/minizinc/std/diffn_nonstrict_k.mzn new file mode 100644 index 0000000..6a6235a --- /dev/null +++ b/share/minizinc/std/diffn_nonstrict_k.mzn @@ -0,0 +1,17 @@ +include "fzn_diffn_nonstrict_k.mzn"; +include "fzn_diffn_nonstrict_k_reif.mzn"; + +/** @group globals.packing + Constrains \p k-dimensional boxes to be non-overlapping. For each box \p i + and dimension \p j, \a box_posn[\p i, \p j] is the base position of the box + in dimension \p j, and \a box_size[\p i, \p j] is the size in that dimension. + Boxes whose size is 0 in at least one dimension can be packed anywhere. +*/ +predicate diffn_nonstrict_k(array[int,int] of var int: box_posn, + array[int,int] of var int: box_size) = + let { set of int: DIMS= index_set_2of2(box_posn) } in + assert(index_set_2of2(box_size) = DIMS /\ + index_set_1of2(box_posn) = index_set_1of2(box_size), + "diffn: index sets of arguments are incorrect", + fzn_diffn_nonstrict_k(box_posn, box_size) + ) diff --git a/share/minizinc/std/disjoint.mzn b/share/minizinc/std/disjoint.mzn new file mode 100644 index 0000000..2ef6955 --- /dev/null +++ b/share/minizinc/std/disjoint.mzn @@ -0,0 +1,6 @@ +include "fzn_disjoint.mzn"; +include "fzn_disjoint_reif.mzn"; + +/** @group globals Requires that sets \a s1 and \a s2 do not intersect. */ +predicate disjoint(var set of int: s1, var set of int: s2) = + fzn_disjoint(s1,s2); diff --git a/share/minizinc/std/disjunctive.mzn b/share/minizinc/std/disjunctive.mzn new file mode 100644 index 0000000..2af7024 --- /dev/null +++ b/share/minizinc/std/disjunctive.mzn @@ -0,0 +1,22 @@ +include "disjunctive_strict.mzn"; +include "fzn_disjunctive.mzn"; +include "fzn_disjunctive_reif.mzn"; + +/** @group globals.scheduling + Requires that a set of tasks given by start times \a s and durations \a d + do not overlap in time. Tasks with duration 0 can be scheduled at any time, + even in the middle of other tasks. + + Assumptions: + - forall \p i, \a d[\p i] >= 0 +*/ +predicate disjunctive(array[int] of var int: s, + array[int] of var int: d) = + assert(index_set(s) == index_set(d), + "disjunctive: the array arguments must have identical index sets", + if (lb_array(d) > 0) then + disjunctive_strict(s,d) + else + fzn_disjunctive(s,d) + endif + ); diff --git a/share/minizinc/std/disjunctive_opt.mzn b/share/minizinc/std/disjunctive_opt.mzn new file mode 100644 index 0000000..2d64822 --- /dev/null +++ b/share/minizinc/std/disjunctive_opt.mzn @@ -0,0 +1,24 @@ +include "disjunctive_strict_opt.mzn"; +include "fzn_disjunctive_opt.mzn"; +include "fzn_disjunctive_opt_reif.mzn"; + +/** @group globals.scheduling + Requires that a set of tasks given by start times \a s and durations \a d + do not overlap in time. Tasks with duration 0 can be scheduled at any time, + even in the middle of other tasks. Start times are optional variables, so + that absent tasks do not need to be scheduled. + + Assumptions: + - forall \p i, \a d[\p i] >= 0 +*/ +predicate disjunctive(array[int] of var opt int: s, + array[int] of var int: d) = + assert(index_set(s) == index_set(d), + "disjunctive: the array arguments must have identical index sets", + forall (i in index_set(d)) (d[i] >= 0) /\ + if (lb_array(d) > 0) then + disjunctive_strict(s,d) + else + fzn_disjunctive_opt(s, d) + endif + ); diff --git a/share/minizinc/std/disjunctive_strict.mzn b/share/minizinc/std/disjunctive_strict.mzn new file mode 100644 index 0000000..d78f5f7 --- /dev/null +++ b/share/minizinc/std/disjunctive_strict.mzn @@ -0,0 +1,18 @@ +include "fzn_disjunctive_strict.mzn"; +include "fzn_disjunctive_strict_reif.mzn"; + +/** @group globals.scheduling + Requires that a set of tasks given by start times \a s and durations \a d + do not overlap in time. Tasks with duration 0 CANNOT be scheduled at any time, + but only when no other task is running. + + Assumptions: + - forall \p i, \a d[\p i] >= 0 +*/ +predicate disjunctive_strict(array[int] of var int: s, + array[int] of var int: d) = + assert(index_set(s) == index_set(d), + "disjunctive: the array arguments must have identical index sets", + fzn_disjunctive_strict(s,d) + ); + diff --git a/share/minizinc/std/disjunctive_strict_opt.mzn b/share/minizinc/std/disjunctive_strict_opt.mzn new file mode 100644 index 0000000..6e7f775 --- /dev/null +++ b/share/minizinc/std/disjunctive_strict_opt.mzn @@ -0,0 +1,18 @@ +include "fzn_disjunctive_strict_opt.mzn"; +include "fzn_disjunctive_strict_opt_reif.mzn"; + +/** @group globals.scheduling + Requires that a set of tasks given by start times \a s and durations \a d + do not overlap in time. Tasks with duration 0 CANNOT be scheduled at any time, + but only when no other task is running. Start times are optional variables, so + that absent tasks do not need to be scheduled. + + Assumptions: + - forall \p i, \a d[\p i] >= 0 +*/ +predicate disjunctive_strict(array[int] of var opt int: s, + array[int] of var int: d) = + assert(index_set(s) == index_set(d), + "disjunctive: the array arguments must have identical index sets", + fzn_disjunctive_strict_opt(s,d) + ); diff --git a/share/minizinc/std/distribute.mzn b/share/minizinc/std/distribute.mzn new file mode 100644 index 0000000..83ade9c --- /dev/null +++ b/share/minizinc/std/distribute.mzn @@ -0,0 +1,14 @@ +include "fzn_distribute.mzn"; +include "fzn_distribute_reif.mzn"; + +/** @group globals.counting + Requires that \a card[\p i] is the number of occurences of \a value[\p i] in + \a base. The values in \a value need not be distinct. +*/ +predicate distribute(array[int] of var int: card, + array[int] of var int: value, + array[int] of var int: base) = + assert(index_set(card) == index_set(value), + "distribute: card and value arrays must have identical index sets", + fzn_distribute(card, value, base) + ); diff --git a/share/minizinc/std/distribute_fn.mzn b/share/minizinc/std/distribute_fn.mzn new file mode 100644 index 0000000..8495163 --- /dev/null +++ b/share/minizinc/std/distribute_fn.mzn @@ -0,0 +1,11 @@ +include "distribute.mzn"; + +/** @group globals.counting + Returns an array of the number of occurences of \a value[\p i] in + \a base. The values in \a value need not be distinct. +*/ +function array[int] of var int: distribute(array[int] of var int: value, + array[int] of var int: base) :: promise_total = + let { array[index_set(value)] of var 0..length(base): card; + constraint distribute(card, value, base); } + in card; diff --git a/share/minizinc/std/element.mzn b/share/minizinc/std/element.mzn new file mode 100644 index 0000000..16a615e --- /dev/null +++ b/share/minizinc/std/element.mzn @@ -0,0 +1,21 @@ +include "element_bool.mzn"; +include "element_float.mzn"; +include "element_int.mzn"; +include "element_set.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that 'y' is the ith element of the array 'x'. +%-----------------------------------------------------------------------------% + +predicate element(var int: i, array[int] of var bool: x, var bool: y) = + element_bool(i, x, y); + +predicate element(var int: i, array[int] of var float: x, var float: y) = + element_float(i, x, y); + +predicate element(var int: i, array[int] of var int: x, var int: y) = + element_int(i, x, y); + +predicate element(var int: i, array[int] of var set of int: x, + var set of int: y) = + element_set(i, x, y); diff --git a/share/minizinc/std/element_bool.mzn b/share/minizinc/std/element_bool.mzn new file mode 100644 index 0000000..3f25606 --- /dev/null +++ b/share/minizinc/std/element_bool.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' is the ith element of the array 'x'. +%-----------------------------------------------------------------------------% + +predicate element_bool(var int: i, array[int] of var bool: x, var bool: y) = + y = x[i]; diff --git a/share/minizinc/std/element_float.mzn b/share/minizinc/std/element_float.mzn new file mode 100644 index 0000000..ad33c37 --- /dev/null +++ b/share/minizinc/std/element_float.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' is the ith element of the array 'x'. +%-----------------------------------------------------------------------------% + +predicate element_float(var int: i, array[int] of var float: x, var float: y) = + y = x[i]; \ No newline at end of file diff --git a/share/minizinc/std/element_int.mzn b/share/minizinc/std/element_int.mzn new file mode 100644 index 0000000..766b4d8 --- /dev/null +++ b/share/minizinc/std/element_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' is the ith element of the array 'x'. +%-----------------------------------------------------------------------------% + +predicate element_int(var int: i, array[int] of var int: x, var int: y) = + y = x[i]; diff --git a/share/minizinc/std/element_set.mzn b/share/minizinc/std/element_set.mzn new file mode 100644 index 0000000..990d95e --- /dev/null +++ b/share/minizinc/std/element_set.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' is the ith element of the array 'x'. +%-----------------------------------------------------------------------------% + +predicate element_set(var int: i, array[int] of var set of int: x, + var set of int: y) = + y = x[i]; diff --git a/share/minizinc/std/exactly.mzn b/share/minizinc/std/exactly.mzn new file mode 100644 index 0000000..31f7afc --- /dev/null +++ b/share/minizinc/std/exactly.mzn @@ -0,0 +1,15 @@ +include "exactly_int.mzn"; +include "exactly_set.mzn"; + +/** @group globals.counting + Requires exactly \a n variables in \a x to take the value \a v. +*/ +predicate exactly(int: n, array[int] of var int: x, int: v) = + exactly_int(n, x, v); + +/** @group globals.counting + Requires exactly \a n variables in \a x to take the value \a v. +*/ +predicate exactly(int: n, array[int] of var set of int: x, set of int: v) = + exactly_set(n, x, v); + diff --git a/share/minizinc/std/exactly_int.mzn b/share/minizinc/std/exactly_int.mzn new file mode 100644 index 0000000..ca9023e --- /dev/null +++ b/share/minizinc/std/exactly_int.mzn @@ -0,0 +1,9 @@ +include "fzn_exactly_int.mzn"; +include "fzn_exactly_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires exactly 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate exactly_int(int: n, array[int] of var int: x, int: v) = + fzn_exactly_int(n, x, v); diff --git a/share/minizinc/std/exactly_set.mzn b/share/minizinc/std/exactly_set.mzn new file mode 100644 index 0000000..044ff4f --- /dev/null +++ b/share/minizinc/std/exactly_set.mzn @@ -0,0 +1,9 @@ +include "fzn_exactly_set.mzn"; +include "fzn_exactly_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires exactly 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate exactly_set(int: n, array[int] of var set of int: x, set of int: v) = + fzn_exactly_set(n, x, v); diff --git a/share/minizinc/std/flatzinc_builtins.mzn b/share/minizinc/std/flatzinc_builtins.mzn new file mode 100644 index 0000000..6a8dd25 --- /dev/null +++ b/share/minizinc/std/flatzinc_builtins.mzn @@ -0,0 +1,279 @@ +%-----------------------------------------------------------------------------% +% +% FlatZinc builtins +% +% This section contains declarations for the standard FlatZinc builtins. +% They can be redefined by providing a custom redefinitions.mzn in the +% solver globals library. +% + +/*** + @groupdef flatzinc FlatZinc builtins + + These are the standard constraints that need to be supported by FlatZinc + solvers (or redefined in the redefinitions.mzn file). +*/ + +/*** + @groupdef flatzinc.int Integer FlatZinc builtins +*/ + +/** @group flatzinc.int Constrains \a b to be the absolute value of \a a */ +predicate int_abs(var int: a, var int: b); +/** @group flatzinc.int Constrains \a a to be equal to \a b */ +predicate int_eq(var int: a, var int: b); +/** @group flatzinc.int Constrains (\a a=\a b) \( \leftrightarrow \) \a r */ +predicate int_eq_reif(var int: a, var int: b, var bool: r); +/** @group flatzinc.int Constrains \a a to be less than or equal to \a b */ +predicate int_le(var int: a, var int: b); +/** @group flatzinc.int Constrains (\a a ≤ \a b) \( \leftrightarrow \) \a r */ +predicate int_le_reif(var int: a, var int: b, var bool: r); +/** @group flatzinc.int Constrains \( \a c = \sum_i \a as[i]*\a bs[i] \) */ +predicate int_lin_eq(array[int] of int: as, array[int] of var int: bs, int: c); +/** @group flatzinc.int Constrains \( \a r \leftrightarrow (\a c = \sum_i \a as[i]*\a bs[i]) \) */ +predicate int_lin_eq_reif(array[int] of int: as, array[int] of var int: bs,int: c, var bool: r); +/** @group flatzinc.int Constrains \( \a c \neq \sum_i \a as[i]*\a bs[i] \) */ +predicate int_lin_ne(array[int] of int: as, array[int] of var int: bs, int: c); +/** @group flatzinc.int Constrains \( \a r \leftrightarrow (\a c \neq \sum_i \a as[i]*\a bs[i]) \) */ +predicate int_lin_ne_reif(array[int] of int: as, array[int] of var int: bs,int: c, var bool: r); + +/** @group flatzinc.int Constrains \( \sum \) \a as[\p i]*\a bs[\p i] ≤ \a c */ +predicate int_lin_le(array[int] of int: as, array[int] of var int: bs, int: c); +/** @group flatzinc.int Constrains \a r \( \leftrightarrow \) (\( \sum \) \a as[\p i]*\a bs[\p i] ≤ \a c) */ +predicate int_lin_le_reif(array[int] of int: as, array[int] of var int: bs,int: c, var bool: r); +/** @group flatzinc.int Constrains \a a ≠ \a b */ +predicate int_ne(var int: a, var int: b); +/** @group flatzinc.int \a r \( \leftrightarrow \) (\a a ≠ \a b) */ +predicate int_ne_reif(var int: a, var int: b, var bool: r); +/** @group flatzinc.int Constrains \a a + \a b = \a c */ +predicate int_plus(var int: a, var int: b, var int: c); +/** @group flatzinc.int Constrains \a a / \a b = \a c */ +predicate int_div(var int: a, var int: b, var int: c); +/** @group flatzinc.int Constrains \a a < \a b */ +predicate int_lt(var int: a, var int: b); +/** @group flatzinc.int Constrains \a r \( \leftrightarrow \) (\a a < \a b) */ +predicate int_lt_reif(var int: a, var int: b, var bool: r); +/** @group flatzinc.int Constrains max(\a a, \a b) = \a c */ +predicate int_max(var int: a, var int: b, var int: c); +/** @group flatzinc.int Constrains min(\a a, \a b) = \a c */ +predicate int_min(var int: a, var int: b, var int: c); +/** @group flatzinc.int Constrains \a a % \a b = \a c */ +predicate int_mod(var int: a, var int: b, var int: c); +/** @group flatzinc.int Constrains \a a * \a b = \a c */ +predicate int_times(var int: a, var int: b, var int: c); +/** @group flatzinc.int Constrains \a z = \(\a x ^ {\a y}\) */ +predicate int_pow(var int: x, var int: y, var int: z); + +/*** + @groupdef flatzinc.bool Bool FlatZinc builtins +*/ + +/** @group flatzinc.bool Constrains \( \a b \in \{0,1\} \) and \( \a a \leftrightarrow \a b=1 \) */ +predicate bool2int(var bool: a, var int: b); +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \a a \land \a b \) */ +predicate bool_and(var bool: a, var bool: b, var bool: r); +/** @group flatzinc.bool Constrains \( \bigvee_i \a as[i] \lor \bigvee_j \lnot \a bs[j] \) */ +predicate bool_clause(array[int] of var bool: as, array[int] of var bool: bs); + +/** @group flatzinc.bool Constrains \a a = \a b */ +predicate bool_eq(var bool: a, var bool: b); +/** @group flatzinc.bool Constrains \a r \( \leftrightarrow \) (\a a = \a b) */ +predicate bool_eq_reif(var bool: a, var bool: b, var bool: r); +/** @group flatzinc.bool Constrains \a a ≤ \a b */ +predicate bool_le(var bool: a, var bool: b); +/** @group flatzinc.bool Constrains \a r \( \leftrightarrow \) (\a a ≤ \a b) */ +predicate bool_le_reif(var bool: a, var bool: b, var bool: r); +/** @group flatzinc.bool Constrains \( \a c = \sum_i \a as[i]*\a bs[i] \) */ +predicate bool_lin_eq(array[int] of int: as, array[int] of var bool: bs, var int: c); +/** @group flatzinc.bool Constrains \( \a c \leq \sum_i \a as[i]*\a bs[i] \) */ +predicate bool_lin_le(array[int] of int: as, array[int] of var bool: bs, int: c); +/** @group flatzinc.bool Constrains \a a < \a b */ +predicate bool_lt(var bool: a, var bool: b); +/** @group flatzinc.bool Constrains \a r \( \leftrightarrow \) (\a a < \a b) */ +predicate bool_lt_reif(var bool: a, var bool: b, var bool: r); +/** @group flatzinc.bool Constrains \a a ≠ \a b */ +predicate bool_not(var bool: a, var bool: b); +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \a a \lor \a b \) */ +predicate bool_or(var bool: a, var bool: b, var bool: r); +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \a a \oplus \a b \) */ +predicate bool_xor(var bool: a, var bool: b, var bool: r); +/** @group flatzinc.bool Constrains \a a \( \oplus \) \a b */ +predicate bool_xor(var bool: a, var bool: b); + +/*** + @groupdef flatzinc.set Set FlatZinc builtins +*/ + +/** @group flatzinc.set Constrains \a x \( \in \) \a S */ +predicate set_in(var int: x, set of int: S); +/** @group flatzinc.set Constrains \a x \( \in \) \a S */ +predicate set_in(var int: x, var set of int: S); +/** @group flatzinc.set Constrains \a x = |\a S| */ +predicate set_card(var set of int: S, var int: x); +/** @group flatzinc.set Constrains \( \a r \leftrightarrow (\a x \in \a S) \) */ +predicate set_in_reif(var int: x, set of int: S, var bool: r); +/** @group flatzinc.set Constrains \( \a r \leftrightarrow (\a x \in \a S) \) */ +predicate set_in_reif(var int: x, var set of int: S, var bool: r); +/** @group flatzinc.set Constrains \a x \( \subseteq \) \a y */ +predicate set_subset(var set of int: x, var set of int: y); +/** @group flatzinc.set Constrains \a x \( \supseteq \) \a y */ +predicate set_superset(var set of int: x, var set of int: y); +/** @group flatzinc.set Constrains \( \a r \leftrightarrow (\a x \subseteq \a y) \) */ +predicate set_subset_reif(var set of int: x, var set of int: y, var bool: r); +/** @group flatzinc.set Constrains \a x ≤ \a y (lexicographic order) */ +predicate set_le(var set of int: x, var set of int: y); +/** @group flatzinc.set Constrains \a x < \a y (lexicographic order) */ +predicate set_lt(var set of int: x, var set of int: y); +/** @group flatzinc.set Constrains \a x = \a y */ +predicate set_eq(var set of int: x, var set of int: y); +/** @group flatzinc.set Constrains \a r \( \leftrightarrow \) (\a x = \a y) */ +predicate set_eq_reif(var set of int: x, var set of int: y, var bool: r); +/** @group flatzinc.set Constrains \a x ≠ \a y */ +predicate set_ne(var set of int: x, var set of int: y); +/** @group flatzinc.set Constrains \a r \( \leftrightarrow \) (\a x ≠ \a y) */ +predicate set_ne_reif(var set of int: x, var set of int: y, var bool: r); +/** @group flatzinc.set Constrains \a r = \a x \( \cap \) \a y */ +predicate set_intersect(var set of int: x, var set of int: y, var set of int: r); +/** @group flatzinc.set Constrains \a r = \a x \( \cup \) \a y */ +predicate set_union(var set of int: x, var set of int: y, var set of int: r); +/** @group flatzinc.set Constrains \a r = \a x \( \setminus \) \a y */ +predicate set_diff(var set of int: x, var set of int: y, var set of int: r); +/** @group flatzinc.set Constrains \a r to be the symmetric difference of \a x and \a y */ +predicate set_symdiff(var set of int: x, var set of int: y, var set of int: r); + +/*** + @groupdef flatzinc.float Float FlatZinc builtins +*/ + +/** @group flatzinc.float Constrains \a b to be the absolute value of \a a */ +predicate float_abs(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = acos(\a a) */ +predicate float_acos(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = acosh(\a a) */ +predicate float_acosh(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = asin(\a a) */ +predicate float_asin(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = asinh(\a a) */ +predicate float_asinh(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = atan(\a a) */ +predicate float_atan(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = atanh(\a a) */ +predicate float_atanh(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = cos(\a a) */ +predicate float_cos(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = cosh(\a a) */ +predicate float_cosh(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = exp(\a a) */ +predicate float_exp(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = ln(\a a) */ +predicate float_ln(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = log10(\a a) */ +predicate float_log10(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = log2(\a a) */ +predicate float_log2(var float: a, var float: b); +/** @group flatzinc.float Constrains \(\a b = \sqrt{\a a}\) */ +predicate float_sqrt(var float: a, var float: b); +/** @group flatzinc.float Constrains \a z = \(\a x ^ {\a y}\) */ +predicate float_pow(var float: x, var float: y, var float: z); +/** @group flatzinc.float Constrains \a b = sin(\a a) */ +predicate float_sin(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = sinh(\a a) */ +predicate float_sinh(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = tan(\a a) */ +predicate float_tan(var float: a, var float: b); +/** @group flatzinc.float Constrains \a b = tanh(\a a) */ +predicate float_tanh(var float: a, var float: b); + +/** @group flatzinc.float Constrains \a a = \a b */ +predicate float_eq(var float: a, var float: b); +/** @group flatzinc.float Constrains \a r \( \leftrightarrow \) (\a a = \a b) */ +predicate float_eq_reif(var float: a, var float: b, var bool: r); +/** @group flatzinc.float Constrains \a a ≤ \a b */ +predicate float_le(var float: a, var float: b); +/** @group flatzinc.float Constrains \a r \( \leftrightarrow \) (\a a ≤ \a b) */ +predicate float_le_reif(var float: a, var float: b, var bool: r); +/** @group flatzinc.float Constrains \a a < \a b */ +predicate float_lt(var float: a, var float: b); +/** @group flatzinc.float Constrains \a r \( \leftrightarrow \) (\a a < \a b) */ +predicate float_lt_reif(var float: a, var float: b, var bool: r); +/** @group flatzinc.float Constrains \a a ≠ \a b */ +predicate float_ne(var float: a, var float: b); +/** @group flatzinc.float Constrains \a r \( \leftrightarrow \) (\a a ≠ \a b) */ +predicate float_ne_reif(var float: a, var float: b, var bool: r); +/** @group flatzinc.float Constrains \( \a a \in\ [ \a b, \a c ] \) */ +predicate float_in(var float: a, float: b, float: c); +/** @group flatzinc.float Constrains \a r \( \leftrightarrow \) \( \a a \in\ [ \a b, \a c ] \) */ +predicate float_in_reif(var float: a, float: b, float: c, var bool: r); +/** @group flatzinc.float Constrains the domain of \a x using the values in \a as, using each + pair of values \a as[2*\p i-1]..\a as[2*\p i] for \p i in 1..\p n/2 as a possible range + */ +predicate float_dom(var float: x, array[int] of float: as); + +/** @group flatzinc.float Constrains \( \a c = \sum_i \a as[i]*\a bs[i] \) */ +predicate float_lin_eq(array[int] of float: as, array[int] of var float: bs, float: c); +/** @group flatzinc.float Constrains \( \a r \leftrightarrow (\a c = \sum_i \a as[i]*\a bs[i]) \) */ +predicate float_lin_eq_reif(array[int] of float: as, array[int] of var float: bs, float: c, var bool: r); +/** @group flatzinc.float Constrains \( \a c \leq \sum_i \a as[i]*\a bs[i] \) */ +predicate float_lin_le(array[int] of float: as, array[int] of var float: bs, float: c); +/** @group flatzinc.float Constrains \( \a r \leftrightarrow (\a c \leq \sum_i \a as[i]*\a bs[i]) \) */ +predicate float_lin_le_reif(array[int] of float: as, array[int] of var float: bs, float: c, var bool: r); +/** @group flatzinc.float Constrains \( \a c < \sum_i \a as[i]*\a bs[i] \) */ +predicate float_lin_lt(array[int] of float: as, array[int] of var float: bs, float: c); +/** @group flatzinc.float Constrains \( \a r \leftrightarrow (\a c < \sum_i \a as[i]*\a bs[i]) \) */ +predicate float_lin_lt_reif(array[int] of float: as, array[int] of var float: bs, float: c, var bool: r); +/** @group flatzinc.float Constrains \( \a c \neq \sum_i \a as[i]*\a bs[i] \) */ +predicate float_lin_ne(array[int] of float: as, array[int] of var float: bs, float: c); +/** @group flatzinc.float Constrains \( \a r \leftrightarrow (\a c \neq \sum_i \a as[i]*\a bs[i]) \) */ +predicate float_lin_ne_reif(array[int] of float: as, array[int] of var float: bs, float: c, var bool: r); + +/** @group flatzinc.float Constrains max(\a a, \a b) = \a c */ +predicate float_max(var float: a, var float: b, var float: c); +/** @group flatzinc.float Constrains min(\a a, \a b) = \a c */ +predicate float_min(var float: a, var float: b, var float: c); +/** @group flatzinc.float Constrains \a a + \a b = \a c */ +predicate float_plus(var float: a, var float: b, var float: c); +/** @group flatzinc.float Constrains \a a / \a b = \a c */ +predicate float_div(var float: a, var float: b, var float: c); +/** @group flatzinc.float Constrains \a a * \a b = \a c */ +predicate float_times(var float: a, var float: b, var float: c); + +/** @group flatzinc.float Constrains \a y=\a x */ +predicate int2float(var int: x, var float: y); + + +% Array constraints + +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \bigwedge_i \a as[i]\) */ +predicate array_bool_and(array[int] of var bool: as, var bool: r); +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \bigvee_i \a as[i]\) */ +predicate array_bool_or(array[int] of var bool: as, var bool: r); +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \oplus_i\ \a as[i]\) */ +predicate array_bool_xor(array[int] of var bool: as); +/** @group flatzinc.bool Constrains \a as[\a b] = \a c */ +predicate array_bool_element(var int: b, array[int] of bool: as, var bool: c); +/** @group flatzinc.int Constrains \a as[\a b] = \a c */ +predicate array_int_element(var int: b, array[int] of int: as, var int: c); +/** @group flatzinc.float Constrains \a as[\a b] = \a c */ +predicate array_float_element(var int: b, array[int] of float: as, var float: c); +/** @group flatzinc.set Constrains \a as[\a b] = \a c */ +predicate array_set_element(var int: b, array[int] of set of int: as, var set of int: c); +/** @group flatzinc.bool Constrains \a as[\a b] = \a c */ +predicate array_var_bool_element(var int: b, array[int] of var bool: as, var bool: c); +/** @group flatzinc.int Constrains \a as[\a b] = \a c */ +predicate array_var_int_element(var int: b, array[int] of var int: as, var int: c); +/** @group flatzinc.float Constrains \a as[\a b] = \a c */ +predicate array_var_float_element(var int: b, array[int] of var float: as, var float: c); +/** @group flatzinc.set Constrains \a as[\a b] = \a c */ +predicate array_var_set_element(var int: b, array[int] of var set of int: as, var set of int: c); +/** @group flatzinc.int Constrains \a m to be the maximum value of the (non-empty) + array \a x */ +predicate array_int_maximum(var int: m, array[int] of var int: x); +/** @group flatzinc.float Constrains \a m to be the maximum value of the (non-empty) + array \a x */ +predicate array_float_maximum(var int: m, array[int] of var int: x); +/** @group flatzinc.int Constrains \a m to be the minimum value of the (non-empty) + array \a x */ +predicate array_int_minimum(var int: m, array[int] of var int: x); +/** @group flatzinc.float Constrains \a m to be the minimum value of the (non-empty) + array \a x */ +predicate array_float_minimum(var int: m, array[int] of var int: x); diff --git a/share/minizinc/std/fzn_all_different_int.mzn b/share/minizinc/std/fzn_all_different_int.mzn new file mode 100644 index 0000000..a313c70 --- /dev/null +++ b/share/minizinc/std/fzn_all_different_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate fzn_all_different_int(array[int] of var int: x) = + forall(i,j in index_set(x) where i < j) ( x[i] != x[j] ); diff --git a/share/minizinc/std/fzn_all_different_int_reif.mzn b/share/minizinc/std/fzn_all_different_int_reif.mzn new file mode 100644 index 0000000..e2472bc --- /dev/null +++ b/share/minizinc/std/fzn_all_different_int_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate fzn_all_different_int_reif(array[int] of var int: x, var bool: b) = + b <-> forall(i,j in index_set(x) where i < j) ( x[i] != x[j] ); diff --git a/share/minizinc/std/fzn_all_different_set.mzn b/share/minizinc/std/fzn_all_different_set.mzn new file mode 100644 index 0000000..3beacf2 --- /dev/null +++ b/share/minizinc/std/fzn_all_different_set.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate fzn_all_different_set(array[int] of var set of int: x) = + forall(i,j in index_set(x) where i < j) ( x[i] != x[j] ); diff --git a/share/minizinc/std/fzn_all_different_set_reif.mzn b/share/minizinc/std/fzn_all_different_set_reif.mzn new file mode 100644 index 0000000..f17f357 --- /dev/null +++ b/share/minizinc/std/fzn_all_different_set_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all different. +%-----------------------------------------------------------------------------% + +predicate fzn_all_different_set_reif(array[int] of var set of int: x, var bool: b) = + b <-> forall(i,j in index_set(x) where i < j) ( x[i] != x[j] ); diff --git a/share/minizinc/std/fzn_all_disjoint.mzn b/share/minizinc/std/fzn_all_disjoint.mzn new file mode 100644 index 0000000..05f46c0 --- /dev/null +++ b/share/minizinc/std/fzn_all_disjoint.mzn @@ -0,0 +1,4 @@ +include "fzn_disjoint.mzn"; + +predicate fzn_all_disjoint(array[int] of var set of int: S) = + forall(i,j in index_set(S) where i < j) ( fzn_disjoint(S[i], S[j]) ); diff --git a/share/minizinc/std/fzn_all_disjoint_reif.mzn b/share/minizinc/std/fzn_all_disjoint_reif.mzn new file mode 100644 index 0000000..907d6cd --- /dev/null +++ b/share/minizinc/std/fzn_all_disjoint_reif.mzn @@ -0,0 +1,4 @@ +include "fzn_disjoint.mzn"; + +predicate fzn_all_disjoint_reif(array[int] of var set of int: S, var bool: b) = + b <-> forall(i,j in index_set(S) where i < j) ( fzn_disjoint(S[i], S[j]) ); diff --git a/share/minizinc/std/fzn_all_equal_int.mzn b/share/minizinc/std/fzn_all_equal_int.mzn new file mode 100644 index 0000000..b9637d5 --- /dev/null +++ b/share/minizinc/std/fzn_all_equal_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all equal. +%-----------------------------------------------------------------------------% + +predicate fzn_all_equal_int(array[int] of var int: x) = + forall(i, j in index_set(x) where i < j) ( x[i] = x[j] ); diff --git a/share/minizinc/std/fzn_all_equal_int_reif.mzn b/share/minizinc/std/fzn_all_equal_int_reif.mzn new file mode 100644 index 0000000..abb906a --- /dev/null +++ b/share/minizinc/std/fzn_all_equal_int_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all equal. +%-----------------------------------------------------------------------------% + +predicate fzn_all_equal_int_reif(array[int] of var int: x, var bool: b) = + b <-> forall(i, j in index_set(x) where i < j) ( x[i] = x[j] ); diff --git a/share/minizinc/std/fzn_all_equal_set.mzn b/share/minizinc/std/fzn_all_equal_set.mzn new file mode 100644 index 0000000..76dacda --- /dev/null +++ b/share/minizinc/std/fzn_all_equal_set.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all equal. +%-----------------------------------------------------------------------------% + +predicate fzn_all_equal_set(array[int] of var set of int: x) = + forall(i, j in index_set(x) where i < j) ( x[i] = x[j] ); diff --git a/share/minizinc/std/fzn_all_equal_set_reif.mzn b/share/minizinc/std/fzn_all_equal_set_reif.mzn new file mode 100644 index 0000000..4a823d0 --- /dev/null +++ b/share/minizinc/std/fzn_all_equal_set_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Constrains the array of objects 'x' to be all equal. +%-----------------------------------------------------------------------------% + +predicate fzn_all_equal_set_reif(array[int] of var set of int: x, var bool: b) = + b <-> forall(i, j in index_set(x) where i < j) ( x[i] = x[j] ); diff --git a/share/minizinc/std/fzn_alldifferent_except_0.mzn b/share/minizinc/std/fzn_alldifferent_except_0.mzn new file mode 100644 index 0000000..c466ddc --- /dev/null +++ b/share/minizinc/std/fzn_alldifferent_except_0.mzn @@ -0,0 +1,4 @@ +predicate fzn_alldifferent_except_0(array[int] of var int: vs) = + forall(i, j in index_set(vs) where i < j) ( + (vs[i] != 0 /\ vs[j] != 0) -> vs[i] != vs[j] + ); diff --git a/share/minizinc/std/fzn_alldifferent_except_0_reif.mzn b/share/minizinc/std/fzn_alldifferent_except_0_reif.mzn new file mode 100644 index 0000000..75ae40f --- /dev/null +++ b/share/minizinc/std/fzn_alldifferent_except_0_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_alldifferent_except_0_reif(array[int] of var int: vs, var bool: b) = + b <-> forall(i, j in index_set(vs) where i < j) ( + (vs[i] != 0 /\ vs[j] != 0) -> vs[i] != vs[j] + ); diff --git a/share/minizinc/std/fzn_alternative.mzn b/share/minizinc/std/fzn_alternative.mzn new file mode 100644 index 0000000..156337f --- /dev/null +++ b/share/minizinc/std/fzn_alternative.mzn @@ -0,0 +1,7 @@ +include "span.mzn"; + +predicate fzn_alternative(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d) = + sum(i in index_set(s))(bool2int(occurs(s[i]))) <= 1 /\ + span(s0,d0,s,d); diff --git a/share/minizinc/std/fzn_alternative_reif.mzn b/share/minizinc/std/fzn_alternative_reif.mzn new file mode 100644 index 0000000..4323b39 --- /dev/null +++ b/share/minizinc/std/fzn_alternative_reif.mzn @@ -0,0 +1,7 @@ +include "span.mzn"; + +predicate fzn_alternative_reif(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d, var bool: b) = + b <-> ( sum(i in index_set(s))(bool2int(occurs(s[i]))) <= 1 /\ + span(s0,d0,s,d) ); diff --git a/share/minizinc/std/fzn_among.mzn b/share/minizinc/std/fzn_among.mzn new file mode 100644 index 0000000..ce988e7 --- /dev/null +++ b/share/minizinc/std/fzn_among.mzn @@ -0,0 +1,3 @@ +predicate fzn_among(var int: n, array[int] of var int: x, set of int: v) = + n == sum(i in index_set(x)) ( bool2int(x[i] in v) ); + diff --git a/share/minizinc/std/fzn_among_reif.mzn b/share/minizinc/std/fzn_among_reif.mzn new file mode 100644 index 0000000..4667a28 --- /dev/null +++ b/share/minizinc/std/fzn_among_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_among_reif(var int: n, array[int] of var int: x, set of int: v, var bool: b) = + b <-> ( n == sum(i in index_set(x)) ( bool2int(x[i] in v) ) ); diff --git a/share/minizinc/std/fzn_arg_max_bool.mzn b/share/minizinc/std/fzn_arg_max_bool.mzn new file mode 100644 index 0000000..0750438 --- /dev/null +++ b/share/minizinc/std/fzn_arg_max_bool.mzn @@ -0,0 +1,20 @@ +predicate fzn_maximum_arg_bool(array[int] of var bool: x, var int: i) = + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + } in + if exists(j in l..u)(is_fixed(x[j]) /\ fix(x[j])) + then % special case: max is known to be 1 + let { array[l..u] of var bool: d; } in + x[i] = true /\ % ith case must be equal to ub + forall(j in l..u)(x[j] -> i <= j) /\ % lower bound + d[l] = x[l] /\ + forall(j in l+1..u)(d[j] <-> (d[j-1] \/ x[j])) /\ + forall(j in l..u)(not d[j] -> i >= j+1) % upper bound + else + % general case: min could be 0 or 1 + let { array[l..u] of var bool: y; + array[l..u] of var l..u: mi; } in + y[l] = x[l] /\ mi[l] = l /\ i = mi[u] /\ + forall (j in l+1 .. u) ( y[j] == (x[j] \/ y[j-1]) /\ + mi[j] = if (y[j-1] \/ not x[j]) then mi[j-1] else j endif ) + endif; diff --git a/share/minizinc/std/fzn_arg_max_float.mzn b/share/minizinc/std/fzn_arg_max_float.mzn new file mode 100644 index 0000000..6c43d40 --- /dev/null +++ b/share/minizinc/std/fzn_arg_max_float.mzn @@ -0,0 +1,21 @@ +predicate fzn_maximum_arg_float(array[int] of var float: x, var int: i) = + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + float: uy = ub_array(x); + } in + if exists(j in l..u)(lb(x[j]) = uy) + then let { array[l..u] of var bool: d; } in + % max is known to be uy + x[i] = uy /\ % ith case must be equal to ub + forall(j in l..u)(x[j] = uy -> i <= j) /\ % lower bound + d[l] = (x[l] = uy) /\ + forall(j in l+1..u)(d[j] <-> (d[j-1] \/ (x[j] = uy))) /\ + forall(j in l..u)(not d[j] -> i >= j+1) % upper bound + else + let { float: ly = lb_array(x); + array[l..u] of var ly..uy: y; + array[l..u] of var l..u: mi; } in + y[l] = x[l] /\ mi[l] = l /\ i = mi[u] /\ + forall (j in l+1 .. u) ( y[j] == max(x[j],y[j-1]) /\ + mi[j] = if y[j-1] >= x[j] then mi[j-1] else j endif ) + endif; diff --git a/share/minizinc/std/fzn_arg_max_int.mzn b/share/minizinc/std/fzn_arg_max_int.mzn new file mode 100644 index 0000000..d3fb305 --- /dev/null +++ b/share/minizinc/std/fzn_arg_max_int.mzn @@ -0,0 +1,21 @@ +predicate fzn_maximum_arg_int(array[int] of var int: x, var int: i) = + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + int: uy = ub_array(x); + } in + if exists(j in l..u)(lb(x[j]) = uy) + then let { array[l..u] of var bool: d; } in + % max is known to be uy + x[i] = uy /\ % ith case must be equal to ub + forall(j in l..u)(x[j] = uy -> i <= j) /\ % lower bound + d[l] = (x[l] = uy) /\ + forall(j in l+1..u)(d[j] <-> (d[j-1] \/ (x[j] = uy))) /\ + forall(j in l..u)(not d[j] -> i >= j+1) % upper bound + else + let { int: ly = lb_array(x); + array[l..u] of var ly..uy: y; + array[l..u] of var l..u: mi; } in + y[l] = x[l] /\ mi[l] = l /\ i = mi[u] /\ + forall (j in l+1 .. u) ( y[j] == max(x[j],y[j-1]) /\ + mi[j] = if y[j-1] >= x[j] then mi[j-1] else j endif ) + endif; diff --git a/share/minizinc/std/fzn_arg_min_bool.mzn b/share/minizinc/std/fzn_arg_min_bool.mzn new file mode 100644 index 0000000..a736836 --- /dev/null +++ b/share/minizinc/std/fzn_arg_min_bool.mzn @@ -0,0 +1,20 @@ +predicate fzn_minimum_arg_bool(array[int] of var bool: x, var int: i) = + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + } in + if exists(j in l..u)(is_fixed(x[j]) /\ not fix(x[j])) + then % special case: min is known to be 0 + let { array[l..u] of var bool: d; } in + x[i] = false /\ % ith case must be equal to lb + forall(j in l..u)(not x[j] -> i <= j) /\ % lower bound + d[l] = x[l] /\ + forall(j in l+1..u)(d[j] <-> (d[j-1] /\ x[j])) /\ + forall(j in l..u)(d[j] -> i >= j+1) % upper bound + else + % general case: min could be 0 or 1 + let { array[l..u] of var bool: y; + array[l..u] of var l..u: mi; } in + y[l] = x[l] /\ mi[l] = l /\ i = mi[u] /\ + forall (j in l+1 .. u) ( y[j] == (x[j] /\ y[j-1]) /\ + mi[j] = if (not y[j-1] \/ x[j]) then mi[j-1] else j endif ) + endif; \ No newline at end of file diff --git a/share/minizinc/std/fzn_arg_min_float.mzn b/share/minizinc/std/fzn_arg_min_float.mzn new file mode 100644 index 0000000..e23f35d --- /dev/null +++ b/share/minizinc/std/fzn_arg_min_float.mzn @@ -0,0 +1,21 @@ +predicate fzn_minimum_arg_float(array[int] of var float: x, var int: i) = + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + float: ly = lb_array(x); + } in + if exists(j in l..u)(ub(x[j]) = ly) + then let { array[l..u] of var bool: d; } in + % min is known to be ly + x[i] = ly /\ % ith case must be equal to ub + forall(j in l..u)(x[j] = ly -> i <= j) /\ % lower bound + d[l] = (x[l] != ly) /\ + forall(j in l+1..u)(d[j] <-> (d[j-1] /\ (x[j] != ly))) /\ + forall(j in l..u)(d[j] -> i >= j+1) % upper bound + else + let { float: uy = ub_array(x); + array[l..u] of var ly..uy: y; + array[l..u] of var l..u: mi; } in + y[l] = x[l] /\ mi[l] = l /\ i = mi[u] /\ + forall (j in l+1 .. u) ( y[j] == min(x[j],y[j-1]) /\ + mi[j] = if y[j-1] <= x[j] then mi[j-1] else j endif ) + endif; diff --git a/share/minizinc/std/fzn_arg_min_int.mzn b/share/minizinc/std/fzn_arg_min_int.mzn new file mode 100644 index 0000000..ee3bfe5 --- /dev/null +++ b/share/minizinc/std/fzn_arg_min_int.mzn @@ -0,0 +1,21 @@ +predicate fzn_minimum_arg_int(array[int] of var int: x, var int: i) = + let { int: l = min(index_set(x)); + int: u = max(index_set(x)); + int: ly = lb_array(x); + } in + if exists(j in l..u)(ub(x[j]) = ly) + then let { array[l..u] of var bool: d; } in + % min is known to be ly + x[i] = ly /\ % ith case must be equal to ub + forall(j in l..u)(x[j] = ly -> i <= j) /\ % lower bound + d[l] = (x[l] != ly) /\ + forall(j in l+1..u)(d[j] <-> (d[j-1] /\ (x[j] != ly))) /\ + forall(j in l..u)(d[j] -> i >= j+1) % upper bound + else + let { int: uy = ub_array(x); + array[l..u] of var ly..uy: y; + array[l..u] of var l..u: mi; } in + y[l] = x[l] /\ mi[l] = l /\ i = mi[u] /\ + forall (j in l+1 .. u) ( y[j] == min(x[j],y[j-1]) /\ + mi[j] = if y[j-1] <= x[j] then mi[j-1] else j endif ) + endif; diff --git a/share/minizinc/std/fzn_arg_sort_float.mzn b/share/minizinc/std/fzn_arg_sort_float.mzn new file mode 100644 index 0000000..8fd70eb --- /dev/null +++ b/share/minizinc/std/fzn_arg_sort_float.mzn @@ -0,0 +1,7 @@ +include "all_different.mzn"; + +predicate fzn_arg_sort_float(array[int] of var float:x, + array[int] of var int:p) = + all_different(p) /\ + forall(j in 1..length(x)-1) + (x[p[j]] <= x[p[j+1]] /\ (x[p[j]] == x[p[j+1]] -> p[j] < p[j+1])); diff --git a/share/minizinc/std/fzn_arg_sort_float_reif.mzn b/share/minizinc/std/fzn_arg_sort_float_reif.mzn new file mode 100644 index 0000000..3dd7c5a --- /dev/null +++ b/share/minizinc/std/fzn_arg_sort_float_reif.mzn @@ -0,0 +1,9 @@ +include "all_different.mzn"; + +predicate fzn_arg_sort_float_reif(array[int] of var float:x, + array[int] of var int:p, + var bool: b) = + b <-> ( all_different(p) /\ + forall(j in 1..length(x)-1) + (x[p[j]] <= x[p[j+1]] /\ (x[p[j]] == x[p[j+1]] -> p[j] < p[j+1])) + ); diff --git a/share/minizinc/std/fzn_arg_sort_int.mzn b/share/minizinc/std/fzn_arg_sort_int.mzn new file mode 100644 index 0000000..1532d87 --- /dev/null +++ b/share/minizinc/std/fzn_arg_sort_int.mzn @@ -0,0 +1,7 @@ +include "all_different.mzn"; + +predicate fzn_arg_sort_int(array[int] of var int:x, + array[int] of var int:p) = + all_different(p) /\ + forall(j in 1..length(x)-1) + (x[p[j]] <= x[p[j+1]] /\ (x[p[j]] == x[p[j+1]] -> p[j] < p[j+1])); diff --git a/share/minizinc/std/fzn_arg_sort_int_reif.mzn b/share/minizinc/std/fzn_arg_sort_int_reif.mzn new file mode 100644 index 0000000..49b9cf3 --- /dev/null +++ b/share/minizinc/std/fzn_arg_sort_int_reif.mzn @@ -0,0 +1,9 @@ +include "all_different.mzn"; + +predicate fzn_arg_sort_int_reif(array[int] of var int:x, + array[int] of var int:p, + var bool:b) = + b <-> ( all_different(p) /\ + forall(j in 1..length(x)-1) + (x[p[j]] <= x[p[j+1]] /\ (x[p[j]] == x[p[j+1]] -> p[j] < p[j+1])) + ); diff --git a/share/minizinc/std/fzn_at_least_int.mzn b/share/minizinc/std/fzn_at_least_int.mzn new file mode 100644 index 0000000..7f86f51 --- /dev/null +++ b/share/minizinc/std/fzn_at_least_int.mzn @@ -0,0 +1,9 @@ +include "count_fn.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at least 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_least_int(int: n, array[int] of var int: x, int: v) = + count(x, n) >= n; + diff --git a/share/minizinc/std/fzn_at_least_int_reif.mzn b/share/minizinc/std/fzn_at_least_int_reif.mzn new file mode 100644 index 0000000..c90c3db --- /dev/null +++ b/share/minizinc/std/fzn_at_least_int_reif.mzn @@ -0,0 +1,8 @@ +include "count_fn.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at least 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_least_int_reif(int: n, array[int] of var int: x, int: v, var bool: b) = + b <-> count(x, n) >= n; diff --git a/share/minizinc/std/fzn_at_least_set.mzn b/share/minizinc/std/fzn_at_least_set.mzn new file mode 100644 index 0000000..295cbdd --- /dev/null +++ b/share/minizinc/std/fzn_at_least_set.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires at least 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_least_set(int: n, array[int] of var set of int: x, set of int: v) = + sum(i in index_set(x)) ( bool2int(x[i] == v) ) >= n; + diff --git a/share/minizinc/std/fzn_at_least_set_reif.mzn b/share/minizinc/std/fzn_at_least_set_reif.mzn new file mode 100644 index 0000000..9c60ed3 --- /dev/null +++ b/share/minizinc/std/fzn_at_least_set_reif.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires at least 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_least_set_reif(int: n, array[int] of var set of int: x, set of int: v, var bool: b) = + b <-> sum(i in index_set(x)) ( bool2int(x[i] == v) ) >= n; + diff --git a/share/minizinc/std/fzn_at_most1.mzn b/share/minizinc/std/fzn_at_most1.mzn new file mode 100644 index 0000000..bc47c45 --- /dev/null +++ b/share/minizinc/std/fzn_at_most1.mzn @@ -0,0 +1,4 @@ +predicate fzn_at_most1(array[int] of var set of int: s) = + forall(i,j in index_set(s) where i < j) ( + card(s[i] intersect s[j]) <= 1 ); + diff --git a/share/minizinc/std/fzn_at_most1_reif.mzn b/share/minizinc/std/fzn_at_most1_reif.mzn new file mode 100644 index 0000000..61081d4 --- /dev/null +++ b/share/minizinc/std/fzn_at_most1_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_at_most1_reif(array[int] of var set of int: s, var bool: b) = + b <-> forall(i,j in index_set(s) where i < j) ( + card(s[i] intersect s[j]) <= 1 ); + diff --git a/share/minizinc/std/fzn_at_most_int.mzn b/share/minizinc/std/fzn_at_most_int.mzn new file mode 100644 index 0000000..098188b --- /dev/null +++ b/share/minizinc/std/fzn_at_most_int.mzn @@ -0,0 +1,9 @@ +include "count_fn.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at most 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_most_int(int: n, array[int] of var int: x, int: v) = + count(x,v) <= n; + diff --git a/share/minizinc/std/fzn_at_most_int_reif.mzn b/share/minizinc/std/fzn_at_most_int_reif.mzn new file mode 100644 index 0000000..44a5d60 --- /dev/null +++ b/share/minizinc/std/fzn_at_most_int_reif.mzn @@ -0,0 +1,9 @@ +include "count_fn.mzn"; + +%-----------------------------------------------------------------------------% +% Requires at most 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_most_int_reif(int: n, array[int] of var int: x, int: v, var bool: b) = + b <-> count(x,v) <= n; + diff --git a/share/minizinc/std/fzn_at_most_set.mzn b/share/minizinc/std/fzn_at_most_set.mzn new file mode 100644 index 0000000..d4ec897 --- /dev/null +++ b/share/minizinc/std/fzn_at_most_set.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires at most 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_most_set(int: n, array[int] of var set of int: x, set of int: v) = + sum(i in index_set(x)) ( bool2int(x[i] == v) ) <= n; + diff --git a/share/minizinc/std/fzn_at_most_set_reif.mzn b/share/minizinc/std/fzn_at_most_set_reif.mzn new file mode 100644 index 0000000..276ee1b --- /dev/null +++ b/share/minizinc/std/fzn_at_most_set_reif.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires at most 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_at_most_set_reif(int: n, array[int] of var set of int: x, set of int: v, var bool: b) = + b <-> sum(i in index_set(x)) ( bool2int(x[i] == v) ) <= n; + diff --git a/share/minizinc/std/fzn_bin_packing.mzn b/share/minizinc/std/fzn_bin_packing.mzn new file mode 100644 index 0000000..9c74890 --- /dev/null +++ b/share/minizinc/std/fzn_bin_packing.mzn @@ -0,0 +1,10 @@ +predicate fzn_bin_packing(int: c, + array[int] of var int: bin, + array[int] of int: w) = + forall( b in lb_array(bin)..ub_array(bin) ) ( + c >= sum ( i in index_set(bin) ) ( + w[i] * bool2int( bin[i] == b ) + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_bin_packing_capa.mzn b/share/minizinc/std/fzn_bin_packing_capa.mzn new file mode 100644 index 0000000..1eddd10 --- /dev/null +++ b/share/minizinc/std/fzn_bin_packing_capa.mzn @@ -0,0 +1,14 @@ +predicate fzn_bin_packing_capa(array[int] of int: c, + array[int] of var int: bin, + array[int] of int: w) = + forall( i in index_set(bin) ) ( + min(index_set(c)) <= bin[i] /\ bin[i] <= max(index_set(c)) + ) + /\ + forall( b in index_set(c) ) ( + c[b] >= sum ( i in index_set(bin) ) ( + w[i] * bool2int( bin[i] = b ) + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_bin_packing_capa_reif.mzn b/share/minizinc/std/fzn_bin_packing_capa_reif.mzn new file mode 100644 index 0000000..4cf5002 --- /dev/null +++ b/share/minizinc/std/fzn_bin_packing_capa_reif.mzn @@ -0,0 +1,16 @@ +predicate fzn_bin_packing_capa_reif(array[int] of int: c, + array[int] of var int: bin, + array[int] of int: w, + var bool: b) = + b <-> ( + forall( i in index_set(bin) ) ( + min(index_set(c)) <= bin[i] /\ bin[i] <= max(index_set(c)) + ) + /\ + forall( b in index_set(c) ) ( + c[b] >= sum ( i in index_set(bin) ) ( + w[i] * bool2int( bin[i] = b ) + ) + )); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_bin_packing_load.mzn b/share/minizinc/std/fzn_bin_packing_load.mzn new file mode 100644 index 0000000..9bbfa2a --- /dev/null +++ b/share/minizinc/std/fzn_bin_packing_load.mzn @@ -0,0 +1,14 @@ +predicate fzn_bin_packing_load(array[int] of var int: load, + array[int] of var int: bin, + array[int] of int: w) = + sum(load) = sum(w) + /\ forall( i in index_set(bin) ) ( + min(index_set(load)) <= bin[i] /\ bin[i] <= max(index_set(load)) + ) + /\ forall( b in index_set(load) ) ( + load[b] = sum ( i in index_set(bin) ) ( + w[i] * bool2int( bin[i] = b ) + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_bin_packing_load_reif.mzn b/share/minizinc/std/fzn_bin_packing_load_reif.mzn new file mode 100644 index 0000000..db530b8 --- /dev/null +++ b/share/minizinc/std/fzn_bin_packing_load_reif.mzn @@ -0,0 +1,16 @@ +predicate fzn_bin_packing_load_reif(array[int] of var int: load, + array[int] of var int: bin, + array[int] of int: w, + var bool: b) = + b <-> ( + sum(load) = sum(w) + /\ forall( i in index_set(bin) ) ( + min(index_set(load)) <= bin[i] /\ bin[i] <= max(index_set(load)) + ) + /\ forall( b in index_set(load) ) ( + load[b] = sum ( i in index_set(bin) ) ( + w[i] * bool2int( bin[i] = b ) + ) + ) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_bin_packing_reif.mzn b/share/minizinc/std/fzn_bin_packing_reif.mzn new file mode 100644 index 0000000..85e53da --- /dev/null +++ b/share/minizinc/std/fzn_bin_packing_reif.mzn @@ -0,0 +1,11 @@ +predicate fzn_bin_packing_reif(int: c, + array[int] of var int: bin, + array[int] of int: w, + var bool: b) = + b <-> forall( b in lb_array(bin)..ub_array(bin) ) ( + c >= sum ( i in index_set(bin) ) ( + w[i] * bool2int( bin[i] == b ) + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_bounded_dpath_enum.mzn b/share/minizinc/std/fzn_bounded_dpath_enum.mzn new file mode 100644 index 0000000..d0ffaaa --- /dev/null +++ b/share/minizinc/std/fzn_bounded_dpath_enum.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_dpath(array[int] of $$N: from, array[int] of $$N: to, array[int] of int: w, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var int: K) = + dpath(from,to,s,t,ns,es) /\ + K = sum(e in index_set(es))(es[e]*w[e]); diff --git a/share/minizinc/std/fzn_bounded_dpath_enum_reif.mzn b/share/minizinc/std/fzn_bounded_dpath_enum_reif.mzn new file mode 100644 index 0000000..7115ea6 --- /dev/null +++ b/share/minizinc/std/fzn_bounded_dpath_enum_reif.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_dpath_reif(array[int] of $$N: from, array[int] of $$N: to, array[int] of int: w, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var int: K, var bool: b) = + b <-> ( dpath(from,to,s,t,ns,es) /\ + K = sum(e in index_set(es))(es[e]*w[e]) ); diff --git a/share/minizinc/std/fzn_bounded_dpath_int.mzn b/share/minizinc/std/fzn_bounded_dpath_int.mzn new file mode 100644 index 0000000..a84927c --- /dev/null +++ b/share/minizinc/std/fzn_bounded_dpath_int.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_dpath(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + dpath(N,E,from,to,s,t,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]); diff --git a/share/minizinc/std/fzn_bounded_dpath_int_reif.mzn b/share/minizinc/std/fzn_bounded_dpath_int_reif.mzn new file mode 100644 index 0000000..746a7ae --- /dev/null +++ b/share/minizinc/std/fzn_bounded_dpath_int_reif.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_dpath_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, var int: K, var bool: b) = + b <-> ( dpath(N,E,from,to,s,t,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]) ); diff --git a/share/minizinc/std/fzn_bounded_path_enum.mzn b/share/minizinc/std/fzn_bounded_path_enum.mzn new file mode 100644 index 0000000..d42577e --- /dev/null +++ b/share/minizinc/std/fzn_bounded_path_enum.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_path(array[int] of $$N: from, array[int] of $$N: to, array[int] of int: w, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var int: K) = + path(from,to,s,t,ns,es) /\ + K = sum(e in index_set(es))(es[e]*w[e]); diff --git a/share/minizinc/std/fzn_bounded_path_enum_reif.mzn b/share/minizinc/std/fzn_bounded_path_enum_reif.mzn new file mode 100644 index 0000000..cb874b5 --- /dev/null +++ b/share/minizinc/std/fzn_bounded_path_enum_reif.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_path_reif(array[int] of $$N: from, array[int] of $$N: to, array[int] of int: w, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var int: K, var bool: b) = + b <-> ( path(from,to,s,t,ns,es) /\ + K = sum(e in index_set(es))(es[e]*w[e]) ); diff --git a/share/minizinc/std/fzn_bounded_path_int.mzn b/share/minizinc/std/fzn_bounded_path_int.mzn new file mode 100644 index 0000000..8c4765f --- /dev/null +++ b/share/minizinc/std/fzn_bounded_path_int.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_path(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + path(N,E,from,to,s,t,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]); diff --git a/share/minizinc/std/fzn_bounded_path_int_reif.mzn b/share/minizinc/std/fzn_bounded_path_int_reif.mzn new file mode 100644 index 0000000..7631db4 --- /dev/null +++ b/share/minizinc/std/fzn_bounded_path_int_reif.mzn @@ -0,0 +1,6 @@ +include "path.mzn"; + +predicate fzn_bounded_path_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, var int: K, var bool: b) = + b <-> ( path(N,E,from,to,s,t,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]) ); diff --git a/share/minizinc/std/fzn_circuit.mzn b/share/minizinc/std/fzn_circuit.mzn new file mode 100644 index 0000000..568a150 --- /dev/null +++ b/share/minizinc/std/fzn_circuit.mzn @@ -0,0 +1,16 @@ +include "all_different.mzn"; + +predicate fzn_circuit(array[int] of var int: x) = + let { set of int: S = index_set(x), + int: l = min(S), + int: n = card(S), + array[S] of var 1..n: order + } in + all_different(x) /\ + all_different(order) /\ + forall(i in S)(x[i] != i) /\ + order[l] = 1 /\ + forall(i in S)(order[i] != n -> order[x[i]] = order[i] + 1) /\ + forall(i in S)(order[i] == n -> x[i] = l ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_circuit_reif.mzn b/share/minizinc/std/fzn_circuit_reif.mzn new file mode 100644 index 0000000..84f9b9f --- /dev/null +++ b/share/minizinc/std/fzn_circuit_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_circuit_reif(array[int] of var int: x, var bool: b) = + abort("Reified circuit/1 is not supported."); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_connected.mzn b/share/minizinc/std/fzn_connected.mzn new file mode 100644 index 0000000..87d82b5 --- /dev/null +++ b/share/minizinc/std/fzn_connected.mzn @@ -0,0 +1,9 @@ +include "reachable.mzn"; + +predicate fzn_connected(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + let { var index_set(ns): r } in + reachable(from, to, r, ns, es); + + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_connected_reif.mzn b/share/minizinc/std/fzn_connected_reif.mzn new file mode 100644 index 0000000..1bbd2d6 --- /dev/null +++ b/share/minizinc/std/fzn_connected_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_connected_reif(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es, var bool: b) = + abort("Reified connected is not supported."); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_cost_mdd.mzn b/share/minizinc/std/fzn_cost_mdd.mzn new file mode 100644 index 0000000..dad8772 --- /dev/null +++ b/share/minizinc/std/fzn_cost_mdd.mzn @@ -0,0 +1,47 @@ +predicate fzn_cost_mdd(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % values of variable on edge + array[int] of int: cost, % cost of using edge + array[int] of int: to, % edge entering node 0..N where 0 = T node + var int: totalcost % total cost of path + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[1..L] of int: maxlevelcost = + [ max(e in EDGE where level[from[e]] = l)(cost[e]) | l in 1..L]; + array[1..L] of int: minlevelcost = + [ min([0] ++ [ cost[e] | e in EDGE where level[from[e]] = l /\ cost[e] < 0])| l in 1..L] ; + int: maxcost = sum(maxlevelcost); + set of int: COST = sum(minlevelcost)..L*(maxcost+1); + array[0..N] of var bool: bn; + array[EDGE] of var bool: be; + array[0..N] of var COST: ln; % distance from T + array[0..N] of var COST: un; % distance from root + } in + bn[0] /\ % true node is true + bn[1] /\ % root must hold + % T1 each node except the root enforces an outgoing edge + forall(n in NODE)(bn[n] -> exists(e in EDGE where from[e] = n)(be[e])) /\ + % T23 each edge enforces its endpoints + forall(e in EDGE)((be[e] -> bn[to[e]]) /\ (be[e] -> bn[to[e]])) /\ + % T4 each edge enforces its label + forall(e in EDGE)(be[e] -> x[level[from[e]]] in label[e]) /\ + % P1 each node enforces its outgoing edges + forall(e in EDGE)(bn[from[e]] /\ x[level[from[e]]] in label[e] -> be[e]) /\ + % P2 each node except the root enforces an incoming edge + exists(e in EDGE where to[e] = 0)(be[e]) /\ + forall(n in 2..N)(bn[n] -> exists(e in EDGE where to[e] = n)(be[e])) /\ + % P3 each label has a support + forall(i in 1..L, d in dom(x[i])) + (x[i] = d -> exists(e in EDGE where level[from[e]] = i /\ d in label[e])(be[e])) /\ + ln[0] = 0 /\ un[1] = 0 /\ + forall(n in NODE) + (ln[n] = min(e in EDGE where from[e] = n)(ln[to[e]] + cost[e] + (not be[e])*(maxcost+1 - cost[e]))) /\ + forall(n in 2..N) + (un[n] = min(e in EDGE where to[e] = n)(un[from[e]] + cost[e] + (not be[e])*(maxcost+1 - cost[e]))) /\ + forall(e in EDGE)(be[e] -> un[from[e]] + cost[e] + ln[to[e]] <= maxcost) /\ + totalcost = ln[1]; diff --git a/share/minizinc/std/fzn_cost_mdd_reif.mzn b/share/minizinc/std/fzn_cost_mdd_reif.mzn new file mode 100644 index 0000000..4fe8b42 --- /dev/null +++ b/share/minizinc/std/fzn_cost_mdd_reif.mzn @@ -0,0 +1,12 @@ +predicate fzn_cost_mdd_reif(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % values of variable on edge + array[int] of int: cost, % cost of using edge + array[int] of int: to, % edge entering node 0..N where 0 = T node + var int: totalcost, % total cost of path + var bool: b % reification variable + ) = + abort("Reified cost_mdd/9 is not supported."); diff --git a/share/minizinc/std/fzn_cost_regular.mzn b/share/minizinc/std/fzn_cost_regular.mzn new file mode 100644 index 0000000..baa050b --- /dev/null +++ b/share/minizinc/std/fzn_cost_regular.mzn @@ -0,0 +1,23 @@ +predicate fzn_cost_regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F, + array[int,int] of int: c, var int: C) = + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)); + int: n = max(index_set(x)) + 1; + array[m..n] of var 1..Q: a; + % cc[i+1] holds the accumulated cost of edges taken process up to x[i] + array[m..n] of var int: cc; + } in + a[m] = q0 /\ % Set a[0]. + cc[m] = 0 /\ % initially zero cost + forall(i in index_set(x)) ( + x[i] in 1..S /\ % Do this in case it's a var. + a[i+1] = d[a[i], x[i]] /\ % Determine a[i+1]. + cc[i+1] = c[a[i],x[i]] + cc[i] % Calculate new cost sum + ) /\ + a[n] in F /\ % Check the final state is in F. + C = cc[n] % return final cost + ; diff --git a/share/minizinc/std/fzn_cost_regular_reif.mzn b/share/minizinc/std/fzn_cost_regular_reif.mzn new file mode 100644 index 0000000..7b63bb5 --- /dev/null +++ b/share/minizinc/std/fzn_cost_regular_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_cost_regular_reif(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F, + array[int,int] of int: c, var int: C, var bool: b) = + abort("Reified cost_regular is not supported."); diff --git a/share/minizinc/std/fzn_count_eq.mzn b/share/minizinc/std/fzn_count_eq.mzn new file mode 100644 index 0000000..1f97096 --- /dev/null +++ b/share/minizinc/std/fzn_count_eq.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_eq(array[int] of var int: x, var int: y, var int: c) = + c = sum(i in index_set(x)) ( bool2int(x[i] == y) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_count_eq_reif.mzn b/share/minizinc/std/fzn_count_eq_reif.mzn new file mode 100644 index 0000000..46d5d75 --- /dev/null +++ b/share/minizinc/std/fzn_count_eq_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_eq_reif(array[int] of var int: x, var int: y, var int: c, var bool: b) = + b <-> count_eq(x,y)=c; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_count_geq.mzn b/share/minizinc/std/fzn_count_geq.mzn new file mode 100644 index 0000000..9d1ead5 --- /dev/null +++ b/share/minizinc/std/fzn_count_geq.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_geq(array[int] of var int: x, var int: y, var int: c) = + c >= sum(i in index_set(x)) ( bool2int(x[i] == y) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_count_gt.mzn b/share/minizinc/std/fzn_count_gt.mzn new file mode 100644 index 0000000..1fd713a --- /dev/null +++ b/share/minizinc/std/fzn_count_gt.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_gt(array[int] of var int: x, var int: y, var int: c) = + c > sum(i in index_set(x)) ( bool2int(x[i] == y) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_count_leq.mzn b/share/minizinc/std/fzn_count_leq.mzn new file mode 100644 index 0000000..3773cb3 --- /dev/null +++ b/share/minizinc/std/fzn_count_leq.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_leq(array[int] of var int: x, var int: y, var int: c) = + c <= sum(i in index_set(x)) ( bool2int(x[i] == y) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_count_lt.mzn b/share/minizinc/std/fzn_count_lt.mzn new file mode 100644 index 0000000..656537f --- /dev/null +++ b/share/minizinc/std/fzn_count_lt.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_lt(array[int] of var int: x, var int: y, var int: c) = + c < sum(i in index_set(x)) ( bool2int(x[i] == y) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_count_neq.mzn b/share/minizinc/std/fzn_count_neq.mzn new file mode 100644 index 0000000..7607569 --- /dev/null +++ b/share/minizinc/std/fzn_count_neq.mzn @@ -0,0 +1,4 @@ +predicate fzn_count_neq(array[int] of var int: x, var int: y, var int: c) = + c != sum(i in index_set(x)) ( bool2int(x[i] == y) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_cumulative.mzn b/share/minizinc/std/fzn_cumulative.mzn new file mode 100644 index 0000000..8ce73af --- /dev/null +++ b/share/minizinc/std/fzn_cumulative.mzn @@ -0,0 +1,53 @@ +predicate fzn_cumulative(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + let { + set of int: Tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 } + } in + if 0==card(Tasks) then /*true*/ 0==card(index_set(s)) \/ b>=0 + else + let { + int: early = min([ lb(s[i]) | i in Tasks ]), + int: late = max([ ub(s[i]) + ub(d[i]) | i in Tasks ]) + } in ( + if late - early > 5000 then + fzn_cumulative_task(s, d, r, b) + else + fzn_cumulative_time(s, d, r, b) + endif + ) + endif + ; + + +predicate fzn_cumulative_time(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + let { + set of int: Tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + int: early = min([ lb(s[i]) | i in Tasks ]), + int: late = max([ ub(s[i]) + ub(d[i]) | i in Tasks ]) + } in ( + forall( t in early..late ) ( + b >= sum( i in Tasks ) ( + bool2int(s[i] <= t /\ t < s[i] + d[i]) * r[i] + ) + ) + ); + +predicate fzn_cumulative_task(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + let { + set of int: Tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 } + } in ( + forall( j in Tasks ) ( + b >= r[j] + sum( i in Tasks where i != j ) ( + bool2int(s[i] <= s[j] /\ s[j] < s[i] + d[i] ) * r[i] + ) + ) + ); + diff --git a/share/minizinc/std/fzn_cumulative_opt.mzn b/share/minizinc/std/fzn_cumulative_opt.mzn new file mode 100644 index 0000000..9526b4b --- /dev/null +++ b/share/minizinc/std/fzn_cumulative_opt.mzn @@ -0,0 +1,16 @@ +predicate fzn_cumulative_opt(array[int] of var opt int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b) = + let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = + min([ lb(s[i]) | i in tasks ]) .. + max([ ub(s[i]) + ub(d[i]) | i in tasks ]) + } + in + forall( t in times ) ( + b >= sum( i in tasks ) ( + bool2int( occurs(s[i]) /\ deopt(s[i]) <= t /\ t < deopt(s[i]) + d[i] ) * r[i] + ) + ); diff --git a/share/minizinc/std/fzn_cumulative_opt_reif.mzn b/share/minizinc/std/fzn_cumulative_opt_reif.mzn new file mode 100644 index 0000000..f5e8d09 --- /dev/null +++ b/share/minizinc/std/fzn_cumulative_opt_reif.mzn @@ -0,0 +1,16 @@ +predicate fzn_cumulative_opt_reif(array[int] of var opt int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b, var bool: bb) = + let { + set of int: tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + set of int: times = + min([ lb(s[i]) | i in tasks ]) .. + max([ ub(s[i]) + ub(d[i]) | i in tasks ]) + } + in + bb <-> forall( t in times ) ( + b >= sum( i in tasks ) ( + bool2int( occurs(s[i]) /\ deopt(s[i]) <= t /\ t < deopt(s[i]) + d[i] ) * r[i] + ) + ); diff --git a/share/minizinc/std/fzn_cumulative_reif.mzn b/share/minizinc/std/fzn_cumulative_reif.mzn new file mode 100644 index 0000000..9089558 --- /dev/null +++ b/share/minizinc/std/fzn_cumulative_reif.mzn @@ -0,0 +1,53 @@ +predicate fzn_cumulative_reif(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b, var bool: bb) = + let { + set of int: Tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 } + } in + if 0==card(Tasks) then bb <-> ( 0==card(index_set(s)) \/ b>=0 ) + else + let { + int: early = min([ lb(s[i]) | i in Tasks ]), + int: late = max([ ub(s[i]) + ub(d[i]) | i in Tasks ]) + } in ( + if late - early > 5000 then + fzn_cumulative_task_reif(s, d, r, b, bb) + else + fzn_cumulative_time_reif(s, d, r, b, bb) + endif + ) + endif + ; + + +predicate fzn_cumulative_time_reif(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b, var bool: bb) = + let { + set of int: Tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 }, + int: early = min([ lb(s[i]) | i in Tasks ]), + int: late = max([ ub(s[i]) + ub(d[i]) | i in Tasks ]) + } in ( + bb <-> forall( t in early..late ) ( + b >= sum( i in Tasks ) ( + bool2int(s[i] <= t /\ t < s[i] + d[i]) * r[i] + ) + ) + ); + +predicate fzn_cumulative_task_reif(array[int] of var int: s, + array[int] of var int: d, + array[int] of var int: r, var int: b, var bool: bb) = + let { + set of int: Tasks = + {i | i in index_set(s) where ub(r[i]) > 0 /\ ub(d[i]) > 0 } + } in ( + bb <-> forall( j in Tasks ) ( + b >= r[j] + sum( i in Tasks where i != j ) ( + bool2int(s[i] <= s[j] /\ s[j] < s[i] + d[i] ) * r[i] + ) + ) + ); + diff --git a/share/minizinc/std/fzn_dag.mzn b/share/minizinc/std/fzn_dag.mzn new file mode 100644 index 0000000..c0078b0 --- /dev/null +++ b/share/minizinc/std/fzn_dag.mzn @@ -0,0 +1,15 @@ +predicate fzn_dag(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + let { + set of int: EDGE = index_set(es); + array[index_set(ns)] of var 0..length(ns)-1: dist; /* distance of longest path */ + + } in + forall(n in index_set(ns))(not ns[n] -> dist[n] = 0) /\ + forall(e in EDGE) + (es[e] -> dist[from[e]] + 1 <= dist[to[e]]) + /\ + % redundant constraint to ensure all distances are fixed + forall(n in index_set(ns)) + (dist[n] = max( [0] ++ [ (dist[from[e]] + 1)*es[e] | e in EDGE where to[e] = n ])) + ; diff --git a/share/minizinc/std/fzn_dag_reif.mzn b/share/minizinc/std/fzn_dag_reif.mzn new file mode 100644 index 0000000..2c65991 --- /dev/null +++ b/share/minizinc/std/fzn_dag_reif.mzn @@ -0,0 +1,3 @@ +predicate fzn_dag_reif(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es, var bool: b) = + abort("Reified dag is not supported"); diff --git a/share/minizinc/std/fzn_dconnected.mzn b/share/minizinc/std/fzn_dconnected.mzn new file mode 100644 index 0000000..868ae8b --- /dev/null +++ b/share/minizinc/std/fzn_dconnected.mzn @@ -0,0 +1,8 @@ +include "reachable.mzn"; + +predicate fzn_dconnected(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + let { var index_set(ns): r } in + dreachable(from, to, r, ns, es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dconnected_reif.mzn b/share/minizinc/std/fzn_dconnected_reif.mzn new file mode 100644 index 0000000..b3b8c72 --- /dev/null +++ b/share/minizinc/std/fzn_dconnected_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_dconnected_reif(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified dconnected is not supported."); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_decreasing_bool.mzn b/share/minizinc/std/fzn_decreasing_bool.mzn new file mode 100644 index 0000000..dce7b39 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_bool.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_bool(array[int] of var bool: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); diff --git a/share/minizinc/std/fzn_decreasing_bool_reif.mzn b/share/minizinc/std/fzn_decreasing_bool_reif.mzn new file mode 100644 index 0000000..b6bc2d9 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_bool_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_bool_reif(array[int] of var bool: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); diff --git a/share/minizinc/std/fzn_decreasing_float.mzn b/share/minizinc/std/fzn_decreasing_float.mzn new file mode 100644 index 0000000..8b71ad7 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_float.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_float(array[int] of var float: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); diff --git a/share/minizinc/std/fzn_decreasing_float_reif.mzn b/share/minizinc/std/fzn_decreasing_float_reif.mzn new file mode 100644 index 0000000..b9fad71 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_float_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_float_reif(array[int] of var float: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); diff --git a/share/minizinc/std/fzn_decreasing_int.mzn b/share/minizinc/std/fzn_decreasing_int.mzn new file mode 100644 index 0000000..c4718c9 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_int.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_int(array[int] of var int: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); + diff --git a/share/minizinc/std/fzn_decreasing_int_reif.mzn b/share/minizinc/std/fzn_decreasing_int_reif.mzn new file mode 100644 index 0000000..20ddd82 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_int_reif.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_int_reif(array[int] of var int: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); + diff --git a/share/minizinc/std/fzn_decreasing_set.mzn b/share/minizinc/std/fzn_decreasing_set.mzn new file mode 100644 index 0000000..50f292c --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_set.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_set(array[int] of var set of int: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); + diff --git a/share/minizinc/std/fzn_decreasing_set_reif.mzn b/share/minizinc/std/fzn_decreasing_set_reif.mzn new file mode 100644 index 0000000..3ed8d53 --- /dev/null +++ b/share/minizinc/std/fzn_decreasing_set_reif.mzn @@ -0,0 +1,7 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in decreasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_decreasing_set_reif(array[int] of var set of int: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] >= x[i]); + diff --git a/share/minizinc/std/fzn_diffn.mzn b/share/minizinc/std/fzn_diffn.mzn new file mode 100644 index 0000000..7fc2deb --- /dev/null +++ b/share/minizinc/std/fzn_diffn.mzn @@ -0,0 +1,10 @@ +predicate fzn_diffn(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy) = + forall(i,j in index_set(x) where i < j)( + x[i] + dx[i] <= x[j] \/ y[i] + dy[i] <= y[j] \/ + x[j] + dx[j] <= x[i] \/ y[j] + dy[j] <= y[i] + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_diffn_k.mzn b/share/minizinc/std/fzn_diffn_k.mzn new file mode 100644 index 0000000..68308b2 --- /dev/null +++ b/share/minizinc/std/fzn_diffn_k.mzn @@ -0,0 +1,16 @@ +predicate fzn_diffn_k(array[int,int] of var int: box_posn, + array[int,int] of var int: box_size) = + let { set of int: DIMS= index_set_2of2(box_posn) } in + forall(b1, b2 in index_set_1of2(box_posn) where b1 < b2) + (fzn_diffn_nonoverlap_k([ box_posn[b1,j] | j in DIMS ], + [ box_size[b1,j] | j in DIMS ], + [ box_posn[b2,j] | j in DIMS ], + [ box_size[b2,j] | j in DIMS ] ) + ); + +predicate fzn_diffn_nonoverlap_k(array[int] of var int: x1, + array[int] of var int: w1, + array[int] of var int: x2, + array[int] of var int: w2) = + exists(j in index_set(x1)) + (x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j]); diff --git a/share/minizinc/std/fzn_diffn_k_reif.mzn b/share/minizinc/std/fzn_diffn_k_reif.mzn new file mode 100644 index 0000000..f767850 --- /dev/null +++ b/share/minizinc/std/fzn_diffn_k_reif.mzn @@ -0,0 +1,16 @@ +predicate fzn_diffn_k_reif(array[int,int] of var int: box_posn, + array[int,int] of var int: box_size, var bool: b) = + let { set of int: DIMS= index_set_2of2(box_posn) } in + b <-> forall(b1, b2 in index_set_1of2(box_posn) where b1 < b2) + (fzn_diffn_nonoverlap_k_for_reif([ box_posn[b1,j] | j in DIMS ], + [ box_size[b1,j] | j in DIMS ], + [ box_posn[b2,j] | j in DIMS ], + [ box_size[b2,j] | j in DIMS ] ) + ); + +predicate fzn_diffn_nonoverlap_k_for_reif(array[int] of var int: x1, + array[int] of var int: w1, + array[int] of var int: x2, + array[int] of var int: w2) = + exists(j in index_set(x1)) + (x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j]); diff --git a/share/minizinc/std/fzn_diffn_nonstrict.mzn b/share/minizinc/std/fzn_diffn_nonstrict.mzn new file mode 100644 index 0000000..1bfcf12 --- /dev/null +++ b/share/minizinc/std/fzn_diffn_nonstrict.mzn @@ -0,0 +1,9 @@ +predicate fzn_diffn_nonstrict(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy) = + forall(i,j in index_set(x) where i < j)( + dx[i] = 0 \/ dx[j] = 0 \/ dy[i]=0 \/ dy[j]=0 \/ + x[i] + dx[i] <= x[j] \/ y[i] + dy[i] <= y[j] \/ + x[j] + dx[j] <= x[i] \/ y[j] + dy[j] <= y[i] + ); diff --git a/share/minizinc/std/fzn_diffn_nonstrict_k.mzn b/share/minizinc/std/fzn_diffn_nonstrict_k.mzn new file mode 100644 index 0000000..5204495 --- /dev/null +++ b/share/minizinc/std/fzn_diffn_nonstrict_k.mzn @@ -0,0 +1,17 @@ +predicate fzn_diffn_nonstrict_k(array[int,int] of var int: box_posn, + array[int,int] of var int: box_size) = + let { set of int: DIMS= index_set_2of2(box_posn) } in + forall(b1, b2 in index_set_1of2(box_posn) where b1 < b2) + (fzn_diffn_nonstrict_nonoverlap_k([ box_posn[b1,j] | j in DIMS ], + [ box_size[b1,j] | j in DIMS ], + [ box_posn[b2,j] | j in DIMS ], + [ box_size[b2,j] | j in DIMS ] ) + ) + ; + +predicate fzn_diffn_nonstrict_nonoverlap_k(array[int] of var int: x1, + array[int] of var int: w1, + array[int] of var int: x2, + array[int] of var int: w2) = + exists(j in index_set(x1)) + (w1[j] = 0 \/ w2[j] = 0 \/ x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j]); diff --git a/share/minizinc/std/fzn_diffn_nonstrict_k_reif.mzn b/share/minizinc/std/fzn_diffn_nonstrict_k_reif.mzn new file mode 100644 index 0000000..0dd3f9b --- /dev/null +++ b/share/minizinc/std/fzn_diffn_nonstrict_k_reif.mzn @@ -0,0 +1,17 @@ +predicate fzn_diffn_nonstrict_k_reif(array[int,int] of var int: box_posn, + array[int,int] of var int: box_size, var bool: b) = + let { set of int: DIMS= index_set_2of2(box_posn) } in + b <-> forall(b1, b2 in index_set_1of2(box_posn) where b1 < b2) + (fzn_diffn_nonstrict_nonoverlap_k_for_reif([ box_posn[b1,j] | j in DIMS ], + [ box_size[b1,j] | j in DIMS ], + [ box_posn[b2,j] | j in DIMS ], + [ box_size[b2,j] | j in DIMS ] ) + ) + ; + +predicate fzn_diffn_nonstrict_nonoverlap_k_for_reif(array[int] of var int: x1, + array[int] of var int: w1, + array[int] of var int: x2, + array[int] of var int: w2) = + exists(j in index_set(x1)) + (w1[j] = 0 \/ w2[j] = 0 \/ x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j]); diff --git a/share/minizinc/std/fzn_diffn_nonstrict_reif.mzn b/share/minizinc/std/fzn_diffn_nonstrict_reif.mzn new file mode 100644 index 0000000..81e2c8c --- /dev/null +++ b/share/minizinc/std/fzn_diffn_nonstrict_reif.mzn @@ -0,0 +1,10 @@ +predicate fzn_diffn_nonstrict_reif(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy, + var bool: b) = + b <-> forall(i,j in index_set(x) where i < j)( + dx[i] = 0 \/ dx[j] = 0 \/ dy[i]=0 \/ dy[j]=0 \/ + x[i] + dx[i] <= x[j] \/ y[i] + dy[i] <= y[j] \/ + x[j] + dx[j] <= x[i] \/ y[j] + dy[j] <= y[i] + ); diff --git a/share/minizinc/std/fzn_diffn_reif.mzn b/share/minizinc/std/fzn_diffn_reif.mzn new file mode 100644 index 0000000..4b99609 --- /dev/null +++ b/share/minizinc/std/fzn_diffn_reif.mzn @@ -0,0 +1,11 @@ +predicate fzn_diffn_reif(array[int] of var int: x, + array[int] of var int: y, + array[int] of var int: dx, + array[int] of var int: dy, + var bool: b) = + b <-> forall(i,j in index_set(x) where i < j)( + x[i] + dx[i] <= x[j] \/ y[i] + dy[i] <= y[j] \/ + x[j] + dx[j] <= x[i] \/ y[j] + dy[j] <= y[i] + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_disjoint.mzn b/share/minizinc/std/fzn_disjoint.mzn new file mode 100644 index 0000000..d371103 --- /dev/null +++ b/share/minizinc/std/fzn_disjoint.mzn @@ -0,0 +1,2 @@ +predicate fzn_disjoint(var set of int: s1, var set of int: s2) = + s1 intersect s2 == {}; diff --git a/share/minizinc/std/fzn_disjoint_reif.mzn b/share/minizinc/std/fzn_disjoint_reif.mzn new file mode 100644 index 0000000..740507f --- /dev/null +++ b/share/minizinc/std/fzn_disjoint_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_disjoint_reif(var set of int: s1, var set of int: s2, var bool: b) = + b <-> s1 intersect s2 == {}; diff --git a/share/minizinc/std/fzn_disjunctive.mzn b/share/minizinc/std/fzn_disjunctive.mzn new file mode 100644 index 0000000..fa30f6d --- /dev/null +++ b/share/minizinc/std/fzn_disjunctive.mzn @@ -0,0 +1,6 @@ +predicate fzn_disjunctive(array[int] of var int: s, + array[int] of var int: d) = + forall (i in index_set(d)) (d[i] >= 0) /\ + forall (i,j in index_set(d) where i forall (i,j in index_set(d) where i ( forall (i in index_set(d)) (d[i] >= 0) /\ + forall (i,j in index_set(d) where i= 0) /\ + forall (i,j in index_set(d) where i= 0) /\ + forall (i,j in index_set(d) where i ( forall (i in index_set(d)) (d[i] >= 0) /\ + forall (i,j in index_set(d) where i ( + forall (i in index_set(d)) (d[i] >= 0) /\ + forall (i,j in index_set(d) where i forall (i in index_set(card)) ( + card[i] == sum(j in index_set(base)) ( + bool2int(value[i] = base[j]) + ) + ); diff --git a/share/minizinc/std/fzn_dpath_enum.mzn b/share/minizinc/std/fzn_dpath_enum.mzn new file mode 100644 index 0000000..900ffec --- /dev/null +++ b/share/minizinc/std/fzn_dpath_enum.mzn @@ -0,0 +1,32 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_dpath(array[int] of $$N: from, array[int] of $$N: to, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { set of int: EDGE = index_set(es); + array[index_set(ns)] of var 0..length(ns)-1: dist; /* distance from root */ + } in + ns[s] /\ % source is selected + ns[t] /\ % dest is selected + dist[s] = 0 /\ % distance of source is zero + forall(n in index_set(ns)) % nonselected nodes have distance 0 + (not ns[n] -> dist[n] = 0) /\ + forall(n in index_set(ns)) % 1 incoming edge + ((ns[n] /\ n != s) -> exists(e in EDGE where to[e] = n)(es[e])) /\ + forall(n in index_set(ns)) % 1 outgoing edge + ((ns[n] /\ n != t) -> exists(e in EDGE where from[e] = n)(es[e])) /\ + forall(n in index_set(ns)) % 1 incoming edge + ((ns[n] /\ n != s) -> sum(e in EDGE where to[e] = n)(es[e]) <= 1) /\ + forall(n in index_set(ns)) % 1 outgoing edge + ((ns[n] /\ n != t) -> sum(e in EDGE where from[e] = n)(es[e]) <= 1) /\ + % alternate for the previous 8 lines + % forall(n in index_set(ns)) % 1 incoming edge + % ((ns[n] /\ n != s) -> sum(e in EDGE where to[e] = n)(es[e]) = 1) /\ + % forall(n in index_set(ns)) % 1 outgoing edge + % ((ns[n] /\ n != t) -> sum(e in EDGE where from[e] = n)(es[e]) = 1) /\ + forall(e in EDGE) + (es[e] -> dist[to[e]] = dist[from[e]] + 1) /\ + sum(n in index_set(ns))(ns[n]) = sum(e in EDGE)(es[e]) + 1 /\ + subgraph(from,to,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dpath_enum_reif.mzn b/share/minizinc/std/fzn_dpath_enum_reif.mzn new file mode 100644 index 0000000..e946945 --- /dev/null +++ b/share/minizinc/std/fzn_dpath_enum_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_dpath_reif(array[int] of $$N: from, array[int] of $$N: to, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, var bool: b) = + abort("Reified dpath constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dpath_int.mzn b/share/minizinc/std/fzn_dpath_int.mzn new file mode 100644 index 0000000..41b7519 --- /dev/null +++ b/share/minizinc/std/fzn_dpath_int.mzn @@ -0,0 +1,9 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_dpath(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es) = + dtree(N,E,from,to,s,ns,es) /\ + dtree(N,E,to,from,t,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dpath_int_reif.mzn b/share/minizinc/std/fzn_dpath_int_reif.mzn new file mode 100644 index 0000000..0bf7d07 --- /dev/null +++ b/share/minizinc/std/fzn_dpath_int_reif.mzn @@ -0,0 +1,10 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_dpath_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, + var bool: b) = + b <-> ( dtree(N,E,from,to,s,ns,es) /\ + dtree(N,E,to,from,t,ns,es) ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dreachable_enum.mzn b/share/minizinc/std/fzn_dreachable_enum.mzn new file mode 100644 index 0000000..108155a --- /dev/null +++ b/share/minizinc/std/fzn_dreachable_enum.mzn @@ -0,0 +1,26 @@ +include "subgraph.mzn"; + +predicate fzn_dreachable(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { + array[index_set(ns)] of var 0..card(index_set(ns))-1: dist; /* distance from root */ + array[index_set(ns)] of var index_set(ns): parent; /* parent */ + } in + ns[r] /\ % the root must be chosen + dist[r] = 0 /\ % root is at distance 0 + parent[r] = r /\ % root is its own parent + forall(n in index_set(ns)) % nonselected nodes have parent 0 + (not ns[n] -> parent[n] = n) /\ + forall(n in index_set(ns)) % nonselected nodes have distance 0 + (not ns[n] -> dist[n] = 0) /\ + forall(n in index_set(ns)) % each in node except root must have a parent + (ns[n] -> (n = r \/ parent[n] != n)) /\ + forall(n in index_set(ns)) % each in node with a parent must be in and also its parent + (parent[n] != n -> (ns[n] /\ ns[parent[n]])) /\ + forall(n in index_set(ns)) % each except with a parent is one more than its parent + (parent[n] != n -> dist[n] = dist[parent[n]] + 1) /\ + forall(n in index_set(ns)) % each node with a parent must have that edge in + (parent[n] != n -> exists(e in index_set(from) where to[e] = n)(es[e] /\ from[e] = parent[n])) /\ + subgraph(from,to,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dreachable_enum_reif.mzn b/share/minizinc/std/fzn_dreachable_enum_reif.mzn new file mode 100644 index 0000000..f5914ce --- /dev/null +++ b/share/minizinc/std/fzn_dreachable_enum_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_dreachable_reif(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified dreachable constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dreachable_int.mzn b/share/minizinc/std/fzn_dreachable_int.mzn new file mode 100644 index 0000000..4ca6474 --- /dev/null +++ b/share/minizinc/std/fzn_dreachable_int.mzn @@ -0,0 +1,26 @@ +include "subgraph.mzn"; + +predicate fzn_dreachable(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es) = + let { + set of int: NODE = 1..N; + array[NODE] of var 0..N-1: dist; /* distance from root */ + array[NODE] of var 0..N: parent; /* parent */ + } in + ns[r] /\ % the root must be chosen + dist[r] = 0 /\ % root is at distance 0 + forall(n in NODE) % nonselected nodes have parent 0 + (not ns[n] -> parent[n] = 0) /\ + forall(n in NODE) % nonselected nodes have distance 0 + (not ns[n] -> dist[n] = 0) /\ + forall(n in NODE) % each in node except root must have a parent + (ns[n] -> (n = r \/ parent[n] > 0)) /\ + forall(n in NODE) % each in node with a parent must be in and also its parent + (parent[n] > 0 -> (ns[n] /\ ns[parent[n]])) /\ + forall(n in NODE) % each except with a parent is one more than its parent + (parent[n] > 0 -> dist[n] = dist[parent[n]] + 1) /\ + forall(n in NODE) % each node with a parent must have that edge in + (parent[n] > 0 -> exists(e in 1..E)(es[e] /\ from[e] = parent[n] /\ to[e] = n)) /\ + subgraph(N,E,from,to,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dreachable_int_reif.mzn b/share/minizinc/std/fzn_dreachable_int_reif.mzn new file mode 100644 index 0000000..878dde6 --- /dev/null +++ b/share/minizinc/std/fzn_dreachable_int_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_dreachable_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified dreachable constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dsteiner.mzn b/share/minizinc/std/fzn_dsteiner.mzn new file mode 100644 index 0000000..e5b1f05 --- /dev/null +++ b/share/minizinc/std/fzn_dsteiner.mzn @@ -0,0 +1,8 @@ +include "tree.mzn"; + +predicate fzn_dsteiner(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + dtree(N,E,from,to,r,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dsteiner_reif.mzn b/share/minizinc/std/fzn_dsteiner_reif.mzn new file mode 100644 index 0000000..3802a2d --- /dev/null +++ b/share/minizinc/std/fzn_dsteiner_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_dsteiner_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, var int: K, var bool: b) = + abort("Reified steiner constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dtree_enum.mzn b/share/minizinc/std/fzn_dtree_enum.mzn new file mode 100644 index 0000000..eeac684 --- /dev/null +++ b/share/minizinc/std/fzn_dtree_enum.mzn @@ -0,0 +1,30 @@ +include "subgraph.mzn"; + +predicate fzn_dtree(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { + set of int: EDGE = index_set(es); + array[index_set(ns)] of var 0..length(ns)-1: dist; /* distance from root */ + array[index_set(ns)] of var index_set(ns): parent; /* parent */ + } in + ns[r] /\ % the root must be chosen + dist[r] = 0 /\ % root is at distance 0 + parent[r] = r /\ % root is its own parent + forall(n in index_set(ns)) % nonselected nodes have parent 0 + (not ns[n] -> parent[n] = n) /\ + forall(n in index_set(ns)) % nonselected nodes have distance 0 + (not ns[n] -> dist[n] = 0) /\ + forall(n in index_set(ns)) % each in node except root must have a parent + (ns[n] -> (n = r \/ parent[n] != n)) /\ + forall(n in index_set(ns)) % each in node with a parent must be in and also its parent + (parent[n] != n -> (ns[n] /\ ns[parent[n]])) /\ + forall(n in index_set(ns)) % each except with a parent is one more than its parent + (parent[n] != n -> dist[n] = dist[parent[n]] + 1) /\ + forall(n in index_set(ns)) % each node with a parent must have that edge in + (parent[n] != n -> exists(e in EDGE where to[e] = n)(es[e] /\ from[e] = parent[n])) /\ + forall(e in EDGE) % each edge must be part of the parent relation + (es[e] -> parent[to[e]] = from[e]) /\ + sum(e in EDGE)(es[e]) = sum(n in index_set(ns))(ns[n]) - 1 /\ % redundant relationship of trees + subgraph(from,to,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dtree_enum_reif.mzn b/share/minizinc/std/fzn_dtree_enum_reif.mzn new file mode 100644 index 0000000..4f789ba --- /dev/null +++ b/share/minizinc/std/fzn_dtree_enum_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_dtree_reif(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified dtree constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dtree_int.mzn b/share/minizinc/std/fzn_dtree_int.mzn new file mode 100644 index 0000000..b9b0f85 --- /dev/null +++ b/share/minizinc/std/fzn_dtree_int.mzn @@ -0,0 +1,30 @@ +include "subgraph.mzn"; + +predicate fzn_dtree(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es) = + let { + set of int: NODE = 1..N; + set of int: EDGE = 1..E; + array[NODE] of var 0..N-1: dist; /* distance from root */ + array[NODE] of var 0..N: parent; /* parent */ + } in + ns[r] /\ % the root must be chosen + dist[r] = 0 /\ % root is at distance 0 + forall(n in NODE) % nonselected nodes have parent 0 + (not ns[n] -> parent[n] <= 0) /\ + forall(n in NODE) % nonselected nodes have distance 0 + (not ns[n] -> dist[n] = 0) /\ + forall(n in NODE) % each in node except root must have a parent + (ns[n] -> (n = r \/ parent[n] > 0)) /\ + forall(n in NODE) % each node with a parent then parent is in + (parent[n] > 0 -> (ns[n] /\ ns[parent[n]])) /\ + forall(n in NODE) % each node with a parent is one more than its parent + (parent[n] > 0 -> dist[n] = dist[parent[n]] + 1) /\ + forall(n in NODE) % each node with a parent must have that edge in + (parent[n] > 0 -> exists(e in EDGE)(es[e] /\ from[e] = parent[n] /\ to[e] = n)) /\ + forall(e in EDGE) % each edge must be part of the parent relation + (es[e] -> parent[to[e]] = from[e]) /\ + sum(e in EDGE)(es[e]) = sum(n in NODE)(ns[n]) - 1 /\ % redundant relationship of trees + subgraph(N,E,from,to,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dtree_int_reif.mzn b/share/minizinc/std/fzn_dtree_int_reif.mzn new file mode 100644 index 0000000..28e479a --- /dev/null +++ b/share/minizinc/std/fzn_dtree_int_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_dtree_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, var bool: b) = + abort("Reified dtree constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dwst.mzn b/share/minizinc/std/fzn_dwst.mzn new file mode 100644 index 0000000..324854a --- /dev/null +++ b/share/minizinc/std/fzn_dwst.mzn @@ -0,0 +1,9 @@ +include "tree.mzn"; + +predicate fzn_dwst(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: r, array[int] of var bool: es, var int: K) = + let { array[1..N] of bool: ns = [true | n in 1..N]; } in + dtree(N,E,from,to,r,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_dwst_reif.mzn b/share/minizinc/std/fzn_dwst_reif.mzn new file mode 100644 index 0000000..81cfa88 --- /dev/null +++ b/share/minizinc/std/fzn_dwst_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_dwst_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: r, array[int] of var bool: es, var int: K, var bool: b) = + abort("Reified dwst constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_exactly_int.mzn b/share/minizinc/std/fzn_exactly_int.mzn new file mode 100644 index 0000000..59bf6c7 --- /dev/null +++ b/share/minizinc/std/fzn_exactly_int.mzn @@ -0,0 +1,8 @@ +include "count_fn.mzn"; + +%-----------------------------------------------------------------------------% +% Requires exactly 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_exactly_int(int: n, array[int] of var int: x, int: v) = + n == count(x, v); diff --git a/share/minizinc/std/fzn_exactly_int_reif.mzn b/share/minizinc/std/fzn_exactly_int_reif.mzn new file mode 100644 index 0000000..73f924b --- /dev/null +++ b/share/minizinc/std/fzn_exactly_int_reif.mzn @@ -0,0 +1,8 @@ +include "count_fn.mzn"; + +%-----------------------------------------------------------------------------% +% Requires exactly 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_exactly_int_reif(int: n, array[int] of var int: x, int: v, var bool: b) = + b <-> n == count(x, v); diff --git a/share/minizinc/std/fzn_exactly_set.mzn b/share/minizinc/std/fzn_exactly_set.mzn new file mode 100644 index 0000000..40a02ee --- /dev/null +++ b/share/minizinc/std/fzn_exactly_set.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires exactly 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_exactly_set(int: n, array[int] of var set of int: x, set of int: v) = + n == sum(i in index_set(x)) ( bool2int(x[i] == v) ); diff --git a/share/minizinc/std/fzn_exactly_set_reif.mzn b/share/minizinc/std/fzn_exactly_set_reif.mzn new file mode 100644 index 0000000..4c2b056 --- /dev/null +++ b/share/minizinc/std/fzn_exactly_set_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires exactly 'n' variables in 'x' to take the value 'v'. +%-----------------------------------------------------------------------------% + +predicate fzn_exactly_set_reif(int: n, array[int] of var set of int: x, set of int: v, var bool: b) = + b <-> n == sum(i in index_set(x)) ( bool2int(x[i] == v) ); diff --git a/share/minizinc/std/fzn_geost.mzn b/share/minizinc/std/fzn_geost.mzn new file mode 100644 index 0000000..a81d7f4 --- /dev/null +++ b/share/minizinc/std/fzn_geost.mzn @@ -0,0 +1,31 @@ +include "fzn_geost_nonoverlap_k.mzn"; + +predicate fzn_geost( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind +) = + % A few useful definitions + let { + set of int: DIMS = 1..k; + set of int: SHAPES = 1..length(shape); + set of int: OBJECTS = index_set(kind); + } in + forall(o1, o2 in OBJECTS where o1 < o2)( + forall(s1 in dom(kind[o1]), s2 in dom(kind[o2]))( + (kind[o1] = s1 /\ kind[o2] = s2 -> + forall(r1 in shape[s1], r2 in shape[s2])( + fzn_geost_nonoverlap_k( + [ x[o1,j] + rect_offset[r1,j] | j in DIMS ], + [ rect_size[r1,j] | j in DIMS ], + [ x[o2,j] + rect_offset[r2,j] | j in DIMS ], + [ rect_size[r2,j] | j in DIMS ] + ) + ) + ) + ) + ); + diff --git a/share/minizinc/std/fzn_geost_bb.mzn b/share/minizinc/std/fzn_geost_bb.mzn new file mode 100644 index 0000000..ff62569 --- /dev/null +++ b/share/minizinc/std/fzn_geost_bb.mzn @@ -0,0 +1,28 @@ +predicate fzn_geost_bb( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + array[int ] of var int : l , + array[int ] of var int : u +) = + % Two useful definitions + let { + set of int: DIMS = 1..k; + set of int: OBJECTS = index_set(kind); + } in + % Posting the geost constraint + fzn_geost(k, rect_size, rect_offset, shape, x, kind) + /\ % Posting the bounding box constraints + forall(o in OBJECTS)( + forall(s in dom(kind[o]))( + (kind[o] = s -> + forall(r in shape[s], j in DIMS)( + x[o,j] + rect_offset[r,j] >= l[j] + /\ x[o,j] + rect_offset[r,j] + rect_size[r,j] <= u[j] + ) + ) + ) + ); diff --git a/share/minizinc/std/fzn_geost_bb_reif.mzn b/share/minizinc/std/fzn_geost_bb_reif.mzn new file mode 100644 index 0000000..cbd4c9d --- /dev/null +++ b/share/minizinc/std/fzn_geost_bb_reif.mzn @@ -0,0 +1,33 @@ +include "fzn_geost.mzn"; +include "fzn_geost_reif.mzn"; + + +predicate fzn_geost_bb_reif( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + array[int ] of var int : l , + array[int ] of var int : u , + var bool: b +) = + % Two useful definitions + let { + set of int: DIMS = 1..k; + set of int: OBJECTS = index_set(kind); + } in b <-> ( + % Posting the geost constraint + fzn_geost(k, rect_size, rect_offset, shape, x, kind) + /\ % Posting the bounding box constraints + forall(o in OBJECTS)( + forall(s in dom(kind[o]))( + (kind[o] = s -> + forall(r in shape[s], j in DIMS)( + x[o,j] + rect_offset[r,j] >= l[j] + /\ x[o,j] + rect_offset[r,j] + rect_size[r,j] <= u[j] + ) + ) + ) + )); diff --git a/share/minizinc/std/fzn_geost_nonoverlap_k.mzn b/share/minizinc/std/fzn_geost_nonoverlap_k.mzn new file mode 100644 index 0000000..45d7ec4 --- /dev/null +++ b/share/minizinc/std/fzn_geost_nonoverlap_k.mzn @@ -0,0 +1,10 @@ +predicate fzn_geost_nonoverlap_k( + array[int] of var int : x1, + array[int] of int : w1, + array[int] of var int : x2, + array[int] of int : w2 +) = + % Non-overlap constraint + exists(j in index_set(x1))( + x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j] + ); diff --git a/share/minizinc/std/fzn_geost_nonoverlap_k_reif.mzn b/share/minizinc/std/fzn_geost_nonoverlap_k_reif.mzn new file mode 100644 index 0000000..d9238e9 --- /dev/null +++ b/share/minizinc/std/fzn_geost_nonoverlap_k_reif.mzn @@ -0,0 +1,11 @@ +predicate fzn_geost_nonoverlap_k_reif( + array[int] of var int : x1, + array[int] of int : w1, + array[int] of var int : x2, + array[int] of int : w2, + var bool: b +) = + % Non-overlap constraint + b <-> exists(j in index_set(x1))( + x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j] + ); diff --git a/share/minizinc/std/fzn_geost_reif.mzn b/share/minizinc/std/fzn_geost_reif.mzn new file mode 100644 index 0000000..f8a2df9 --- /dev/null +++ b/share/minizinc/std/fzn_geost_reif.mzn @@ -0,0 +1,33 @@ +include "fzn_geost_nonoverlap_k.mzn"; +include "fzn_geost_nonoverlap_k_reif.mzn"; + +predicate fzn_geost_reif( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + var bool: b +) = + % A few useful definitions + let { + set of int: DIMS = 1..k; + set of int: SHAPES = 1..length(shape); + set of int: OBJECTS = index_set(kind); + } in b <-> + forall(o1, o2 in OBJECTS where o1 < o2)( + forall(s1 in dom(kind[o1]), s2 in dom(kind[o2]))( + (kind[o1] = s1 /\ kind[o2] = s2 -> + forall(r1 in shape[s1], r2 in shape[s2])( + fzn_geost_nonoverlap_k( + [ x[o1,j] + rect_offset[r1,j] | j in DIMS ], + [ rect_size[r1,j] | j in DIMS ], + [ x[o2,j] + rect_offset[r2,j] | j in DIMS ], + [ rect_size[r2,j] | j in DIMS ] + ) + ) + ) + ) + ); + diff --git a/share/minizinc/std/fzn_geost_smallest_bb.mzn b/share/minizinc/std/fzn_geost_smallest_bb.mzn new file mode 100644 index 0000000..1e57774 --- /dev/null +++ b/share/minizinc/std/fzn_geost_smallest_bb.mzn @@ -0,0 +1,35 @@ +predicate fzn_geost_smallest_bb( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + array[int ] of var int : l , + array[int ] of var int : u +) = + % Two useful definitions + let { + set of int: DIMS = 1..k; + set of int: OBJECTS = index_set(kind); + } in ( + % Posting the geost constraint + fzn_geost_bb(k, rect_size, rect_offset, shape, x, kind, l, u) + /\ % Posting the smallest bounding box constraints + forall(j in DIMS)( + % Lower boundary + exists(o in OBJECTS, s in dom(kind[o]))( + kind[o] = s + /\ exists(r in shape[s])( + x[o,j] + rect_offset[r,j] == l[j] + ) + ) + /\ % Upper boundary + exists(o in OBJECTS, s in dom(kind[o]))( + kind[o] = s + /\ exists(r in shape[s])( + x[o,j] + rect_offset[r,j] + rect_size[r,j] == u[j] + ) + ) + ) + ); diff --git a/share/minizinc/std/fzn_geost_smallest_bb_reif.mzn b/share/minizinc/std/fzn_geost_smallest_bb_reif.mzn new file mode 100644 index 0000000..f48a84f --- /dev/null +++ b/share/minizinc/std/fzn_geost_smallest_bb_reif.mzn @@ -0,0 +1,39 @@ +include "fzn_geost_bb.mzn"; +include "fzn_geost_bb_reif.mzn"; + +predicate fzn_geost_smallest_bb_reif( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + array[int ] of var int : l , + array[int ] of var int : u , + var bool: b +) = + % Two useful definitions + let { + set of int: DIMS = 1..k; + set of int: OBJECTS = index_set(kind); + } in b <-> ( + % Posting the geost constraint + fzn_geost_bb(k, rect_size, rect_offset, shape, x, kind, l, u) + /\ % Posting the smallest bounding box constraints + forall(j in DIMS)( + % Lower boundary + exists(o in OBJECTS, s in dom(kind[o]))( + kind[o] = s + /\ exists(r in shape[s])( + x[o,j] + rect_offset[r,j] == l[j] + ) + ) + /\ % Upper boundary + exists(o in OBJECTS, s in dom(kind[o]))( + kind[o] = s + /\ exists(r in shape[s])( + x[o,j] + rect_offset[r,j] + rect_size[r,j] == u[j] + ) + ) + ) + ); diff --git a/share/minizinc/std/fzn_global_cardinality.mzn b/share/minizinc/std/fzn_global_cardinality.mzn new file mode 100644 index 0000000..955d817 --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality.mzn @@ -0,0 +1,9 @@ +include "count.mzn"; + +predicate fzn_global_cardinality(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts) = + forall(i in index_set(cover))( count(x, cover[i], counts[i]) ) + /\ + % Implied constraint + length(x) >= sum(counts); diff --git a/share/minizinc/std/fzn_global_cardinality_closed.mzn b/share/minizinc/std/fzn_global_cardinality_closed.mzn new file mode 100644 index 0000000..48ffbd9 --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_closed.mzn @@ -0,0 +1,8 @@ +include "global_cardinality.mzn"; + +predicate fzn_global_cardinality_closed(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts) = + forall(i in index_set(x))( x[i] in { d | d in cover } ) + /\ + global_cardinality(x, cover, counts); diff --git a/share/minizinc/std/fzn_global_cardinality_closed_reif.mzn b/share/minizinc/std/fzn_global_cardinality_closed_reif.mzn new file mode 100644 index 0000000..7b301ff --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_closed_reif.mzn @@ -0,0 +1,11 @@ +include "global_cardinality.mzn"; + +predicate fzn_global_cardinality_closed_reif(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts, + var bool: b) = + b <-> ( + forall(i in index_set(x))( x[i] in { d | d in cover } ) + /\ + global_cardinality(x, cover, counts) + ); diff --git a/share/minizinc/std/fzn_global_cardinality_low_up.mzn b/share/minizinc/std/fzn_global_cardinality_low_up.mzn new file mode 100644 index 0000000..e670f00 --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_low_up.mzn @@ -0,0 +1,8 @@ +predicate fzn_global_cardinality_low_up(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound) = + forall(i in index_set(cover)) ( + sum(j in index_set(x)) ( bool2int(x[j] = cover[i]) ) + in lbound[i]..ubound[i] + ); diff --git a/share/minizinc/std/fzn_global_cardinality_low_up_closed.mzn b/share/minizinc/std/fzn_global_cardinality_low_up_closed.mzn new file mode 100644 index 0000000..2a3d5ac --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_low_up_closed.mzn @@ -0,0 +1,13 @@ +predicate fzn_global_cardinality_low_up_closed(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound) = + forall(i in index_set(x))( x[i] in { d | d in array2set(cover) } ) + /\ + global_cardinality_low_up(x, cover, lbound, ubound) + /\ + % Implied condition + length(x) in sum(lbound)..sum(ubound); + + +include "global_cardinality_low_up.mzn"; diff --git a/share/minizinc/std/fzn_global_cardinality_low_up_closed_reif.mzn b/share/minizinc/std/fzn_global_cardinality_low_up_closed_reif.mzn new file mode 100644 index 0000000..456d6e9 --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_low_up_closed_reif.mzn @@ -0,0 +1,14 @@ +predicate fzn_global_cardinality_low_up_closed_reif(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound, + var bool: b) = b <-> ( + forall(i in index_set(x))( x[i] in { d | d in array2set(cover) } ) + /\ + global_cardinality_low_up(x, cover, lbound, ubound) + /\ + % Implied condition + length(x) in sum(lbound)..sum(ubound)); + + +include "global_cardinality_low_up.mzn"; diff --git a/share/minizinc/std/fzn_global_cardinality_low_up_reif.mzn b/share/minizinc/std/fzn_global_cardinality_low_up_reif.mzn new file mode 100644 index 0000000..0ade36a --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_low_up_reif.mzn @@ -0,0 +1,10 @@ +predicate fzn_global_cardinality_low_up_reif(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound, + var bool: b) = + b <-> + forall(i in index_set(cover)) ( + sum(j in index_set(x)) ( bool2int(x[j] = cover[i]) ) + in lbound[i]..ubound[i] + ); diff --git a/share/minizinc/std/fzn_global_cardinality_reif.mzn b/share/minizinc/std/fzn_global_cardinality_reif.mzn new file mode 100644 index 0000000..5ddb061 --- /dev/null +++ b/share/minizinc/std/fzn_global_cardinality_reif.mzn @@ -0,0 +1,12 @@ +include "count.mzn"; + +predicate fzn_global_cardinality_reif(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts, + var bool: b) = + b <-> ( + forall(i in index_set(cover))( count(x, cover[i], counts[i]) ) + /\ + % Implied constraint + length(x) >= sum(counts) + ); diff --git a/share/minizinc/std/fzn_if_then_else_bool.mzn b/share/minizinc/std/fzn_if_then_else_bool.mzn new file mode 100644 index 0000000..87bfc73 --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_bool.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_bool(array[int] of var bool: c, array[int] of bool: x, var bool: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_float.mzn b/share/minizinc/std/fzn_if_then_else_float.mzn new file mode 100644 index 0000000..d53f73a --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_float.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_float(array[int] of var bool: c, array[int] of float: x, var float: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_int.mzn b/share/minizinc/std/fzn_if_then_else_int.mzn new file mode 100644 index 0000000..8a545bb --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_int.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_int(array[int] of var bool: c, array[int] of int: x, var int: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_opt_bool.mzn b/share/minizinc/std/fzn_if_then_else_opt_bool.mzn new file mode 100644 index 0000000..42a1f31 --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_opt_bool.mzn @@ -0,0 +1,6 @@ +include "fzn_if_then_else_bool.mzn"; +include "fzn_if_then_else_var_bool.mzn"; + +predicate fzn_if_then_else_opt_bool(array[int] of var bool: c, array[int] of opt bool: x, var opt bool: y) = + fzn_if_then_else_bool(array1d(c),[deopt(x[i]) | i in index_set(x)],deopt(y)) /\ + fzn_if_then_else_var_bool(array1d(c),[occurs(x[i]) | i in index_set(x)],occurs(y)); diff --git a/share/minizinc/std/fzn_if_then_else_opt_int.mzn b/share/minizinc/std/fzn_if_then_else_opt_int.mzn new file mode 100644 index 0000000..096ebdf --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_opt_int.mzn @@ -0,0 +1,6 @@ +include "fzn_if_then_else_int.mzn"; +include "fzn_if_then_else_var_bool.mzn"; + +predicate fzn_if_then_else_opt_int(array[int] of var bool: c, array[int] of opt int: x, var opt int: y) = + fzn_if_then_else_int(array1d(c),[deopt(x[i]) | i in index_set(x)],deopt(y)) /\ + fzn_if_then_else_var_bool(array1d(c),[occurs(x[i]) | i in index_set(x)],occurs(y)); diff --git a/share/minizinc/std/fzn_if_then_else_partiality.mzn b/share/minizinc/std/fzn_if_then_else_partiality.mzn new file mode 100644 index 0000000..f63041a --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_partiality.mzn @@ -0,0 +1,11 @@ +predicate fzn_if_then_else_partiality(array[int] of var bool: c, array[int] of var bool: def, var bool: b) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall(i in index_set(c)) + ( (b /\ c[i] /\ d[i] -> def[i])) /\ + forall (i in index_set(c)) (d[i] /\ c[i] -> b); diff --git a/share/minizinc/std/fzn_if_then_else_set.mzn b/share/minizinc/std/fzn_if_then_else_set.mzn new file mode 100644 index 0000000..bb0ea6b --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_set.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_set(array[int] of var bool: c, array[int] of set of int: x, var set of int: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_var_bool.mzn b/share/minizinc/std/fzn_if_then_else_var_bool.mzn new file mode 100644 index 0000000..94a0d37 --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_var_bool.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_var_bool(array[int] of var bool: c, array[int] of var bool: x, var bool: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_var_float.mzn b/share/minizinc/std/fzn_if_then_else_var_float.mzn new file mode 100644 index 0000000..5cfdb1f --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_var_float.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_var_float(array[int] of var bool: c, array[int] of var float: x, var float: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_var_int.mzn b/share/minizinc/std/fzn_if_then_else_var_int.mzn new file mode 100644 index 0000000..3bd77d2 --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_var_int.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_var_int(array[int] of var bool: c, array[int] of var int: x, var int: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_if_then_else_var_opt_bool.mzn b/share/minizinc/std/fzn_if_then_else_var_opt_bool.mzn new file mode 100644 index 0000000..9308c4a --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_var_opt_bool.mzn @@ -0,0 +1,5 @@ +include "fzn_if_then_else_var_bool.mzn"; + +predicate fzn_if_then_else_var_opt_bool(array[int] of var bool: c, array[int] of var opt bool: x, var opt bool: y) = + fzn_if_then_else_var_bool(array1d(c),[deopt(x[i]) | i in index_set(x)],deopt(y)) /\ + fzn_if_then_else_var_bool(array1d(c),[occurs(x[i]) | i in index_set(x)],occurs(y)); diff --git a/share/minizinc/std/fzn_if_then_else_var_opt_int.mzn b/share/minizinc/std/fzn_if_then_else_var_opt_int.mzn new file mode 100644 index 0000000..5db6c29 --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_var_opt_int.mzn @@ -0,0 +1,6 @@ +include "fzn_if_then_else_var_bool.mzn"; +include "fzn_if_then_else_var_int.mzn"; + +predicate fzn_if_then_else_var_opt_int(array[int] of var bool: c, array[int] of var opt int: x, var opt int: y) = + fzn_if_then_else_var_int(array1d(c),[deopt(x[i]) | i in index_set(x)],deopt(y)) /\ + fzn_if_then_else_var_bool(array1d(c),[occurs(x[i]) | i in index_set(x)],occurs(y)); diff --git a/share/minizinc/std/fzn_if_then_else_var_set.mzn b/share/minizinc/std/fzn_if_then_else_var_set.mzn new file mode 100644 index 0000000..3ba17f3 --- /dev/null +++ b/share/minizinc/std/fzn_if_then_else_var_set.mzn @@ -0,0 +1,9 @@ +predicate fzn_if_then_else_var_set(array[int] of var bool: c, array[int] of var set of int: x, var set of int: y) = + let { array[index_set(c)] of var bool: d; } in + forall(i in index_set(c)) + (if i > min(index_set(c)) then + d[i] = (not c[i-1] /\ d[i-1]) + else + d[i] = true + endif) /\ + forall (i in index_set(c)) (c[i] /\ d[i] -> y=x[i]); diff --git a/share/minizinc/std/fzn_increasing_bool.mzn b/share/minizinc/std/fzn_increasing_bool.mzn new file mode 100644 index 0000000..0378b24 --- /dev/null +++ b/share/minizinc/std/fzn_increasing_bool.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_bool(array[int] of var bool: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_bool_reif.mzn b/share/minizinc/std/fzn_increasing_bool_reif.mzn new file mode 100644 index 0000000..76d344b --- /dev/null +++ b/share/minizinc/std/fzn_increasing_bool_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_bool_reif(array[int] of var bool: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_float.mzn b/share/minizinc/std/fzn_increasing_float.mzn new file mode 100644 index 0000000..eb3dd45 --- /dev/null +++ b/share/minizinc/std/fzn_increasing_float.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_float(array[int] of var float: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_float_reif.mzn b/share/minizinc/std/fzn_increasing_float_reif.mzn new file mode 100644 index 0000000..98204d3 --- /dev/null +++ b/share/minizinc/std/fzn_increasing_float_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_float_reif(array[int] of var float: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_int.mzn b/share/minizinc/std/fzn_increasing_int.mzn new file mode 100644 index 0000000..f83cf15 --- /dev/null +++ b/share/minizinc/std/fzn_increasing_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_int(array[int] of var int: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_int_reif.mzn b/share/minizinc/std/fzn_increasing_int_reif.mzn new file mode 100644 index 0000000..bc4ebda --- /dev/null +++ b/share/minizinc/std/fzn_increasing_int_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_int_reif(array[int] of var int: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_set.mzn b/share/minizinc/std/fzn_increasing_set.mzn new file mode 100644 index 0000000..17c8447 --- /dev/null +++ b/share/minizinc/std/fzn_increasing_set.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_set(array[int] of var set of int: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_increasing_set_reif.mzn b/share/minizinc/std/fzn_increasing_set_reif.mzn new file mode 100644 index 0000000..f94ce37 --- /dev/null +++ b/share/minizinc/std/fzn_increasing_set_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate fzn_increasing_set_reif(array[int] of var set of int: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] <= x[i]); diff --git a/share/minizinc/std/fzn_int_set_channel.mzn b/share/minizinc/std/fzn_int_set_channel.mzn new file mode 100644 index 0000000..8a9ab04 --- /dev/null +++ b/share/minizinc/std/fzn_int_set_channel.mzn @@ -0,0 +1,7 @@ +predicate fzn_int_set_channel(array[int] of var int: x, + array[int] of var set of int: y) = + forall(i in index_set(x)) (x[i] in index_set(y)) /\ + forall(j in index_set(y)) (y[j] subset index_set(x)) /\ + forall(i in index_set(x), j in index_set(y)) (x[i] = j <-> i in y[j]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_int_set_channel_reif.mzn b/share/minizinc/std/fzn_int_set_channel_reif.mzn new file mode 100644 index 0000000..cb96f49 --- /dev/null +++ b/share/minizinc/std/fzn_int_set_channel_reif.mzn @@ -0,0 +1,10 @@ +predicate fzn_int_set_channel_reif(array[int] of var int: x, + array[int] of var set of int: y, + var bool: b) = + b <-> ( + forall(i in index_set(x)) (x[i] in index_set(y)) /\ + forall(j in index_set(y)) (y[j] subset index_set(x)) /\ + forall(i in index_set(x), j in index_set(y)) (x[i] = j <-> i in y[j]) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_inverse.mzn b/share/minizinc/std/fzn_inverse.mzn new file mode 100644 index 0000000..8fc8993 --- /dev/null +++ b/share/minizinc/std/fzn_inverse.mzn @@ -0,0 +1,7 @@ +predicate fzn_inverse(array[int] of var int: f, + array[int] of var int: invf) = + forall(i in index_set(f), j in index_set(invf)) ( + f[i] in index_set(invf) /\ + invf[j] in index_set(f) /\ + (j == f[i] <-> i == invf[j]) + ); diff --git a/share/minizinc/std/fzn_inverse_reif.mzn b/share/minizinc/std/fzn_inverse_reif.mzn new file mode 100644 index 0000000..6ad84bf --- /dev/null +++ b/share/minizinc/std/fzn_inverse_reif.mzn @@ -0,0 +1,8 @@ +predicate fzn_inverse_reif(array[int] of var int: f, + array[int] of var int: invf, + var bool: b) = + b <-> forall(i in index_set(f), j in index_set(invf)) ( + f[i] in index_set(invf) /\ + invf[j] in index_set(f) /\ + (j == f[i] <-> i == invf[j]) + ); diff --git a/share/minizinc/std/fzn_inverse_set.mzn b/share/minizinc/std/fzn_inverse_set.mzn new file mode 100644 index 0000000..62a35d0 --- /dev/null +++ b/share/minizinc/std/fzn_inverse_set.mzn @@ -0,0 +1,11 @@ +predicate fzn_inverse_set(array[int] of var set of int: f, + array[int] of var set of int: invf) = + forall(i in index_set(f)) ( + f[i] subset index_set(invf) + ) /\ + forall(j in index_set(invf)) ( + invf[j] subset index_set(f) + ) /\ + forall(i in index_set(f), j in index_set(invf)) ( + (j in f[i] <-> i in invf[j]) + ); diff --git a/share/minizinc/std/fzn_inverse_set_reif.mzn b/share/minizinc/std/fzn_inverse_set_reif.mzn new file mode 100644 index 0000000..51022b0 --- /dev/null +++ b/share/minizinc/std/fzn_inverse_set_reif.mzn @@ -0,0 +1,14 @@ +predicate fzn_inverse_set_reif(array[int] of var set of int: f, + array[int] of var set of int: invf, + var bool: b) = + b <-> ( + forall(i in index_set(f)) ( + f[i] subset index_set(invf) + ) /\ + forall(j in index_set(invf)) ( + invf[j] subset index_set(f) + ) /\ + forall(i in index_set(f), j in index_set(invf)) ( + (j in f[i] <-> i in invf[j]) + ) + ); diff --git a/share/minizinc/std/fzn_knapsack.mzn b/share/minizinc/std/fzn_knapsack.mzn new file mode 100644 index 0000000..32f5d1c --- /dev/null +++ b/share/minizinc/std/fzn_knapsack.mzn @@ -0,0 +1,6 @@ +predicate fzn_knapsack(array[int] of int: w, array[int] of int:p, + array[int] of var int:x, var int: W, var int: P) = + forall (i in index_set(x)) (x[i] >= 0) /\ + W >= 0 /\ P >= 0 /\ + P = sum(i in index_set(p)) (x[i]*p[i]) /\ + W = sum(i in index_set(w)) (x[i]*w[i]); diff --git a/share/minizinc/std/fzn_knapsack_reif.mzn b/share/minizinc/std/fzn_knapsack_reif.mzn new file mode 100644 index 0000000..e699354 --- /dev/null +++ b/share/minizinc/std/fzn_knapsack_reif.mzn @@ -0,0 +1,8 @@ +predicate fzn_knapsack_reif(array[int] of int: w, array[int] of int:p, + array[int] of var int:x, var int: W, var int: P, var bool: b) = + b <-> ( + forall (i in index_set(x)) (x[i] >= 0) /\ + W >= 0 /\ P >= 0 /\ + P = sum(i in index_set(p)) (x[i]*p[i]) /\ + W = sum(i in index_set(w)) (x[i]*w[i]) + ); diff --git a/share/minizinc/std/fzn_lex2.mzn b/share/minizinc/std/fzn_lex2.mzn new file mode 100644 index 0000000..c9319eb --- /dev/null +++ b/share/minizinc/std/fzn_lex2.mzn @@ -0,0 +1,21 @@ +include "lex_lesseq.mzn"; + +predicate fzn_lex2(array[int, int] of var int: x) = + let { + int: lbx1 = min(index_set_1of2(x)), + int: ubx1 = max(index_set_1of2(x)), + int: lbx2 = min(index_set_2of2(x)), + int: ubx2 = max(index_set_2of2(x)) + } in ( + forall(i in lbx1 + 1 .. ubx1) ( + lex_lesseq([x[i - 1, j] | j in index_set_2of2(x)], + [x[i, j] | j in index_set_2of2(x)] + ) + ) + /\ + forall(j in lbx2 + 1 .. ubx2) ( + lex_lesseq([x[i, j - 1] | i in index_set_1of2(x)], + [x[i, j ] | i in index_set_1of2(x)] + ) + ) + ); diff --git a/share/minizinc/std/fzn_lex2_reif.mzn b/share/minizinc/std/fzn_lex2_reif.mzn new file mode 100644 index 0000000..784f054 --- /dev/null +++ b/share/minizinc/std/fzn_lex2_reif.mzn @@ -0,0 +1,21 @@ +include "lex_lesseq.mzn"; + +predicate fzn_lex2_reif(array[int, int] of var int: x, var bool: b) = + let { + int: lbx1 = min(index_set_1of2(x)), + int: ubx1 = max(index_set_1of2(x)), + int: lbx2 = min(index_set_2of2(x)), + int: ubx2 = max(index_set_2of2(x)) + } in b <-> ( + forall(i in lbx1 + 1 .. ubx1) ( + lex_lesseq([x[i - 1, j] | j in index_set_2of2(x)], + [x[i, j] | j in index_set_2of2(x)] + ) + ) + /\ + forall(j in lbx2 + 1 .. ubx2) ( + lex_lesseq([x[i, j - 1] | i in index_set_1of2(x)], + [x[i, j ] | i in index_set_1of2(x)] + ) + ) + ); diff --git a/share/minizinc/std/fzn_lex_less_bool.mzn b/share/minizinc/std/fzn_lex_less_bool.mzn new file mode 100644 index 0000000..3bbdee5 --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_bool.mzn @@ -0,0 +1,25 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_bool(array[int] of var bool: x, + array[int] of var bool: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_bool_reif.mzn b/share/minizinc/std/fzn_lex_less_bool_reif.mzn new file mode 100644 index 0000000..e354b86 --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_bool_reif.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_bool_reif(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_float.mzn b/share/minizinc/std/fzn_lex_less_float.mzn new file mode 100644 index 0000000..cf56cfe --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_float.mzn @@ -0,0 +1,25 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_float(array[int] of var float: x, + array[int] of var float: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_float_reif.mzn b/share/minizinc/std/fzn_lex_less_float_reif.mzn new file mode 100644 index 0000000..fc477ac --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_float_reif.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_float_reif(array[int] of var float: x, + array[int] of var float: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_int.mzn b/share/minizinc/std/fzn_lex_less_int.mzn new file mode 100644 index 0000000..08424f9 --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_int.mzn @@ -0,0 +1,25 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_int(array[int] of var int: x, + array[int] of var int: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_int_reif.mzn b/share/minizinc/std/fzn_lex_less_int_reif.mzn new file mode 100644 index 0000000..2d9a4a9 --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_int_reif.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_int_reif(array[int] of var int: x, + array[int] of var int: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_set.mzn b/share/minizinc/std/fzn_lex_less_set.mzn new file mode 100644 index 0000000..27665b8 --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_set.mzn @@ -0,0 +1,25 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_set(array[int] of var set of int: x, + array[int] of var set of int: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_less_set_reif.mzn b/share/minizinc/std/fzn_lex_less_set_reif.mzn new file mode 100644 index 0000000..4385964 --- /dev/null +++ b/share/minizinc/std/fzn_lex_less_set_reif.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_less_set_reif(array[int] of var set of int: x, + array[int] of var set of int: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size+1] of var bool: b } + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + (x[lx + i] < y[ly + i] \/ b[i+1]) ) + ) + /\ + b[size + 1] = (ux - lx < uy - ly); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_bool.mzn b/share/minizinc/std/fzn_lex_lesseq_bool.mzn new file mode 100644 index 0000000..ca6ee0a --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_bool.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_bool(array[int] of var bool: x, + array[int] of var bool: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_bool_reif.mzn b/share/minizinc/std/fzn_lex_lesseq_bool_reif.mzn new file mode 100644 index 0000000..06c55d4 --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_bool_reif.mzn @@ -0,0 +1,27 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_bool_reif(array[int] of var bool: x, + array[int] of var bool: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_float.mzn b/share/minizinc/std/fzn_lex_lesseq_float.mzn new file mode 100644 index 0000000..1f968c4 --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_float.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_float(array[int] of var float: x, + array[int] of var float: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_float_reif.mzn b/share/minizinc/std/fzn_lex_lesseq_float_reif.mzn new file mode 100644 index 0000000..9888ff2 --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_float_reif.mzn @@ -0,0 +1,27 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_float_reif(array[int] of var float: x, + array[int] of var float: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_int.mzn b/share/minizinc/std/fzn_lex_lesseq_int.mzn new file mode 100644 index 0000000..8b8f869 --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_int.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_int(array[int] of var int: x, + array[int] of var int: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_int_reif.mzn b/share/minizinc/std/fzn_lex_lesseq_int_reif.mzn new file mode 100644 index 0000000..6836d85 --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_int_reif.mzn @@ -0,0 +1,28 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_int_reif(array[int] of var int: x, + array[int] of var int: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_set.mzn b/share/minizinc/std/fzn_lex_lesseq_set.mzn new file mode 100644 index 0000000..79e07ea --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_set.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_set(array[int] of var set of int: x, + array[int] of var set of int: y) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + b[0] + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_lex_lesseq_set_reif.mzn b/share/minizinc/std/fzn_lex_lesseq_set_reif.mzn new file mode 100644 index 0000000..8a80efd --- /dev/null +++ b/share/minizinc/std/fzn_lex_lesseq_set_reif.mzn @@ -0,0 +1,27 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate fzn_lex_lesseq_set_reif(array[int] of var set of int: x, + array[int] of var set of int: y, + var bool: c) = + let { int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + int: size = max(ux - lx, uy - ly), + array[0..size] of var bool: b } + % b[i] is true if the lexicographical order holds from position i on. + in + (c <-> b[0]) + /\ + forall(i in 0..size) ( + b[i] = ( x[lx + i] <= y[ly + i] + /\ + if i = size then true + else x[lx + i] < y[ly + i] \/ b[i+1] endif + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_link_set_to_booleans.mzn b/share/minizinc/std/fzn_link_set_to_booleans.mzn new file mode 100644 index 0000000..9f21825 --- /dev/null +++ b/share/minizinc/std/fzn_link_set_to_booleans.mzn @@ -0,0 +1,2 @@ +predicate fzn_link_set_to_booleans(var set of int: s, array[int] of var bool: b) = + forall(i in index_set(b)) ( b[i] <-> i in s ); diff --git a/share/minizinc/std/fzn_link_set_to_booleans_reif.mzn b/share/minizinc/std/fzn_link_set_to_booleans_reif.mzn new file mode 100644 index 0000000..f0a3a0e --- /dev/null +++ b/share/minizinc/std/fzn_link_set_to_booleans_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_link_set_to_booleans_reif(var set of int: s, array[int] of var bool: b, var bool: bb) = + bb <-> forall(i in index_set(b)) ( b[i] <-> i in s ); diff --git a/share/minizinc/std/fzn_mdd.mzn b/share/minizinc/std/fzn_mdd.mzn new file mode 100644 index 0000000..d805582 --- /dev/null +++ b/share/minizinc/std/fzn_mdd.mzn @@ -0,0 +1,30 @@ +predicate fzn_mdd(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % value of variable + array[int] of int: to % edge entering node 0..N where 0 = T node + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of var bool: bn; + array[EDGE] of var bool: be; + set of int: D = dom_array(x); } in + bn[0] /\ % true node is true + bn[1] /\ % root must hold + % T1 each node except the root enforces an outgoing edge + forall(n in NODE)(bn[n] -> exists(e in EDGE where from[e] = n)(be[e])) /\ + % T23 each edge enforces its endpoints + forall(e in EDGE)((be[e] -> bn[from[e]]) /\ (be[e] -> bn[to[e]])) /\ + % T4 each edge enforces its label + forall(e in EDGE)(be[e] -> x[level[from[e]]] in label[e]) /\ + % P1 each node enforces its outgoing edges + forall(e in EDGE)(bn[from[e]] /\ x[level[from[e]]] in label[e] -> be[e]) /\ + % P2 each node except the root enforces an incoming edge + exists(e in EDGE where to[e] = 0)(be[e]) /\ + forall(n in 2..N)(bn[n] -> exists(e in EDGE where to[e] = n)(be[e])) /\ + % P3 each label has a support + forall(i in 1..L, d in D) + (x[i] = d -> exists(e in EDGE where level[from[e]] = i /\ d in label[e])(be[e])); diff --git a/share/minizinc/std/fzn_mdd_nondet.mzn b/share/minizinc/std/fzn_mdd_nondet.mzn new file mode 100644 index 0000000..98a3068 --- /dev/null +++ b/share/minizinc/std/fzn_mdd_nondet.mzn @@ -0,0 +1,28 @@ +predicate fzn_mdd_nondet(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % value of variable + array[int] of int: to % edge entering node 0..N where 0 = T node + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of var bool: bn; + array[EDGE] of var bool: be; + set of int: D = dom_array(x); } in + bn[0] /\ % true node is true + bn[1] /\ % root must hold + % T1 each node except the root enforces an outgoing edge + forall(n in NODE)(bn[n] -> exists(e in EDGE where from[e] = n)(be[e])) /\ + % T23 each edge enforces its endpoints + forall(e in EDGE)((be[e] -> bn[from[e]]) /\ (be[e] -> bn[to[e]])) /\ + % T4 each edge enforces its label + forall(e in EDGE)(be[e] -> x[level[from[e]]] in label[e]) /\ + % P2 each node except the root enforces an incoming edge + exists(e in EDGE where to[e] = 0)(be[e]) /\ + forall(n in 2..N)(bn[n] -> exists(e in EDGE where to[e] = n)(be[e])) /\ + % P3 each label has a support + forall(i in 1..L, d in D) + (x[i] = d -> exists(e in EDGE where level[from[e]] = i /\ d in label[e])(be[e])); diff --git a/share/minizinc/std/fzn_mdd_nondet_reif.mzn b/share/minizinc/std/fzn_mdd_nondet_reif.mzn new file mode 100644 index 0000000..3a2eb37 --- /dev/null +++ b/share/minizinc/std/fzn_mdd_nondet_reif.mzn @@ -0,0 +1,29 @@ +predicate fzn_mdd_nondet_reif(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % value of variable + array[int] of int: to, % edge entering node 0..N where 0 = T node + var bool: b % reification value + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of var bool: bn; + array[EDGE] of var bool: be; + set of int: D = dom_array(x); } in + bn[0] /\ % true node is true + (b <-> bn[1]) /\ % root gives truth value + % T1 each node except the root enforces an outgoing edge + forall(n in NODE)(bn[n] -> exists(e in EDGE where from[e] = n)(be[e])) /\ + % T23 each edge enforces its endpoints + forall(e in EDGE)((be[e] -> bn[from[e]]) /\ (be[e] -> bn[to[e]])) /\ + % T4 each edge enforces its label + forall(e in EDGE)(be[e] -> x[level[from[e]]] in label[e]) /\ + % P2 each node except the root enforces an incoming edge + exists(e in EDGE where to[e] = 0)(be[e]) /\ + forall(n in 2..N)(bn[n] -> exists(e in EDGE where to[e] = n)(be[e])) /\ + % P3 each label has a support + forall(i in 1..L, d in D) + (x[i] = d -> exists(e in EDGE where level[from[e]] = i /\ d in label[e])(be[e])); diff --git a/share/minizinc/std/fzn_mdd_reif.mzn b/share/minizinc/std/fzn_mdd_reif.mzn new file mode 100644 index 0000000..2d6ad0b --- /dev/null +++ b/share/minizinc/std/fzn_mdd_reif.mzn @@ -0,0 +1,31 @@ +predicate fzn_mdd_reif(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % value of variable + array[int] of int: to, % edge entering node 0..N where 0 = T node + var bool: b % reification value + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of var bool: bn; + array[EDGE] of var bool: be; + set of int: D = dom_array(x); } in + bn[0] /\ % true node is true + (b <-> bn[1]) /\ % root gives truth value + % T1 each node except the root enforces an outgoing edge + forall(n in NODE)(bn[n] -> exists(e in EDGE where from[e] = n)(be[e])) /\ + % T23 each edge enforces its endpoints + forall(e in EDGE)((be[e] -> bn[from[e]]) /\ (be[e] -> bn[to[e]])) /\ + % T4 each edge enforces its label + forall(e in EDGE)(be[e] -> x[level[from[e]]] in label[e]) /\ + % P1 each node enforces its outgoing edges + forall(e in EDGE)(bn[from[e]] /\ x[level[from[e]]] in label[e] -> be[e]) /\ + % P2 each node except the root enforces an incoming edge + exists(e in EDGE where to[e] = 0)(be[e]) /\ + forall(n in 2..N)(bn[n] -> exists(e in EDGE where to[e] = n)(be[e])) /\ + % P3 each label has a support + forall(i in 1..L, d in D) + (x[i] = d -> exists(e in EDGE where level[from[e]] = i /\ d in label[e])(be[e])); diff --git a/share/minizinc/std/fzn_member_bool.mzn b/share/minizinc/std/fzn_member_bool.mzn new file mode 100644 index 0000000..6d4e1ae --- /dev/null +++ b/share/minizinc/std/fzn_member_bool.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_bool(array[int] of var bool: x, var bool: y) = + exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_bool_reif.mzn b/share/minizinc/std/fzn_member_bool_reif.mzn new file mode 100644 index 0000000..d435525 --- /dev/null +++ b/share/minizinc/std/fzn_member_bool_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_bool_reif(array[int] of var bool: x, var bool: y, var bool: b) = + b <-> exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_float.mzn b/share/minizinc/std/fzn_member_float.mzn new file mode 100644 index 0000000..01f1641 --- /dev/null +++ b/share/minizinc/std/fzn_member_float.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_float(array[int] of var float: x, var float: y) = + exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_float_reif.mzn b/share/minizinc/std/fzn_member_float_reif.mzn new file mode 100644 index 0000000..fea3946 --- /dev/null +++ b/share/minizinc/std/fzn_member_float_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_float_reif(array[int] of var float: x, var float: y, var bool: b) = + b <-> exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_int.mzn b/share/minizinc/std/fzn_member_int.mzn new file mode 100644 index 0000000..0d97bba --- /dev/null +++ b/share/minizinc/std/fzn_member_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_int(array[int] of var int: x, var int: y) = + exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_int_reif.mzn b/share/minizinc/std/fzn_member_int_reif.mzn new file mode 100644 index 0000000..764a475 --- /dev/null +++ b/share/minizinc/std/fzn_member_int_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_int_reif(array[int] of var int: x, var int: y, var bool: b) = + b <-> exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_set.mzn b/share/minizinc/std/fzn_member_set.mzn new file mode 100644 index 0000000..58d054d --- /dev/null +++ b/share/minizinc/std/fzn_member_set.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array of set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_set(array[int] of var set of int: x, var set of int: y) = + exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_member_set_reif.mzn b/share/minizinc/std/fzn_member_set_reif.mzn new file mode 100644 index 0000000..77f42a4 --- /dev/null +++ b/share/minizinc/std/fzn_member_set_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array of set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_member_set_reif(array[int] of var set of int: x, var set of int: y, var bool: b) = + b <-> exists(i in index_set(x)) ( x[i] == y ); diff --git a/share/minizinc/std/fzn_network_flow.mzn b/share/minizinc/std/fzn_network_flow.mzn new file mode 100644 index 0000000..87afe36 --- /dev/null +++ b/share/minizinc/std/fzn_network_flow.mzn @@ -0,0 +1,14 @@ +predicate fzn_network_flow(array[int,1..2] of int: arc, + array[int] of int: balance, + array[int] of var int: flow) = + + let { int: source_node = 1; + int: sink_node = 2; + set of int: ARCS = index_set_1of2(arc); + set of int: NODES = index_set(balance); + } in + forall (i in NODES) ( + sum (j in ARCS where i == arc[j,source_node]) (flow[j]) - + sum (j in ARCS where i == arc[j,sink_node]) (flow[j]) + = balance[i] + ); diff --git a/share/minizinc/std/fzn_network_flow_cost.mzn b/share/minizinc/std/fzn_network_flow_cost.mzn new file mode 100644 index 0000000..d2cb530 --- /dev/null +++ b/share/minizinc/std/fzn_network_flow_cost.mzn @@ -0,0 +1,17 @@ +predicate fzn_network_flow_cost(array[int,1..2] of int: arc, + array[int] of int: balance, + array[int] of int: weight, + array[int] of var int: flow, var int: cost) = + + let { int: source_node = 1; + int: sink_node = 2; + set of int: ARCS = index_set_1of2(arc); + set of int: NODES = index_set(balance); + } in + cost = sum(i in ARCS) (flow[i] * weight[i]) + /\ + forall (i in NODES) ( + sum (j in ARCS where i == arc[j,source_node]) (flow[j]) - + sum (j in ARCS where i == arc[j,sink_node]) (flow[j]) + = balance[i] + ); diff --git a/share/minizinc/std/fzn_network_flow_cost_reif.mzn b/share/minizinc/std/fzn_network_flow_cost_reif.mzn new file mode 100644 index 0000000..5359540 --- /dev/null +++ b/share/minizinc/std/fzn_network_flow_cost_reif.mzn @@ -0,0 +1,19 @@ +predicate fzn_network_flow_cost_reif(array[int,1..2] of int: arc, + array[int] of int: balance, + array[int] of int: weight, + array[int] of var int: flow, var int: cost, + var bool: b) = + + let { int: source_node = 1; + int: sink_node = 2; + set of int: ARCS = index_set_1of2(arc); + set of int: NODES = index_set(balance); + } in b <-> ( + cost = sum(i in ARCS) (flow[i] * weight[i]) + /\ + forall (i in NODES) ( + sum (j in ARCS where i == arc[j,source_node]) (flow[j]) - + sum (j in ARCS where i == arc[j,sink_node]) (flow[j]) + = balance[i] + ) + ); diff --git a/share/minizinc/std/fzn_network_flow_reif.mzn b/share/minizinc/std/fzn_network_flow_reif.mzn new file mode 100644 index 0000000..381fd0d --- /dev/null +++ b/share/minizinc/std/fzn_network_flow_reif.mzn @@ -0,0 +1,15 @@ +predicate fzn_network_flow_reif(array[int,1..2] of int: arc, + array[int] of int: balance, + array[int] of var int: flow, + var bool: b) = + + let { int: source_node = 1; + int: sink_node = 2; + set of int: ARCS = index_set_1of2(arc); + set of int: NODES = index_set(balance); + } in + b <-> forall (i in NODES) ( + sum (j in ARCS where i == arc[j,source_node]) (flow[j]) - + sum (j in ARCS where i == arc[j,sink_node]) (flow[j]) + = balance[i] + ); diff --git a/share/minizinc/std/fzn_neural_net.mzn b/share/minizinc/std/fzn_neural_net.mzn new file mode 100644 index 0000000..7ca6b53 --- /dev/null +++ b/share/minizinc/std/fzn_neural_net.mzn @@ -0,0 +1,50 @@ +predicate fzn_neural_net(array[int] of var float: inputs, + array[int] of int: input_ids, + array[int] of var float: outputs, + array[int] of int: output_ids, + array[int] of float: bias, + array[int] of float: edge_weight, + array[int] of int: edge_parent, + array[int] of int: first_edge, + NEURON_TYPE: neuron_type) = + let { set of int: NODE = index_set(bias) } in + let { set of int: INPUTS = array2set(input_ids) } in + let { set of int: EDGE = index_set(edge_weight) } in + let { array[NODE] of var float: neuron } in + forall(i in index_set(inputs))(neuron[input_ids[i]] = inputs[i]) /\ + forall(i in index_set(outputs))(neuron[output_ids[i]] = outputs[i]) /\ + forall(i in NODE diff INPUTS) ( + let { int: first = first_edge[i]; + int: last = if i = max(NODE) then max(index_set(first_edge)) + else first_edge[i+1] - 1 endif; + array[int] of var float: ins = [neuron[edge_parent[j]] | j in first..last]; + array[int] of float: ws = [ edge_weight[j] | j in first..last ]; + float: b = bias[i]; } in + neuron[i] = if neuron_type = NT_RELU then neuron_relu(ins,ws,b) + elseif neuron_type = NT_STEP then neuron_step(ins,ws,b) + elseif neuron_type = NT_LINEAR then neuron_linear(ins,ws,b) + elseif neuron_type = NT_SOFTPLUS then neuron_softplus(ins,ws,b) + else 0.0 endif + ); + +%-----------------------------------------------------------------------------% + +function var float: neuron_relu(array[int] of var float: inputs, + array[int] of float: weights, + float: bias) = + max(0.0, sum(i in index_set(inputs))(weights[i]*inputs[i]) + bias); + +function var float: neuron_step(array[int] of var float: inputs, + array[int] of float: weights, + float: bias) = + (sum(i in index_set(inputs))(weights[i]*inputs[i]) + bias >= 0.0); + +function var float: neuron_linear(array[int] of var float: inputs, + array[int] of float: weights, + float: bias) = + (sum(i in index_set(inputs))(weights[i]*inputs[i]) + bias); + +function var float: neuron_softplus(array[int] of var float: inputs, + array[int] of float: weights, + float: bias) = + (ln(1 + exp(sum(i in index_set(inputs))(weights[i]*inputs[i]) + bias))); diff --git a/share/minizinc/std/fzn_neural_net_reif.mzn b/share/minizinc/std/fzn_neural_net_reif.mzn new file mode 100644 index 0000000..5f43b75 --- /dev/null +++ b/share/minizinc/std/fzn_neural_net_reif.mzn @@ -0,0 +1,11 @@ +predicate fzn_neural_net_reif(array[int] of var float: inputs, + array[int] of int: input_ids, + array[int] of var float: outputs, + array[int] of int: output_ids, + array[int] of float: bias, + array[int] of float: edge_weight, + array[int] of int: edge_parent, + array[int] of int: first_edge, + NEURON_TYPE: neuron_type, + var bool: b) = + abort("Reification of neural_net constraint is not supported"); diff --git a/share/minizinc/std/fzn_nvalue.mzn b/share/minizinc/std/fzn_nvalue.mzn new file mode 100644 index 0000000..702076b --- /dev/null +++ b/share/minizinc/std/fzn_nvalue.mzn @@ -0,0 +1,7 @@ +predicate fzn_nvalue(var int: n, array[int] of var int: x) = + let { int: lx = lb_array(x), + int: ux = ub_array(x), + } in + n == sum(j in lx..ux) ( + bool2int(exists(i in index_set(x)) ( x[i] = j )) + ); diff --git a/share/minizinc/std/fzn_nvalue_reif.mzn b/share/minizinc/std/fzn_nvalue_reif.mzn new file mode 100644 index 0000000..49958ad --- /dev/null +++ b/share/minizinc/std/fzn_nvalue_reif.mzn @@ -0,0 +1,7 @@ +predicate fzn_nvalue_reif(var int: n, array[int] of var int: x, var bool: b) = + let { int: lx = lb_array(x), + int: ux = ub_array(x), + } in b <-> + n == sum(j in lx..ux) ( + bool2int(exists(i in index_set(x)) ( x[i] = j )) + ); diff --git a/share/minizinc/std/fzn_partition_set.mzn b/share/minizinc/std/fzn_partition_set.mzn new file mode 100644 index 0000000..8179235 --- /dev/null +++ b/share/minizinc/std/fzn_partition_set.mzn @@ -0,0 +1,6 @@ +include "all_disjoint.mzn"; + +predicate fzn_partition_set(array[int] of var set of int: S, + set of int: universe) = + all_disjoint(S) /\ universe == array_union(i in index_set(S)) ( S[i] ); + diff --git a/share/minizinc/std/fzn_partition_set_reif.mzn b/share/minizinc/std/fzn_partition_set_reif.mzn new file mode 100644 index 0000000..e4dac8f --- /dev/null +++ b/share/minizinc/std/fzn_partition_set_reif.mzn @@ -0,0 +1,6 @@ +include "all_disjoint.mzn"; + +predicate fzn_partition_set_reif(array[int] of var set of int: S, + set of int: universe, + var bool: b) = + b <-> ( all_disjoint(S) /\ universe == array_union(i in index_set(S)) ( S[i] ) ); diff --git a/share/minizinc/std/fzn_path_enum.mzn b/share/minizinc/std/fzn_path_enum.mzn new file mode 100644 index 0000000..db76066 --- /dev/null +++ b/share/minizinc/std/fzn_path_enum.mzn @@ -0,0 +1,16 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_path(array[int] of $$N: from, array[int] of $$N: to, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { int: E = length(es); + array[1..2*E] of int: dfrom = from ++ to; + array[1..2*E] of int: dto = to ++ from; + array[1..2*E] of var bool: des; + } in + /* ensure that the directed edges selected agree with undirected edges */ + forall(e in 1..E)(es[e-1+min(index_set(es))] <-> (des[e] \/ des[e+E])) /\ + /* duplicate the edges so that the we can use directed graph path */ + fzn_dpath(dfrom,dto,s,t,ns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_path_enum_reif.mzn b/share/minizinc/std/fzn_path_enum_reif.mzn new file mode 100644 index 0000000..f05874b --- /dev/null +++ b/share/minizinc/std/fzn_path_enum_reif.mzn @@ -0,0 +1,9 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_path_reif(array[int] of $$N: from, array[int] of $$N: to, + var $$N: s, var $$N: t, array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified path constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_path_int.mzn b/share/minizinc/std/fzn_path_int.mzn new file mode 100644 index 0000000..e56b1b6 --- /dev/null +++ b/share/minizinc/std/fzn_path_int.mzn @@ -0,0 +1,15 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_path(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es) = + let { array[1..2*E] of int: dfrom = from ++ to; + array[1..2*E] of int: dto = to ++ from; + array[1..2*E] of var bool: des; + } in + /* ensure that the directed edges selected agree with undirected edges */ + forall(e in 1..E)(es[e] <-> (des[e] \/ des[e+E])) /\ + /* duplicate the edges so that the we can use directed graph path */ + fzn_dpath(N,2*E,dfrom,dto,s,t,ns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_path_int_reif.mzn b/share/minizinc/std/fzn_path_int_reif.mzn new file mode 100644 index 0000000..e67399b --- /dev/null +++ b/share/minizinc/std/fzn_path_int_reif.mzn @@ -0,0 +1,9 @@ +include "tree.mzn"; +include "subgraph.mzn"; + +predicate fzn_path_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: s, var int: t, array[int] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified path constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_piecewise_linear.mzn b/share/minizinc/std/fzn_piecewise_linear.mzn new file mode 100644 index 0000000..cde3f9c --- /dev/null +++ b/share/minizinc/std/fzn_piecewise_linear.mzn @@ -0,0 +1,32 @@ +/* + * Function fzn_piecewise_linear_base creates + * interval representation of x, + * which is reusable if we have several pwls + * on the same set of breakpoints + */ +function array[int, int] of var 0.0..1.0: fzn_piecewise_linear_base( + var float: x, array[int] of float: xi) ::promise_total = + let { + set of int: is = index_set(xi), + set of int: is_1 = is diff { max(is) }, + array[is_1] of var 0.0..1.0: s, %% Segment variables + array[is_1] of var 0..1: f, + constraint 1 == sum(f), + constraint forall(i in is_1)(f[i] >= s[i]), + constraint x == sum(i in is_1)(xi[i] * f[i] + (xi[i+1]-xi[i]) * s[i]), + } in + array2d(is_1, 1..2, f++s); + +predicate fzn_piecewise_linear(var float: x, var float: y, + array[int] of float: xi, array[int] of float: vi) = + let { + set of int: is = index_set(xi), + constraint assert(is == index_set(vi) /\ 0 x[i] in t + ) /\ + % All values in 't' must be mapped from a value in 's'. + forall(i in ub(t)) ( + i in t -> exists(j in ub(s)) ( j in s /\ x[j] == i ) + ); diff --git a/share/minizinc/std/fzn_range_reif.mzn b/share/minizinc/std/fzn_range_reif.mzn new file mode 100644 index 0000000..8057ce7 --- /dev/null +++ b/share/minizinc/std/fzn_range_reif.mzn @@ -0,0 +1,13 @@ +predicate fzn_range_reif(array[int] of var int: x, var set of int: s, + var set of int: t, + var bool: b) = + b <-> ( + % All values in 's' must map to a value in 't'. + forall(i in ub(s)) ( + i in s -> x[i] in t + ) /\ + % All values in 't' must be mapped from a value in 's'. + forall(i in ub(t)) ( + i in t -> exists(j in ub(s)) ( j in s /\ x[j] == i ) + ) + ); diff --git a/share/minizinc/std/fzn_reachable_enum.mzn b/share/minizinc/std/fzn_reachable_enum.mzn new file mode 100644 index 0000000..2b98568 --- /dev/null +++ b/share/minizinc/std/fzn_reachable_enum.mzn @@ -0,0 +1,17 @@ +include "subgraph.mzn"; + +predicate fzn_reachable(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { + int: E = length(es); + set of int: NODE = min(index_set(ns))..max(index_set(ns)); + array[1..2*E] of NODE: dfrom = from ++ to; + array[1..2*E] of NODE: dto = to ++ from; + array[1..2*E] of var bool: des = es ++ es; + array[NODE] of var bool: dns = array1d(NODE,ns); + var NODE: dr = r; + } in + /* duplicate the edges so that we can use directed graph reachability */ + fzn_dreachable(dfrom,dto,dr,dns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_reachable_enum_reif.mzn b/share/minizinc/std/fzn_reachable_enum_reif.mzn new file mode 100644 index 0000000..50778d4 --- /dev/null +++ b/share/minizinc/std/fzn_reachable_enum_reif.mzn @@ -0,0 +1,16 @@ +predicate fzn_reachable_reif(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + let { + int: E = length(es); + set of int: NODE = min(index_set(ns))..max(index_set(ns)); + array[1..2*E] of NODE: dfrom = from ++ to; + array[1..2*E] of NODE: dto = to ++ from; + array[1..2*E] of var bool: des = es ++ es; + array[NODE] of var bool: dns = array1d(NODE,ns); + var NODE: dr = r; + } in + /* duplicate the edges so that we can use directed graph reachability */ + b <-> fzn_dreachable(dfrom,dto,dr,dns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_reachable_int.mzn b/share/minizinc/std/fzn_reachable_int.mzn new file mode 100644 index 0000000..4d01e7f --- /dev/null +++ b/share/minizinc/std/fzn_reachable_int.mzn @@ -0,0 +1,13 @@ +include "subgraph.mzn"; + +predicate fzn_reachable(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es) = + let { + array[1..2*E] of int: dfrom = from ++ to; + array[1..2*E] of int: dto = to ++ from; + array[1..2*E] of var bool: des = es ++ es; + } in + /* duplicate the edges so that the we can use directed graph reachability */ + fzn_dreachable(N,2*E,dfrom,dto,r,ns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_reachable_int_reif.mzn b/share/minizinc/std/fzn_reachable_int_reif.mzn new file mode 100644 index 0000000..ee4aae2 --- /dev/null +++ b/share/minizinc/std/fzn_reachable_int_reif.mzn @@ -0,0 +1,12 @@ +predicate fzn_reachable_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, + var bool: b) = + let { + array[1..2*E] of int: dfrom = from ++ to; + array[1..2*E] of int: dto = to ++ from; + array[1..2*E] of var bool: des = es ++ es; + } in + /* duplicate the edges so that the we can use directed graph reachability */ + b <-> dreachable(N,2*E,dfrom,dto,r,ns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_regular.mzn b/share/minizinc/std/fzn_regular.mzn new file mode 100644 index 0000000..ff2f777 --- /dev/null +++ b/share/minizinc/std/fzn_regular.mzn @@ -0,0 +1,16 @@ +predicate fzn_regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F) = + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)), + int: n = max(index_set(x)) + 1, + array[m..n] of var 1..Q: a + } in + a[m] = q0 /\ % Set a[0]. + forall(i in index_set(x)) ( + x[i] in 1..S /\ % Do this in case it's a var. + a[i+1] = d[a[i], x[i]] % Determine a[i+1]. + ) /\ + a[n] in F; % Check the final state is in F. diff --git a/share/minizinc/std/fzn_regular_nfa.mzn b/share/minizinc/std/fzn_regular_nfa.mzn new file mode 100644 index 0000000..cd7b192 --- /dev/null +++ b/share/minizinc/std/fzn_regular_nfa.mzn @@ -0,0 +1,16 @@ +predicate fzn_regular_nfa(array[int] of var int: x, int: Q, int: S, + array[int,int] of set of int: d, int: q0, set of int: F) = + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)), + int: n = max(index_set(x)) + 1, + array[m..n] of var 1..Q: a + } in + a[m] = q0 /\ % Set a[0]. + forall(i in index_set(x)) ( + x[i] in 1..S /\ % Do this in case it's a var. + a[i+1] in d[a[i], x[i]] % Determine a[i+1]. + ) /\ + a[n] in F; % Check the final state is in F. diff --git a/share/minizinc/std/fzn_regular_nfa_reif.mzn b/share/minizinc/std/fzn_regular_nfa_reif.mzn new file mode 100644 index 0000000..37a633f --- /dev/null +++ b/share/minizinc/std/fzn_regular_nfa_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_regular_nfa_reif(array[int] of var int: x, int: Q, int: S, + array[int,int] of set of int: d, int: q0, set of int: F, + var bool: b) = + abort("Reified regular_nfa constraint is not supported"); diff --git a/share/minizinc/std/fzn_regular_nfa_set.mzn b/share/minizinc/std/fzn_regular_nfa_set.mzn new file mode 100644 index 0000000..80c3990 --- /dev/null +++ b/share/minizinc/std/fzn_regular_nfa_set.mzn @@ -0,0 +1,16 @@ +predicate fzn_regular_nfa(array[int] of var int: x, int: Q, set of int: S, + array[int,int] of set of int: d, int: q0, set of int: F) = + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)), + int: n = max(index_set(x)) + 1, + array[m..n] of var 1..Q: a + } in + a[m] = q0 /\ % Set a[0]. + forall(i in index_set(x)) ( + x[i] in S /\ % Do this in case it's a var. + a[i+1] in d[a[i], x[i]] % Determine a[i+1]. + ) /\ + a[n] in F; % Check the final state is in F. diff --git a/share/minizinc/std/fzn_regular_nfa_set_reif.mzn b/share/minizinc/std/fzn_regular_nfa_set_reif.mzn new file mode 100644 index 0000000..c1b55e0 --- /dev/null +++ b/share/minizinc/std/fzn_regular_nfa_set_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_regular_nfa_reif(array[int] of var int: x, int: Q, set of int: S, + array[int,int] of set of int: d, int: q0, set of int: F, + var bool: b) = + abort("Reified regular_nfa constraint is not supported"); diff --git a/share/minizinc/std/fzn_regular_regexp.mzn b/share/minizinc/std/fzn_regular_regexp.mzn new file mode 100644 index 0000000..db589e9 --- /dev/null +++ b/share/minizinc/std/fzn_regular_regexp.mzn @@ -0,0 +1 @@ +predicate fzn_regular(array[int] of var int: x, string: r); diff --git a/share/minizinc/std/fzn_regular_reif.mzn b/share/minizinc/std/fzn_regular_reif.mzn new file mode 100644 index 0000000..37d332f --- /dev/null +++ b/share/minizinc/std/fzn_regular_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_regular_reif(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F, + var bool: b) = + abort("Reified regular constraint is not supported"); diff --git a/share/minizinc/std/fzn_regular_set.mzn b/share/minizinc/std/fzn_regular_set.mzn new file mode 100644 index 0000000..32a369b --- /dev/null +++ b/share/minizinc/std/fzn_regular_set.mzn @@ -0,0 +1,16 @@ +predicate fzn_regular(array[int] of var int: x, int: Q, set of int: S, + array[int,int] of int: d, int: q0, set of int: F) = + let { + % If x has index set m..n-1, then a[m] holds the initial state + % (q0), and a[i+1] holds the state we're in after processing + % x[i]. If a[n] is in F, then we succeed (ie. accept the string). + int: m = min(index_set(x)), + int: n = max(index_set(x)) + 1, + array[m..n] of var 1..Q: a + } in + a[m] = q0 /\ % Set a[0]. + forall(i in index_set(x)) ( + x[i] in S /\ % Do this in case it's a var. + a[i+1] = d[a[i], x[i]] % Determine a[i+1]. + ) /\ + a[n] in F; % Check the final state is in F. diff --git a/share/minizinc/std/fzn_regular_set_reif.mzn b/share/minizinc/std/fzn_regular_set_reif.mzn new file mode 100644 index 0000000..ca18b90 --- /dev/null +++ b/share/minizinc/std/fzn_regular_set_reif.mzn @@ -0,0 +1,4 @@ +predicate fzn_regular_reif(array[int] of var int: x, int: Q, set of int: S, + array[int,int] of int: d, int: q0, set of int: F, + var bool: b) = + abort("Reified regular constraint is not supported"); diff --git a/share/minizinc/std/fzn_roots.mzn b/share/minizinc/std/fzn_roots.mzn new file mode 100644 index 0000000..b95fe77 --- /dev/null +++ b/share/minizinc/std/fzn_roots.mzn @@ -0,0 +1,9 @@ +predicate fzn_roots(array[int] of var int: x, var set of int: s, + var set of int: t) = + % All values in 's' must map to a value in 't'. + forall(i in ub(s)) ( + i in s -> x[i] in t + ) /\ + forall(i in ub(t)) ( + i in t -> forall(j in index_set(x)) (x[j] = i -> j in s ) + ); diff --git a/share/minizinc/std/fzn_roots_reif.mzn b/share/minizinc/std/fzn_roots_reif.mzn new file mode 100644 index 0000000..a5a3c23 --- /dev/null +++ b/share/minizinc/std/fzn_roots_reif.mzn @@ -0,0 +1,11 @@ +predicate fzn_roots_reif(array[int] of var int: x, var set of int: s, + var set of int: t, var bool: b) = + b <-> ( + % All values in 's' must map to a value in 't'. + forall(i in ub(s)) ( + i in s -> x[i] in t + ) /\ + forall(i in ub(t)) ( + i in t -> forall(j in index_set(x)) (x[j] = i -> j in s ) + ) + ); diff --git a/share/minizinc/std/fzn_seq_precede_chain_int.mzn b/share/minizinc/std/fzn_seq_precede_chain_int.mzn new file mode 100644 index 0000000..4e56e1c --- /dev/null +++ b/share/minizinc/std/fzn_seq_precede_chain_int.mzn @@ -0,0 +1,11 @@ +predicate fzn_seq_precede_chain_int(array[int] of var int: X) = + let { + int : l = lb_array (X) ; % least possible value + int : u = ub_array (X) ; % greatest possible value + int : f = min ( index_set (X )); + array [ index_set (X) ] of var l .. u: H; + } in + H[f] <= 1 /\ H[f] = max (X[f], 0) /\ + forall ( i in index_set ( X) diff {f} ) ( + H[i] <= H[i-1] + 1 /\ H[i] = max (X[i], H[i-1]) + ); diff --git a/share/minizinc/std/fzn_seq_precede_chain_int_reif.mzn b/share/minizinc/std/fzn_seq_precede_chain_int_reif.mzn new file mode 100644 index 0000000..edda49e --- /dev/null +++ b/share/minizinc/std/fzn_seq_precede_chain_int_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_seq_precede_chain_int_reif(array[int] of var int: X, var bool: b) = + abort("Reified seq_precede_chain constraint is not supported"); diff --git a/share/minizinc/std/fzn_seq_precede_chain_set.mzn b/share/minizinc/std/fzn_seq_precede_chain_set.mzn new file mode 100644 index 0000000..e46c129 --- /dev/null +++ b/share/minizinc/std/fzn_seq_precede_chain_set.mzn @@ -0,0 +1,2 @@ +predicate fzn_seq_precede_chain_set_reif(array[int] of var set of int: X, var bool: b) = + abort("Reified seq_precede_chain constraint is not supported"); diff --git a/share/minizinc/std/fzn_seq_precede_chain_set_reif.mzn b/share/minizinc/std/fzn_seq_precede_chain_set_reif.mzn new file mode 100644 index 0000000..4e167d5 --- /dev/null +++ b/share/minizinc/std/fzn_seq_precede_chain_set_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_seq_precede_chain_set(array[int] of var set of int: X) = + abort("Reified seq_precede_chain constraint is not supported"); diff --git a/share/minizinc/std/fzn_set_member.mzn b/share/minizinc/std/fzn_set_member.mzn new file mode 100644 index 0000000..d69e3dd --- /dev/null +++ b/share/minizinc/std/fzn_set_member.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_set_member(var set of int: x, var int: y) = + y in x; diff --git a/share/minizinc/std/fzn_set_member_reif.mzn b/share/minizinc/std/fzn_set_member_reif.mzn new file mode 100644 index 0000000..72b3175 --- /dev/null +++ b/share/minizinc/std/fzn_set_member_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate fzn_set_member_reif(var set of int: x, var int: y, var bool: b) = + b <-> y in x; diff --git a/share/minizinc/std/fzn_sliding_sum.mzn b/share/minizinc/std/fzn_sliding_sum.mzn new file mode 100644 index 0000000..0fa326d --- /dev/null +++ b/share/minizinc/std/fzn_sliding_sum.mzn @@ -0,0 +1,15 @@ +predicate fzn_sliding_sum(int: low, int: up, int: seq, array[int] of var int: vs) = + /* CS decomposition: see S. Brand, N. Narodytska, C-G. Quimper, P.J. Stuckey, and T. Walsh. Encodings of the sequence constraint. In C. Bessiere, editor, Proceedings of the 13th International Conference on Principles and Practice of Constraint Programming, volume 4741 of LNCS, pages 210–224. Springer-Verlag, 2007. + */ + let { int: lx = min(index_set(vs)); + int: ux = max(index_set(vs)); + array[lx-1..ux] of var int: S; + } in + S[lx-1] = 0 /\ + forall (i in lx .. ux) ( + S[i] = vs[i] + S[i-1] + ) /\ + forall (i in lx-1 .. ux - seq) ( + S[i] <= S[i+seq] - low /\ + S[i+seq] <= S[i] + up + ); diff --git a/share/minizinc/std/fzn_sliding_sum_reif.mzn b/share/minizinc/std/fzn_sliding_sum_reif.mzn new file mode 100644 index 0000000..21ee388 --- /dev/null +++ b/share/minizinc/std/fzn_sliding_sum_reif.mzn @@ -0,0 +1,17 @@ +predicate fzn_sliding_sum_reif(int: low, int: up, int: seq, + array[int] of var int: vs, var bool: b) = + /* CS decomposition: see S. Brand, N. Narodytska, C-G. Quimper, P.J. Stuckey, and T. Walsh. Encodings of the sequence constraint. In C. Bessiere, editor, Proceedings of the 13th International Conference on Principles and Practice of Constraint Programming, volume 4741 of LNCS, pages 210–224. Springer-Verlag, 2007. + */ + let { int: lx = min(index_set(vs)); + int: ux = max(index_set(vs)); + array[lx-1..ux] of var int: S; + } in + S[lx-1] = 0 /\ + forall (i in lx .. ux) ( + S[i] = vs[i] + S[i-1] + ) /\ + b <-> forall (i in lx-1 .. ux - seq) ( + S[i] <= S[i+seq] - low /\ + S[i+seq] <= S[i] + up + ); + diff --git a/share/minizinc/std/fzn_sort.mzn b/share/minizinc/std/fzn_sort.mzn new file mode 100644 index 0000000..87c550b --- /dev/null +++ b/share/minizinc/std/fzn_sort.mzn @@ -0,0 +1,14 @@ +include "alldifferent.mzn"; +include "increasing.mzn"; + +predicate fzn_sort(array[int] of var int: x, array[int] of var int: y) = + let { + int: lx = min(index_set(x)), + int: ux = max(index_set(x)), + int: ly = min(index_set(y)), + int: uy = max(index_set(y)), + array[lx..ux] of var ly..uy: p + } in + forall(i in index_set(x)) ( y[p[i]] == x[i] ) /\ + alldifferent(p) /\ + increasing(y); diff --git a/share/minizinc/std/fzn_sort_reif.mzn b/share/minizinc/std/fzn_sort_reif.mzn new file mode 100644 index 0000000..64a1889 --- /dev/null +++ b/share/minizinc/std/fzn_sort_reif.mzn @@ -0,0 +1,4 @@ +include "sort_fn.mzn"; + +predicate fzn_sort_reif(array[int] of var int: x, array[int] of var int: y, var bool: b) = + let { array[int] of var int: s = sort(x); } in b <-> s=y; diff --git a/share/minizinc/std/fzn_span.mzn b/share/minizinc/std/fzn_span.mzn new file mode 100644 index 0000000..b26ac36 --- /dev/null +++ b/share/minizinc/std/fzn_span.mzn @@ -0,0 +1,7 @@ +predicate fzn_span(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d) = + (occurs(s0) <-> exists(i in index_set(s))(occurs(s[i]))) /\ + s0 = min(s) /\ + (absent(s0) -> d0 = 0) /\ + s0 ~+ d0 = max([s[i] ~+ d[i] | i in index_set(s)]); diff --git a/share/minizinc/std/fzn_span_reif.mzn b/share/minizinc/std/fzn_span_reif.mzn new file mode 100644 index 0000000..cc3e1d3 --- /dev/null +++ b/share/minizinc/std/fzn_span_reif.mzn @@ -0,0 +1,10 @@ +predicate fzn_span_reif(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d, + var bool: b) = + b <-> ( + (occurs(s0) <-> exists(i in index_set(s))(occurs(s[i]))) /\ + s0 = min(s) /\ + (absent(s0) -> d0 = 0) /\ + s0 ~+ d0 = max([s[i] ~+ d[i] | i in index_set(s)]) + ); diff --git a/share/minizinc/std/fzn_steiner.mzn b/share/minizinc/std/fzn_steiner.mzn new file mode 100644 index 0000000..ef10afd --- /dev/null +++ b/share/minizinc/std/fzn_steiner.mzn @@ -0,0 +1,9 @@ +include "tree.mzn"; + +predicate fzn_steiner(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + let { var 1..N: r; } in + tree(N,E,from,to,r,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_steiner_reif.mzn b/share/minizinc/std/fzn_steiner_reif.mzn new file mode 100644 index 0000000..5947221 --- /dev/null +++ b/share/minizinc/std/fzn_steiner_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_steiner_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + array[int] of var bool: ns, array[int] of var bool: es, var int: K, + var bool: b) = + abort("Reified steiner constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_strict_lex2.mzn b/share/minizinc/std/fzn_strict_lex2.mzn new file mode 100644 index 0000000..aace6bb --- /dev/null +++ b/share/minizinc/std/fzn_strict_lex2.mzn @@ -0,0 +1,22 @@ +include "lex_less.mzn"; + +predicate fzn_strict_lex2(array[int, int] of var int: x) = + let { + int: lbx1 = min(index_set_1of2(x)), + int: ubx1 = max(index_set_1of2(x)), + int: lbx2 = min(index_set_2of2(x)), + int: ubx2 = max(index_set_2of2(x)) + } in ( + forall(i in lbx1 + 1 .. ubx1) ( + lex_less([x[i - 1, j] | j in index_set_2of2(x)], + [x[i, j] | j in index_set_2of2(x)] + ) + ) + /\ + + forall(j in lbx2 + 1 .. ubx2) ( + lex_less([x[i, j - 1] | i in index_set_1of2(x)], + [x[i, j ] | i in index_set_1of2(x)] + ) + ) + ); diff --git a/share/minizinc/std/fzn_strict_lex2_reif.mzn b/share/minizinc/std/fzn_strict_lex2_reif.mzn new file mode 100644 index 0000000..059beb0 --- /dev/null +++ b/share/minizinc/std/fzn_strict_lex2_reif.mzn @@ -0,0 +1,22 @@ +include "lex_less.mzn"; + +predicate fzn_strict_lex2_reif(array[int, int] of var int: x, var bool: b) = + let { + int: lbx1 = min(index_set_1of2(x)), + int: ubx1 = max(index_set_1of2(x)), + int: lbx2 = min(index_set_2of2(x)), + int: ubx2 = max(index_set_2of2(x)) + } in b <-> ( + forall(i in lbx1 + 1 .. ubx1) ( + lex_less([x[i - 1, j] | j in index_set_2of2(x)], + [x[i, j] | j in index_set_2of2(x)] + ) + ) + /\ + + forall(j in lbx2 + 1 .. ubx2) ( + lex_less([x[i, j - 1] | i in index_set_1of2(x)], + [x[i, j ] | i in index_set_1of2(x)] + ) + ) + ); diff --git a/share/minizinc/std/fzn_strictly_decreasing_bool.mzn b/share/minizinc/std/fzn_strictly_decreasing_bool.mzn new file mode 100644 index 0000000..a3bdd64 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_decreasing_bool.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict decreasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_decreasing_bool(array[int] of var bool: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] > x[i]); diff --git a/share/minizinc/std/fzn_strictly_decreasing_bool_reif.mzn b/share/minizinc/std/fzn_strictly_decreasing_bool_reif.mzn new file mode 100644 index 0000000..17998bf --- /dev/null +++ b/share/minizinc/std/fzn_strictly_decreasing_bool_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict decreasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_decreasing_bool_reif(array[int] of var bool: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] > x[i]); diff --git a/share/minizinc/std/fzn_strictly_decreasing_int.mzn b/share/minizinc/std/fzn_strictly_decreasing_int.mzn new file mode 100644 index 0000000..3d5d510 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_decreasing_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict decreasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_decreasing_int(array[int] of var int: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] > x[i]); diff --git a/share/minizinc/std/fzn_strictly_decreasing_int_reif.mzn b/share/minizinc/std/fzn_strictly_decreasing_int_reif.mzn new file mode 100644 index 0000000..a838fb7 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_decreasing_int_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict decreasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_decreasing_int_reif(array[int] of var int: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] > x[i]); diff --git a/share/minizinc/std/fzn_strictly_increasing_bool.mzn b/share/minizinc/std/fzn_strictly_increasing_bool.mzn new file mode 100644 index 0000000..26efb65 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_increasing_bool.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict increasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_increasing_bool(array[int] of var bool: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] < x[i]); diff --git a/share/minizinc/std/fzn_strictly_increasing_bool_reif.mzn b/share/minizinc/std/fzn_strictly_increasing_bool_reif.mzn new file mode 100644 index 0000000..d4bee29 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_increasing_bool_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict increasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_increasing_bool_reif(array[int] of var bool: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] < x[i]); diff --git a/share/minizinc/std/fzn_strictly_increasing_int.mzn b/share/minizinc/std/fzn_strictly_increasing_int.mzn new file mode 100644 index 0000000..f7e0908 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_increasing_int.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict increasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_increasing_int(array[int] of var int: x) = + forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] < x[i]); diff --git a/share/minizinc/std/fzn_strictly_increasing_int_reif.mzn b/share/minizinc/std/fzn_strictly_increasing_int_reif.mzn new file mode 100644 index 0000000..4fd96b2 --- /dev/null +++ b/share/minizinc/std/fzn_strictly_increasing_int_reif.mzn @@ -0,0 +1,6 @@ +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict increasing order +%-----------------------------------------------------------------------------% + +predicate fzn_strictly_increasing_int_reif(array[int] of var int: x, var bool: b) = + b <-> forall(i in index_set(x) diff { min(index_set(x)) }) (x[i-1] < x[i]); diff --git a/share/minizinc/std/fzn_subcircuit.mzn b/share/minizinc/std/fzn_subcircuit.mzn new file mode 100644 index 0000000..8d5d19c --- /dev/null +++ b/share/minizinc/std/fzn_subcircuit.mzn @@ -0,0 +1,50 @@ +include "alldifferent.mzn"; + +predicate fzn_subcircuit(array[int] of var int: x) = + let { + set of int: S = index_set(x), + int: l = min(S), + int: u = max(S), + int: n = card(S), + array[S] of var 1..n: order, + array[S] of var bool: ins = array1d(S,[ x[i] != i | i in S]), + var l..u+1: firstin = min([ u+1 + bool2int(ins[i])*(i-u-1) | i in S]), + var S: lastin, + var bool: empty = (firstin > u), + } in + alldifferent(x) /\ + alldifferent(order) /\ + + % If the subcircuit is empty then each node points at itself. + % + (empty -> forall(i in S)(not ins[i])) /\ + + % If the subcircuit is non-empty then order numbers the subcircuit. + % + ((not empty) -> + + % The firstin node is numbered 1. + order[firstin] = 1 /\ + + % The lastin node is greater than firstin. + lastin > firstin /\ + + % The lastin node points at firstin. + x[lastin] = firstin /\ + + % And both are in + ins[lastin] /\ ins[firstin] /\ + + % The successor of each node except where it is firstin is + % numbered one more than the predecessor. + forall(i in S) ( + (ins[i] /\ x[i] != firstin) -> order[x[i]] = order[i] + 1 + ) /\ + + % Each node that is not in is numbered after the lastin node. + forall(i in S) ( + ins[i] \/ order[lastin] < order[i] + ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_subcircuit_reif.mzn b/share/minizinc/std/fzn_subcircuit_reif.mzn new file mode 100644 index 0000000..e320de2 --- /dev/null +++ b/share/minizinc/std/fzn_subcircuit_reif.mzn @@ -0,0 +1,6 @@ +include "alldifferent.mzn"; + +predicate fzn_subcircuit_reif(array[int] of var int: x, var bool: b) = + abort("Reified subcircuit/1 is not supported."); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_subgraph_enum.mzn b/share/minizinc/std/fzn_subgraph_enum.mzn new file mode 100644 index 0000000..f02e923 --- /dev/null +++ b/share/minizinc/std/fzn_subgraph_enum.mzn @@ -0,0 +1,7 @@ +predicate fzn_subgraph(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + forall(e in index_set(from)) ( + (es[e] -> ns[from[e]]) /\ (es[e] -> ns[to[e]]) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_subgraph_enum_reif.mzn b/share/minizinc/std/fzn_subgraph_enum_reif.mzn new file mode 100644 index 0000000..cf00b40 --- /dev/null +++ b/share/minizinc/std/fzn_subgraph_enum_reif.mzn @@ -0,0 +1,8 @@ +predicate fzn_subgraph_reif(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + b <-> forall(e in index_set(from)) ( + (es[e] -> ns[from[e]]) /\ (es[e] -> ns[to[e]]) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_subgraph_int.mzn b/share/minizinc/std/fzn_subgraph_int.mzn new file mode 100644 index 0000000..75279a0 --- /dev/null +++ b/share/minizinc/std/fzn_subgraph_int.mzn @@ -0,0 +1,7 @@ +predicate fzn_subgraph(int: N, int: E, array[int] of int: from, array[int] of int: to, + array[int] of var bool: ns, array[int] of var bool: es) = + forall(e in 1..E) ( + (es[e] -> ns[from[e]]) /\ (es[e] -> ns[to[e]]) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_subgraph_int_reif.mzn b/share/minizinc/std/fzn_subgraph_int_reif.mzn new file mode 100644 index 0000000..3661508 --- /dev/null +++ b/share/minizinc/std/fzn_subgraph_int_reif.mzn @@ -0,0 +1,8 @@ +predicate fzn_subgraph_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + array[int] of var bool: ns, array[int] of var bool: es, + var bool: b) = + b <-> forall(e in 1..E) ( + (es[e] -> ns[from[e]]) /\ (es[e] -> ns[to[e]]) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_sum_pred.mzn b/share/minizinc/std/fzn_sum_pred.mzn new file mode 100644 index 0000000..eef9e2c --- /dev/null +++ b/share/minizinc/std/fzn_sum_pred.mzn @@ -0,0 +1,6 @@ +predicate fzn_sum_pred(var int: i, array[int] of set of int: sets, + array[int] of int: cs, var int: s) = + let { + array[index_set(sets)] of int: sums = [ sum(i in sets[j])(cs[i]) | j in index_set(sets) ]; + } in sums[i] = s; + diff --git a/share/minizinc/std/fzn_sum_pred_reif.mzn b/share/minizinc/std/fzn_sum_pred_reif.mzn new file mode 100644 index 0000000..6fa79e6 --- /dev/null +++ b/share/minizinc/std/fzn_sum_pred_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_sum_pred_reif(var int: i, array[int] of set of int: sets, + array[int] of int: cs, var int: s, var bool: b) = + let { + array[index_set(sets)] of int: sums = [ sum(i in sets[j])(cs[i]) | j in index_set(sets) ]; + } in b <-> sums[i] = s; + diff --git a/share/minizinc/std/fzn_sum_set.mzn b/share/minizinc/std/fzn_sum_set.mzn new file mode 100644 index 0000000..3bc68be --- /dev/null +++ b/share/minizinc/std/fzn_sum_set.mzn @@ -0,0 +1,3 @@ +predicate fzn_sum_set(array[int] of int: vs, array[int] of int: ws, + var set of int: x, var int: s) = + s == sum(j in index_set(vs)) ( bool2int(vs[j] in x) * ws[j] ); diff --git a/share/minizinc/std/fzn_sum_set_reif.mzn b/share/minizinc/std/fzn_sum_set_reif.mzn new file mode 100644 index 0000000..2e1f9ae --- /dev/null +++ b/share/minizinc/std/fzn_sum_set_reif.mzn @@ -0,0 +1,3 @@ +predicate fzn_sum_set_reif(array[int] of int: vs, array[int] of int: ws, + var set of int: x, var int: s, var bool: b) = + b <-> s == sum(j in index_set(vs)) ( bool2int(j in x) * ws[j] ); diff --git a/share/minizinc/std/fzn_symmetric_all_different.mzn b/share/minizinc/std/fzn_symmetric_all_different.mzn new file mode 100644 index 0000000..12cca58 --- /dev/null +++ b/share/minizinc/std/fzn_symmetric_all_different.mzn @@ -0,0 +1,5 @@ +include "all_different.mzn"; + +predicate fzn_symmetric_all_different(array[int] of var int:x) = + all_different(x) + /\ forall(i, j in index_set(x) where i!=j) (x[i] = j -> x[j] = i); diff --git a/share/minizinc/std/fzn_symmetric_all_different_reif.mzn b/share/minizinc/std/fzn_symmetric_all_different_reif.mzn new file mode 100644 index 0000000..b841119 --- /dev/null +++ b/share/minizinc/std/fzn_symmetric_all_different_reif.mzn @@ -0,0 +1,5 @@ +include "all_different.mzn"; + +predicate fzn_symmetric_all_different_reif(array[int] of var int:x, var bool: b) = + b <-> ( all_different(x) + /\ forall(i, j in index_set(x) where i!=j) (x[i] = j -> x[j] = i) ); diff --git a/share/minizinc/std/fzn_table_bool.mzn b/share/minizinc/std/fzn_table_bool.mzn new file mode 100644 index 0000000..8908b8d --- /dev/null +++ b/share/minizinc/std/fzn_table_bool.mzn @@ -0,0 +1,24 @@ +%-----------------------------------------------------------------------------% +% A table constraint: table(x, t) represents the constraint x in t where we +% consider each row in t to be a tuple and t as a set of tuples. +%-----------------------------------------------------------------------------% + +predicate fzn_table_bool(array[int] of var bool: x, array[int, int] of bool: t) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: lt = min(index_set_1of2(t)), + int: ut = max(index_set_1of2(t)), + var lt..ut: i, + array[l..u, lt..ut] of bool: t_transposed = + array2d(l..u, lt..ut, [ t[i,j] | j in l..u, i in lt..ut ]) } + in + forall(j in l..u) ( + % Having the variable index component at the left position + % means that the nD-to-1D array translation during Mzn-to-Fzn + % will generate at most an offset constraint, instead of a + % scaling + offset constraint. + % + t_transposed[j,i] = x[j] + % + % t[i,j] = x[j] + ); diff --git a/share/minizinc/std/fzn_table_bool_reif.mzn b/share/minizinc/std/fzn_table_bool_reif.mzn new file mode 100644 index 0000000..1f8676d --- /dev/null +++ b/share/minizinc/std/fzn_table_bool_reif.mzn @@ -0,0 +1,8 @@ +%-----------------------------------------------------------------------------% +% A table constraint: table(x, t) represents the constraint x in t where we +% consider each row in t to be a tuple and t as a set of tuples. +%-----------------------------------------------------------------------------% + +predicate fzn_table_bool_reif(array[int] of var bool: x, array[int, int] of bool: t, + var bool: b) = + abort("Reified table/2 for Booleans is not supported."); diff --git a/share/minizinc/std/fzn_table_int.mzn b/share/minizinc/std/fzn_table_int.mzn new file mode 100644 index 0000000..98186d0 --- /dev/null +++ b/share/minizinc/std/fzn_table_int.mzn @@ -0,0 +1,26 @@ +%-----------------------------------------------------------------------------% +% A table constraint table(x, t) represents the constraint x in t where we +% consider each row in t to be a tuple and t as a set of tuples. +%-----------------------------------------------------------------------------% + +predicate fzn_table_int(array[int] of var int: x, array[int, int] of int: t) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: lt = min(index_set_1of2(t)), + int: ut = max(index_set_1of2(t)), + var lt..ut: i, + array[l..u, lt..ut] of int: t_transposed = + array2d(l..u, lt..ut, [ t[i,j] | j in l..u, i in lt..ut ]) } + in + forall(j in l..u) ( + % Having the variable index component at the left position + % means that the nD-to-1D array translation during Mzn-to-Fzn + % will generate at most an offset constraint, instead of a + % scaling + offset constraint. + % + t_transposed[j,i] = x[j] + % + % t[i,j] = x[j] + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_table_int_reif.mzn b/share/minizinc/std/fzn_table_int_reif.mzn new file mode 100644 index 0000000..d3a83c8 --- /dev/null +++ b/share/minizinc/std/fzn_table_int_reif.mzn @@ -0,0 +1,109 @@ +%-----------------------------------------------------------------------------% +% A table constraint table(x, t) represents the constraint x in t where we +% consider each row in t to be a tuple and t as a set of tuples. +%-----------------------------------------------------------------------------% + +%-----------------------------------------------------------------------------% +% Reified version +% +% We only support special cases of a few variables. +% +% The approach is to add the Boolean variable to the list of variables and +% create an extended table. The extended table covers all combinations of +% assignments to the original variables, and every entry in it is padded with a +% value that depends on whether that entry occurs in the original table. +% +% For example, the original table constraint +% +% x y +% --- +% 2 3 +% 5 8 +% 4 1 +% +% reified with a Boolean b is turned into a table constraint of the form +% +% x y b +% --------- +% 2 3 true +% 5 8 true +% 4 1 true +% ... false +% ... false % for all other pairs (x,y) +% ... false +% + +predicate fzn_table_int_reif(array[int] of var int: x, + array[int, int] of int: t, + var bool: b) = + + let { int: n_vars = length(x) } + in + + assert(n_vars in 1..5, + "'table' constraints in a reified context " ++ + "are only supported for 1..5 variables.", + + if n_vars = 1 then + + x[1] in { t[it,1] | it in index_set_1of2(t) } <-> b + + else + + let { set of int: ix = index_set(x), + set of int: full_size = 1..product(i in ix)( dom_size(x[i]) ), + array[full_size, 1..n_vars + 1] of int: t_b = + array2d(full_size, 1..n_vars + 1, + + if n_vars = 2 then + + [ let { array[ix] of int: tpl = [i1,i2] } in + (tpl ++ [bool2int(aux_is_in_table(tpl,t))])[p] + | i1 in dom(x[1]), + i2 in dom(x[2]), + p in 1..n_vars + 1 ] + + else if n_vars = 3 then + + [ let { array[ix] of int: tpl = [i1,i2,i3] } in + (tpl ++ [bool2int(aux_is_in_table(tpl,t))])[p] + | i1 in dom(x[1]), + i2 in dom(x[2]), + i3 in dom(x[3]), + p in 1..n_vars + 1 ] + + else if n_vars = 4 then + + [ let { array[ix] of int: tpl = [i1,i2,i3,i4] } + in + (tpl ++ [bool2int(aux_is_in_table(tpl,t))])[p] + | i1 in dom(x[1]), + i2 in dom(x[2]), + i3 in dom(x[3]), + i4 in dom(x[4]), + p in 1..n_vars + 1 ] + + else % if n_vars = 5 then + + [ let { array[ix] of int: tpl = [i1,i2,i3,i4,i5] } in + (tpl ++ [bool2int(aux_is_in_table(tpl,t))])[p] + | i1 in dom(x[1]), + i2 in dom(x[2]), + i3 in dom(x[3]), + i4 in dom(x[4]), + i5 in dom(x[5]), + p in 1..n_vars + 1 ] + + endif endif endif ) } + in + fzn_table_int(x ++ [bool2int(b)], t_b) + + endif + ); + +test aux_is_in_table(array[int] of int: e, array[int, int] of int: t) = + exists(i in index_set_1of2(t))( + forall(j in index_set(e))( t[i,j] = e[j] ) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_transitive_closure.mzn b/share/minizinc/std/fzn_transitive_closure.mzn new file mode 100644 index 0000000..d3f5e65 --- /dev/null +++ b/share/minizinc/std/fzn_transitive_closure.mzn @@ -0,0 +1,25 @@ +include "subgraph.mzn"; + +function array[$$N,$$N] of var bool: + fzn_transitive_closure(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { + array[index_set(ns)] of var 0..card(index_set(ns))-1: dist; /* distance from root */ + array[index_set(ns)] of var index_set(ns): parent; /* parent */ + } in + ns[r] /\ % the root must be chosen + dist[r] = 0 /\ % root is at distance 0 + parent[r] = r /\ % root is its own parent + forall(n in index_set(ns)) % nonselected nodes have parent 0 + (not ns[n] -> parent[n] = n) /\ + forall(n in index_set(ns)) % nonselected nodes have distance 0 + (not ns[n] -> dist[n] = 0) /\ + forall(n in index_set(ns)) % each in node except root must have a parent + (ns[n] -> (n = r \/ parent[n] != n)) /\ + forall(n in index_set(ns)) % each in node with a parent must be in and also its parent + (parent[n] != n -> (ns[n] /\ ns[parent[n]])) /\ + forall(n in index_set(ns)) % each except with a parent is one more than its parent + (parent[n] != n -> dist[n] = dist[parent[n]] + 1) /\ + forall(n in index_set(ns)) % each node with a parent must have that edge in + (parent[n] != n -> exists(e in index_set(from) where to[e] = n)(es[e] /\ from[e] = parent[n])) /\ + subgraph(from,to,ns,es); diff --git a/share/minizinc/std/fzn_tree_enum.mzn b/share/minizinc/std/fzn_tree_enum.mzn new file mode 100644 index 0000000..d3d16ca --- /dev/null +++ b/share/minizinc/std/fzn_tree_enum.mzn @@ -0,0 +1,14 @@ +predicate fzn_tree(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + let { + int: E = length(es); + array[1..2*E] of int: dfrom = from ++ to; + array[1..2*E] of int: dto = to ++ from; + array[1..2*E] of var bool: des; + } in + /* ensure that the directed edges selected agree with undirected edges */ + forall(e in 1..E)(es[e-1+min(index_set(es))] <-> (des[e] \/ des[e+E])) /\ + /* duplicate the edges so that the we can use directed graph reachability */ + fzn_dtree(dfrom,dto,r,ns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_tree_enum_reif.mzn b/share/minizinc/std/fzn_tree_enum_reif.mzn new file mode 100644 index 0000000..78da140 --- /dev/null +++ b/share/minizinc/std/fzn_tree_enum_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_tree_reif(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es, + var bool: b) = + abort("Reified tree constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_tree_int.mzn b/share/minizinc/std/fzn_tree_int.mzn new file mode 100644 index 0000000..b32f292 --- /dev/null +++ b/share/minizinc/std/fzn_tree_int.mzn @@ -0,0 +1,15 @@ +include "subgraph.mzn"; + +predicate fzn_tree(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es) = + let { + array[1..2*E] of int: dfrom = from ++ to; + array[1..2*E] of int: dto = to ++ from; + array[1..2*E] of var bool: des; + } in + /* ensure that the directed edges selected agree with undirected edges */ + forall(e in 1..E)(es[e] <-> (des[e] \/ des[e+E])) /\ + /* duplicate the edges so that the we can use directed graph reachability */ + fzn_dtree(N,2*E,dfrom,dto,r,ns,des); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_tree_int_reif.mzn b/share/minizinc/std/fzn_tree_int_reif.mzn new file mode 100644 index 0000000..967205a --- /dev/null +++ b/share/minizinc/std/fzn_tree_int_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_tree_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, var bool: b) = + abort("Reified tree constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_value_precede_chain_int.mzn b/share/minizinc/std/fzn_value_precede_chain_int.mzn new file mode 100644 index 0000000..7730612 --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_chain_int.mzn @@ -0,0 +1,17 @@ +include "seq_precede_chain.mzn"; + +predicate fzn_value_precede_chain_int(array[int] of int: T, array[int] of var int: X) = + if min(index_set(T)) = 1 /\ forall (i in index_set(T))(T[i] = i) + /\ max(T) = ub_array(X) + then + seq_precede_chain(X) + else + let { + int: l = lb_array(X); + int: u = ub_array(X); + array[1.. u -l +1] of int : p + = [sum([i | i in index_set(T) where T[i] = j]) | j in l..u]; + array [int] of var 0..length(T): Y + = array1d(index_set(X),[p[X[i]-l+1] | i in index_set(X)]); + } in seq_precede_chain(Y) + endif; diff --git a/share/minizinc/std/fzn_value_precede_chain_int_reif.mzn b/share/minizinc/std/fzn_value_precede_chain_int_reif.mzn new file mode 100644 index 0000000..949dbdf --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_chain_int_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_value_precede_chain_int_reif(array[int] of int: T, array[int] of var int: X, var bool: b) = + abort("Reified value_precede_chain constraint is not supported"); diff --git a/share/minizinc/std/fzn_value_precede_chain_set.mzn b/share/minizinc/std/fzn_value_precede_chain_set.mzn new file mode 100644 index 0000000..c6b719c --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_chain_set.mzn @@ -0,0 +1,6 @@ +include "value_precede.mzn"; + +predicate fzn_value_precede_chain_set(array[int] of int: c, array[int] of var set of int: x) = + forall (i in min(index_set(c)) + 1 .. max(index_set(c))) ( + value_precede(c[i - 1], c[i], x) + ); diff --git a/share/minizinc/std/fzn_value_precede_chain_set_reif.mzn b/share/minizinc/std/fzn_value_precede_chain_set_reif.mzn new file mode 100644 index 0000000..1f9cbe6 --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_chain_set_reif.mzn @@ -0,0 +1,2 @@ +predicate fzn_value_precede_chain_set_reif(array[int] of int: c, array[int] of var set of int: x, var bool: b) = + abort("Reified value_precede_chain is not supported"); diff --git a/share/minizinc/std/fzn_value_precede_int.mzn b/share/minizinc/std/fzn_value_precede_int.mzn new file mode 100644 index 0000000..501b238 --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_int.mzn @@ -0,0 +1,16 @@ +predicate fzn_value_precede_int(int: s, int: t, array[int] of var int: x) = + let { + int: imin = min(index_set(x)), + int: imax = max(index_set(x)), + array[imin..imax+1] of var bool: b + } in ( + forall (i in imin..imax) ( + let { + var bool: xis = (x[i] == s) + } in + (xis -> (b[i+1] == true)) + /\ ((not xis) -> (b[i] == b[i+1])) + /\ ((not b[i]) -> (x[i] != t)) + ) + /\ b[imin] == false + ); diff --git a/share/minizinc/std/fzn_value_precede_int_reif.mzn b/share/minizinc/std/fzn_value_precede_int_reif.mzn new file mode 100644 index 0000000..2aa8855 --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_int_reif.mzn @@ -0,0 +1,3 @@ +predicate fzn_value_precede_int_reif(int: s, int: t, + array[int] of var int: x, var bool: b) = + abort("Reified value_precede/3 for integers is not supported."); diff --git a/share/minizinc/std/fzn_value_precede_set.mzn b/share/minizinc/std/fzn_value_precede_set.mzn new file mode 100644 index 0000000..6d7a2ad --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_set.mzn @@ -0,0 +1,16 @@ +predicate fzn_value_precede_set(int: s, int: t, array[int] of var set of int: x) = + let { + int: imin = min(index_set(x)), + int: imax = max(index_set(x)), + array[imin..imax + 1] of var bool: b + } in ( + forall (i in imin..imax) ( + let { + var bool: xis = (s in x[i] /\ not (t in x[i])) + } in + (xis -> (b[i + 1] == true)) + /\ ((not xis) -> (b[i] == b[i + 1])) + /\ ((not b[i]) -> (s in x[i] \/ not (t in x[i]))) + ) + /\ b[imin] == false + ); diff --git a/share/minizinc/std/fzn_value_precede_set_reif.mzn b/share/minizinc/std/fzn_value_precede_set_reif.mzn new file mode 100644 index 0000000..e2c44e3 --- /dev/null +++ b/share/minizinc/std/fzn_value_precede_set_reif.mzn @@ -0,0 +1,3 @@ +predicate fzn_value_precede_set_reif(int: s, int: t, + array[int] of var set of int: x, var bool: b) = + abort("Reified value_precede/3 for sets is not supported."); diff --git a/share/minizinc/std/fzn_write.mzn b/share/minizinc/std/fzn_write.mzn new file mode 100644 index 0000000..753b7b3 --- /dev/null +++ b/share/minizinc/std/fzn_write.mzn @@ -0,0 +1,6 @@ +predicate fzn_write(array[int] of var int: I, var int: i, var int: v, + array[int] of var int: O) = + forall(j in index_set(I)) + (O[j] = if j = i then v else I[j] endif); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_write_reif.mzn b/share/minizinc/std/fzn_write_reif.mzn new file mode 100644 index 0000000..9eed158 --- /dev/null +++ b/share/minizinc/std/fzn_write_reif.mzn @@ -0,0 +1,6 @@ +predicate fzn_write_reif(array[int] of var int: I, var int: i, var int: v, + array[int] of var int: O, var bool: b) = + b <-> forall(j in index_set(I)) + (O[j] = if j = i then v else I[j] endif); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_writes.mzn b/share/minizinc/std/fzn_writes.mzn new file mode 100644 index 0000000..d9c3127 --- /dev/null +++ b/share/minizinc/std/fzn_writes.mzn @@ -0,0 +1,9 @@ +predicate fzn_writes(array[int] of var int: I, array[int] of var int: P, + array[int] of var int: V, array[int] of var int: O) = + forall(j in index_set(P))(O[P[j]] = V[j]) /\ + forall(i in index_set(I)) + (if forall(j in index_set(P))(P[j] != i) then + O[i] = I[i] + else true endif); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_writes_reif.mzn b/share/minizinc/std/fzn_writes_reif.mzn new file mode 100644 index 0000000..815a518 --- /dev/null +++ b/share/minizinc/std/fzn_writes_reif.mzn @@ -0,0 +1,12 @@ +predicate fzn_writes_reif(array[int] of var int: I, array[int] of var int: P, + array[int] of var int: V, array[int] of var int: O, + var bool: b) = + b <-> ( + forall(j in index_set(P))(O[P[j]] = V[j]) /\ + forall(i in index_set(I)) + (if forall(j in index_set(P))(P[j] != i) then + O[i] = I[i] + else true endif) + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_writes_seq.mzn b/share/minizinc/std/fzn_writes_seq.mzn new file mode 100644 index 0000000..aeff520 --- /dev/null +++ b/share/minizinc/std/fzn_writes_seq.mzn @@ -0,0 +1,12 @@ +include "arg_max.mzn"; + +predicate fzn_writes_seq(array[int] of var int: I, array[int] of var int: P, + array[int] of var int: V, array[int] of var int: O) = + forall (i in index_set(I)) ( + let { + array[1..length(V)+1] of var int: Vi = array1d(1..length(V)+1, reverse(V) ++ [I[i]]); + array[1..length(V)+1] of var bool: Pi = array1d(1..length(V)+1, reverse([P[j] == i | j in index_set(V)]) ++ [true]); + } in O[i] = Vi[arg_max(Pi)] + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_writes_seq_reif.mzn b/share/minizinc/std/fzn_writes_seq_reif.mzn new file mode 100644 index 0000000..19e7f10 --- /dev/null +++ b/share/minizinc/std/fzn_writes_seq_reif.mzn @@ -0,0 +1,13 @@ +include "arg_max.mzn"; + +predicate fzn_writes_seq_reif(array[int] of var int: I, array[int] of var int: P, + array[int] of var int: V, array[int] of var int: O, + var bool: b) = + b <-> forall (i in index_set(I)) ( + let { + array[1..length(V)+1] of var int: Vi = array1d(1..length(V)+1, reverse(V) ++ [I[i]]); + array[1..length(V)+1] of var bool: Pi = array1d(1..length(V)+1, reverse([P[j] == i | j in index_set(V)]) ++ [true]); + } in O[i] = Vi[arg_max(Pi)] + ); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_wst.mzn b/share/minizinc/std/fzn_wst.mzn new file mode 100644 index 0000000..9f13055 --- /dev/null +++ b/share/minizinc/std/fzn_wst.mzn @@ -0,0 +1,10 @@ +include "tree.mzn"; + +predicate fzn_wst(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + array[int] of var bool: es, var int: K) = + let { var 1..N: r; /* root of tree */ + array[1..N] of bool: ns = [true | n in 1..N]; } in + tree(N,E,from,to,r,ns,es) /\ + K = sum(e in 1..E)(es[e]*w[e]); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/fzn_wst_reif.mzn b/share/minizinc/std/fzn_wst_reif.mzn new file mode 100644 index 0000000..4a19aa7 --- /dev/null +++ b/share/minizinc/std/fzn_wst_reif.mzn @@ -0,0 +1,5 @@ +predicate fzn_wst_reif(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + array[int] of var bool: es, var int: K, var bool: b) = + abort("Reified wst constraint is not supported"); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/geost.mzn b/share/minizinc/std/geost.mzn new file mode 100644 index 0000000..6f744f4 --- /dev/null +++ b/share/minizinc/std/geost.mzn @@ -0,0 +1,217 @@ +include "fzn_geost.mzn"; +include "fzn_geost_reif.mzn"; +include "fzn_geost_bb.mzn"; +include "fzn_geost_bb_reif.mzn"; +include "fzn_geost_smallest_bb.mzn"; +include "fzn_geost_smallest_bb_reif.mzn"; +include "fzn_geost_nonoverlap_k.mzn"; +include "fzn_geost_nonoverlap_k_reif.mzn"; + +/** @group globals.packing + A global non-overlap constraint for \a k dimensional objects. It enforces that no two objects overlap. + +@param k: the number of dimensions +@param rect_size: the size of each box in \a k dimensios +@param rect_offset: the offset of each box from the base position in \a k dimensions + +@param shape: the set of rectangles defining the \p i-th shape. + +@param x: the base position of each object. \a x[\p i,\p j] is the position of object \p i in. dimension \p j. +@param kind: the shape used by each object. +*/ + +predicate geost( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind +) = + assert( + % Some sanity checks + index_set_1of2( rect_size ) = index_set_1of2(rect_offset) /\ + index_set_2of2( rect_size ) = 1..k /\ + index_set_2of2( rect_offset ) = 1..k /\ + index_set( shape ) = 1..length(shape) /\ + index_set_1of2( x ) = index_set(kind) /\ + index_set_2of2( x ) = 1..k /\ + forall(i in index_set(shape))( + shape[i] subset index_set_1of2(rect_size) + ), + % Error message + "geost: index sets of arguments are incorrect", + assert( + % More sanity checks + forall(i in index_set(shape))(card(shape[i]) > 0), + % Error message + "geost: sets in shape must be non-empty", + + fzn_geost(k, rect_size, rect_offset, shape, x, kind) + )); % End assert statements + + +/** @group globals.packing + A global non-overlap constraint for \a k dimensional objects. It enforces that no two objects overlap, and that all objects fit within a global \a k dimensional bounding box. + +@param k: the number of dimensions +@param rect_size: the size of each box in \a k dimensios +@param rect_offset: the offset of each box from the base position in \a k dimensions + +@param shape: the set of rectangles defining the \p i-th shape. + +@param x: the base position of each object. \a x[\p i,\p j] is the position of object \p i in dimension \p j. +@param kind: the shape used by each object. + +@param l: is an array of lower bounds, \a l[\p i] is the minimum bounding box for all objects in dimension \p i. +@param u: is an array of upper bounds, \a u[\p i] is the maximum bounding box for all objects in dimension \p i. +*/ + +predicate geost_bb( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + array[int ] of var int : l , + array[int ] of var int : u +) = + assert( + % Some sanity checks + index_set_1of2( rect_size ) = index_set_1of2(rect_offset) /\ + index_set_2of2( rect_size ) = 1..k /\ + index_set_2of2( rect_offset ) = 1..k /\ + index_set( shape ) = 1..length(shape) /\ + index_set_1of2( x ) = index_set(kind) /\ + index_set_2of2( x ) = 1..k /\ + forall(i in index_set(shape))( + shape[i] subset index_set_1of2(rect_size) + ), + % Error message + "geost_bb: index sets of arguments are incorrect", + assert( + % More sanity checks + forall(i in index_set(shape))(card(shape[i]) > 0), + % Error message + "geost_bb: sets in shape must be non-empty", + + assert( + % Sanity check + index_set(l) = 1..k /\ index_set(u) = 1..k, + % Error message + "geost_bb: index set of bounds arrays is not 1.." ++ show(k), + % Posting the geost constraint + fzn_geost_bb(k, rect_size, rect_offset, shape, x, kind, l, u) + ))); + +/** @group globals.packing + A global non-overlap constraint for \a k dimensional objects. It enforces that no two objects overlap, and that all objects fit within a global \a k dimensional bounding box. In addition, it enforces that the bounding box is the smallest one containing all objects, i.e., each of the \a 2k boundaries is touched by at least by one object. + +@param k: the number of dimensions +@param rect_size: the size of each box in \a k dimensios +@param rect_offset: the offset of each box from the base position in \a k dimensions + +@param shape: the set of rectangles defining the \p i-th shape. + +@param x: the base position of each object. \a x[\p i,\p j] is the position of object \p i in dimension \p j. +@param kind: the shape used by each object. + +@param l: is an array of lower bounds, \a l[\p i] is the minimum bounding box for all objects in dimension \p i. +@param u: is an array of upper bounds, \a u[\p i] is the maximum bounding box for all objects in dimension \p i. +*/ + +predicate geost_smallest_bb( + int : k , + array[int,int] of int : rect_size , + array[int,int] of int : rect_offset , + array[int ] of set of int : shape , + array[int,int] of var int : x , + array[int ] of var int : kind , + array[int ] of var int : l , + array[int ] of var int : u +) = + assert( + % Some sanity checks + index_set_1of2( rect_size ) = index_set_1of2(rect_offset) /\ + index_set_2of2( rect_size ) = 1..k /\ + index_set_2of2( rect_offset ) = 1..k /\ + index_set( shape ) = 1..length(shape) /\ + index_set_1of2( x ) = index_set(kind) /\ + index_set_2of2( x ) = 1..k /\ + forall(i in index_set(shape))( + shape[i] subset index_set_1of2(rect_size) + ), + % Error message + "geost_bb: index sets of arguments are incorrect", + assert( + % More sanity checks + forall(i in index_set(shape))(card(shape[i]) > 0), + % Error message + "geost_bb: sets in shape must be non-empty", + + % A few useful definitions + let { + set of int: DIMS = 1..k; + set of int: SHAPES = 1..length(shape); + set of int: OBJECTS = index_set(kind); + } in + % Two useful definitions + let { + set of int: DIMS = 1..k; + set of int: OBJECTS = index_set(kind); + } in ( + assert( + % Sanity check + index_set(l) = 1..k /\ index_set(u) = 1..k, + % Error message + "geost_bb: index set of bounds arrays is not 1.." ++ show(k), + % Posting the geost constraint + fzn_geost_smallest_bb(k, rect_size, rect_offset, shape, x, kind, l, u) + )))); + +/** @group globals.packing + A global non-overlap constraint for \a 2 dimensional objects. It enforces that no two objects overlap + and zero-length objects do not appear in the middle of other objects. + +@param x1: first coordinate of each object +@param w2: width in first dimension for each object +@param x2: second coordinate of each object +@param w2: width in second dimension for each object +*/ +predicate geost_nonoverlap_k( + array[int] of var int : x1, + array[int] of int : w1, + array[int] of var int : x2, + array[int] of int : w2 +) = + assert( + % Some sanity checks + index_set( x1 ) = index_set( w1 ) /\ + index_set( x1 ) = index_set( x2 ) /\ + index_set( x1 ) = index_set( w2 ), + % Error message + "geost_nonoverlap_k: index sets of arguments do not match", + % Non-overlap constraint + fzn_geost_nonoverlap_k(x1,w1,x2,w2) + ); + +test geost_nonoverlap_k( + array[int] of int: x1, + array[int] of int: w1, + array[int] of int: x2, + array[int] of int: w2 +) = + assert( + % Some sanity checks + index_set( x1 ) = index_set( w1 ) /\ + index_set( x1 ) = index_set( x2 ) /\ + index_set( x1 ) = index_set( w2 ), + % Error message + "geost_nonoverlap_k: index sets of arguments do not match", + % Non-overlap test + exists(j in index_set(x1))( + x1[j] + w1[j] <= x2[j] \/ x2[j] + w2[j] <= x1[j] + ) + ); + diff --git a/share/minizinc/std/global_cardinality.mzn b/share/minizinc/std/global_cardinality.mzn new file mode 100644 index 0000000..db39edc --- /dev/null +++ b/share/minizinc/std/global_cardinality.mzn @@ -0,0 +1,13 @@ +include "fzn_global_cardinality.mzn"; +include "fzn_global_cardinality_reif.mzn"; + +/** @group globals.counting + Requires that the number of occurrences of \a cover[\p i] in \a x is \a counts[\p i]. +*/ +predicate global_cardinality(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts) = + assert(index_set(cover) = index_set(counts), + "global_cardinality: cover and counts must have identical index sets", + fzn_global_cardinality(x, cover, counts) + ); diff --git a/share/minizinc/std/global_cardinality_closed.mzn b/share/minizinc/std/global_cardinality_closed.mzn new file mode 100644 index 0000000..4377cf9 --- /dev/null +++ b/share/minizinc/std/global_cardinality_closed.mzn @@ -0,0 +1,16 @@ +include "fzn_global_cardinality_closed.mzn"; +include "fzn_global_cardinality_closed_reif.mzn"; + +/** @group globals.counting + Requires that the number of occurences of \p i in \a x is \a counts[\p i]. + + The elements of \a x must take their values from \a cover. +*/ +predicate global_cardinality_closed(array[int] of var int: x, + array[int] of int: cover, + array[int] of var int: counts) = + assert(index_set(cover) = index_set(counts), + "global_cardinality_closed: " ++ + "cover and counts must have identical index sets", + fzn_global_cardinality_closed(x, cover, counts) + ); diff --git a/share/minizinc/std/global_cardinality_closed_fn.mzn b/share/minizinc/std/global_cardinality_closed_fn.mzn new file mode 100644 index 0000000..652e531 --- /dev/null +++ b/share/minizinc/std/global_cardinality_closed_fn.mzn @@ -0,0 +1,12 @@ +include "global_cardinality_closed.mzn"; + +/** @group globals.counting + Returns an array with number of occurences of \p i in \a x. + + The elements of \a x must take their values from \a cover. +*/ +function array[int] of var int: global_cardinality_closed(array[int] of var int: x, + array[int] of int: cover) :: promise_total = + let { array[index_set(cover)] of var 0..length(x): counts; + constraint global_cardinality_closed(x,cover,counts); } + in counts; diff --git a/share/minizinc/std/global_cardinality_fn.mzn b/share/minizinc/std/global_cardinality_fn.mzn new file mode 100644 index 0000000..01fb042 --- /dev/null +++ b/share/minizinc/std/global_cardinality_fn.mzn @@ -0,0 +1,10 @@ +include "global_cardinality.mzn"; + +/** @group globals.counting + Returns the number of occurrences of \a cover[\p i] in \a x. +*/ +function array[int] of var int: global_cardinality(array[int] of var int: x, + array[int] of int: cover) :: promise_total = + let { array[index_set(cover)] of var 0..length(x): counts; + constraint global_cardinality(x,cover,counts) } in + counts; diff --git a/share/minizinc/std/global_cardinality_low_up.mzn b/share/minizinc/std/global_cardinality_low_up.mzn new file mode 100644 index 0000000..75c26b9 --- /dev/null +++ b/share/minizinc/std/global_cardinality_low_up.mzn @@ -0,0 +1,12 @@ +include "fzn_global_cardinality_low_up.mzn"; +include "fzn_global_cardinality_low_up_reif.mzn"; + +/** @group globals.counting + Requires that for all \p i, the value \a cover[\p i] appears at least \a lbound[\p i] + and at most \a ubound[\p i] times in the array \a x. +*/ +predicate global_cardinality_low_up(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound) = + fzn_global_cardinality_low_up(x,cover,lbound,ubound); diff --git a/share/minizinc/std/global_cardinality_low_up_closed.mzn b/share/minizinc/std/global_cardinality_low_up_closed.mzn new file mode 100644 index 0000000..bed80eb --- /dev/null +++ b/share/minizinc/std/global_cardinality_low_up_closed.mzn @@ -0,0 +1,14 @@ +include "fzn_global_cardinality_low_up_closed.mzn"; +include "fzn_global_cardinality_low_up_closed_reif.mzn"; + +/** @group globals.counting + Requires that for all \p i, the value \a cover[\p i] appears at least \a lbound[\p i] + and at most \a ubound[\p i] times in the array \a x. + + The elements of \a x must take their values from \a cover. +*/ +predicate global_cardinality_low_up_closed(array[int] of var int: x, + array[int] of int: cover, + array[int] of int: lbound, + array[int] of int: ubound) = + fzn_global_cardinality_low_up_closed(x, cover, lbound, ubound); diff --git a/share/minizinc/std/globals.mzn b/share/minizinc/std/globals.mzn new file mode 100644 index 0000000..83db8e7 --- /dev/null +++ b/share/minizinc/std/globals.mzn @@ -0,0 +1,131 @@ +/*** + @groupdef globals Global constraints + + These constraints represent high-level modelling abstractions, for which + many solvers implement special, efficient inference algorithms. +*/ + +/*** + @groupdef globals.alldifferent All-Different and related constraints + + @groupdef globals.lexicographic Lexicographic constraints + + @groupdef globals.sort Sorting constraints + + @groupdef globals.channeling Channeling constraints + + @groupdef globals.counting Counting constraints + + @groupdef globals.packing Packing constraints + + @groupdef globals.scheduling Scheduling constraints + + @groupdef globals.graph Graph constraints + + @groupdef globals.extensional Extensional constraints (table, regular etc.) +*/ + +include "all_different.mzn"; +include "alldifferent_except_0.mzn"; +include "all_disjoint.mzn"; +include "all_equal.mzn"; +include "alternative.mzn"; +include "among.mzn"; +include "among_fn.mzn"; +include "arg_sort.mzn"; +include "arg_min.mzn"; +include "arg_max.mzn"; +include "at_least.mzn"; +include "at_most.mzn"; +include "at_most1.mzn"; +include "bin_packing.mzn"; +include "bin_packing_capa.mzn"; +include "bin_packing_load.mzn"; +include "bin_packing_load_fn.mzn"; +include "bounded_path.mzn"; +include "circuit.mzn"; +include "connected.mzn"; +include "cost_mdd.mzn"; +include "cost_regular.mzn"; +include "count.mzn"; +include "count_fn.mzn"; +include "count_eq.mzn"; +include "count_neq.mzn"; +include "count_geq.mzn"; +include "count_gt.mzn"; +include "count_leq.mzn"; +include "count_lt.mzn"; +include "cumulative.mzn"; +include "cumulative_opt.mzn"; +include "dag.mzn"; +include "decreasing.mzn"; +include "diffn.mzn"; +include "diffn_nonstrict.mzn"; +include "diffn_k.mzn"; +include "diffn_nonstrict_k.mzn"; +include "disjoint.mzn"; +include "disjunctive.mzn"; +include "disjunctive_strict.mzn"; +include "disjunctive_opt.mzn"; +include "disjunctive_strict_opt.mzn"; +include "distribute.mzn"; +include "distribute_fn.mzn"; +include "element.mzn"; +include "exactly.mzn"; +include "geost.mzn"; +include "global_cardinality.mzn"; +include "global_cardinality_fn.mzn"; +include "global_cardinality_closed.mzn"; +include "global_cardinality_closed_fn.mzn"; +include "global_cardinality_low_up.mzn"; +include "global_cardinality_low_up_closed.mzn"; +include "increasing.mzn"; +include "int_set_channel.mzn"; +include "inverse.mzn"; +include "inverse_fn.mzn"; +include "inverse_set.mzn"; +include "knapsack.mzn"; +include "lex_greater.mzn"; +include "lex_greatereq.mzn"; +include "lex_lesseq.mzn"; +include "lex_less.mzn"; +include "lex2.mzn"; +include "link_set_to_booleans.mzn"; +include "maximum.mzn"; +include "mdd.mzn"; +include "mdd_nondet.mzn"; +include "member.mzn"; +include "minimum.mzn"; +include "network_flow.mzn"; +include "neural_net.mzn"; +include "nvalue.mzn"; +include "nvalue_fn.mzn"; +include "partition_set.mzn"; +include "piecewise_linear.mzn"; +include "range.mzn"; +include "range_fn.mzn"; +include "reachable.mzn"; +include "regular.mzn"; +include "regular_nfa.mzn"; +include "regular_set.mzn"; +include "regular_regexp.mzn"; +include "roots.mzn"; +include "roots_fn.mzn"; +include "sliding_sum.mzn"; +include "sort.mzn"; +include "sort_fn.mzn"; +include "span.mzn"; +include "steiner.mzn"; +include "strictly_decreasing.mzn"; +include "strictly_increasing.mzn"; +include "strict_lex2.mzn"; +include "subcircuit.mzn"; +include "subgraph.mzn"; +include "sum_pred.mzn"; +include "sum_set.mzn"; +include "symmetric_all_different.mzn"; +include "table.mzn"; +include "tree.mzn"; +include "value_precede.mzn"; +include "value_precede_chain.mzn"; +include "weighted_spanning_tree.mzn"; diff --git a/share/minizinc/std/increasing.mzn b/share/minizinc/std/increasing.mzn new file mode 100644 index 0000000..b30b4b2 --- /dev/null +++ b/share/minizinc/std/increasing.mzn @@ -0,0 +1,21 @@ +include "increasing_bool.mzn"; +include "increasing_float.mzn"; +include "increasing_int.mzn"; +include "increasing_set.mzn"; + +/** @group globals.sort Requires that the array \a x is in increasing order (duplicates are allowed). */ +predicate increasing(array[int] of var bool: x) = + increasing_bool(x); + +/** @group globals.sort Requires that the array \a x is in increasing order (duplicates are allowed). */ +predicate increasing(array[int] of var float: x) = + increasing_float(x); + +/** @group globals.sort Requires that the array \a x is in increasing order (duplicates are allowed). */ +predicate increasing(array[int] of var int: x) = + increasing_int(x); + +/** @group globals.sort Requires that the array \a x is in increasing order (duplicates are allowed). */ +predicate increasing(array[int] of var set of int: x) = + increasing_set(x); + diff --git a/share/minizinc/std/increasing_bool.mzn b/share/minizinc/std/increasing_bool.mzn new file mode 100644 index 0000000..74a1a3e --- /dev/null +++ b/share/minizinc/std/increasing_bool.mzn @@ -0,0 +1,9 @@ +include "fzn_increasing_bool.mzn"; +include "fzn_increasing_bool_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate increasing_bool(array[int] of var bool: x) = + fzn_increasing_bool(x); diff --git a/share/minizinc/std/increasing_float.mzn b/share/minizinc/std/increasing_float.mzn new file mode 100644 index 0000000..6423d33 --- /dev/null +++ b/share/minizinc/std/increasing_float.mzn @@ -0,0 +1,9 @@ +include "fzn_increasing_float.mzn"; +include "fzn_increasing_float_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate increasing_float(array[int] of var float: x) = + fzn_increasing_float(x); diff --git a/share/minizinc/std/increasing_int.mzn b/share/minizinc/std/increasing_int.mzn new file mode 100644 index 0000000..70622d4 --- /dev/null +++ b/share/minizinc/std/increasing_int.mzn @@ -0,0 +1,9 @@ +include "fzn_increasing_int.mzn"; +include "fzn_increasing_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate increasing_int(array[int] of var int: x) = + fzn_increasing_int(x); diff --git a/share/minizinc/std/increasing_set.mzn b/share/minizinc/std/increasing_set.mzn new file mode 100644 index 0000000..d9212d3 --- /dev/null +++ b/share/minizinc/std/increasing_set.mzn @@ -0,0 +1,9 @@ +include "fzn_increasing_set.mzn"; +include "fzn_increasing_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in increasing order (duplicates are allowed). +%-----------------------------------------------------------------------------% + +predicate increasing_set(array[int] of var set of int: x) = + fzn_increasing_set(x); diff --git a/share/minizinc/std/int_set_channel.mzn b/share/minizinc/std/int_set_channel.mzn new file mode 100644 index 0000000..fe5244f --- /dev/null +++ b/share/minizinc/std/int_set_channel.mzn @@ -0,0 +1,12 @@ +include "fzn_int_set_channel.mzn"; +include "fzn_int_set_channel_reif.mzn"; + +/** @group globals.channeling + Requires that array of int variables \a x and array of set variables \a y + are related such that (\a x[\p i] = \p j) ↔ (\p i in \a y[\p j]). +*/ +predicate int_set_channel(array[int] of var int: x, + array[int] of var set of int: y) = + fzn_int_set_channel(x,y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/inverse.mzn b/share/minizinc/std/inverse.mzn new file mode 100644 index 0000000..aae6b8e --- /dev/null +++ b/share/minizinc/std/inverse.mzn @@ -0,0 +1,19 @@ +include "fzn_inverse.mzn"; +include "fzn_inverse_reif.mzn"; +include "analyse_all_different.mzn"; + +/** @group globals.channeling + Constrains two arrays of int variables, \a f and \a invf, to represent + inverse functions. All the values in each array must be within the index + set of the other array. +*/ +predicate inverse(array[int] of var int: f, + array[int] of var int: invf) = + analyse_all_different(f) /\ + analyse_all_different(invf) /\ + fzn_inverse(f, invf); + +predicate inverse_reif(array[int] of var int: f, + array[int] of var int: invf, + var bool: b) = + fzn_inverse_reif(f, invf, b); diff --git a/share/minizinc/std/inverse_fn.mzn b/share/minizinc/std/inverse_fn.mzn new file mode 100644 index 0000000..6b81236 --- /dev/null +++ b/share/minizinc/std/inverse_fn.mzn @@ -0,0 +1,10 @@ +include "inverse.mzn"; + +/** @group globals.channeling + Given a function \a f represented as an array, return the inverse function. +*/ +function array[int] of var int: inverse(array[int] of var int: f) = + let { + array[lb_array(f)..ub_array(f)] of var index_set(f): invf; + constraint inverse(f,invf); + } in invf; diff --git a/share/minizinc/std/inverse_set.mzn b/share/minizinc/std/inverse_set.mzn new file mode 100644 index 0000000..92accd8 --- /dev/null +++ b/share/minizinc/std/inverse_set.mzn @@ -0,0 +1,11 @@ +include "fzn_inverse_set.mzn"; +include "fzn_inverse_set_reif.mzn"; + +/** @group globals.channeling + Constrains two arrays of set of int variables, \a f and \a invf, so that + a \p j in f[\p i] iff \p i in invf[\p j]. All the values in each array's sets + must be within the index set of the other array. +*/ +predicate inverse_set(array[int] of var set of int: f, + array[int] of var set of int: invf) = + fzn_inverse_set(f,invf); diff --git a/share/minizinc/std/knapsack.mzn b/share/minizinc/std/knapsack.mzn new file mode 100644 index 0000000..ef7899c --- /dev/null +++ b/share/minizinc/std/knapsack.mzn @@ -0,0 +1,26 @@ +include "fzn_knapsack.mzn"; +include "fzn_knapsack_reif.mzn"; + +/** @group globals.packing +Requires that items are packed in a knapsack with certain weight and profit restrictions. + +Assumptions: + - Weights \a w and profits \a p must be non-negative + - \a w, \a p and \a x must have the same index sets + +@param w: weight of each type of item +@param p: profit of each type of item +@param x: number of items of each type that are packed +@param W: sum of sizes of all items in the knapsack +@param P: sum of profits of all items in the knapsack +*/ +predicate knapsack(array[int] of int: w, array[int] of int:p, + array[int] of var int:x, var int: W, var int: P) = + assert(index_set(w) = index_set(p) /\ index_set(w) = index_set(x), + "index set of weights must be equal to index set of profits and index set of items", + assert(lb_array(w) >= 0, + "weights must be non-negative", + assert(lb_array(p) >= 0, + "profits must be non-negative", + fzn_knapsack(w, p, x, W, P) + ))); diff --git a/share/minizinc/std/lex2.mzn b/share/minizinc/std/lex2.mzn new file mode 100644 index 0000000..2104b5d --- /dev/null +++ b/share/minizinc/std/lex2.mzn @@ -0,0 +1,9 @@ +include "fzn_lex2.mzn"; +include "fzn_lex2_reif.mzn"; + +/** @group globals.lexicographic + Require adjacent rows and adjacent columns in the array \a x to be + lexicographically ordered. Adjacent rows and adjacent columns may be equal. +*/ +predicate lex2(array[int, int] of var int: x) = + fzn_lex2(x); diff --git a/share/minizinc/std/lex_greater.mzn b/share/minizinc/std/lex_greater.mzn new file mode 100644 index 0000000..b97827b --- /dev/null +++ b/share/minizinc/std/lex_greater.mzn @@ -0,0 +1,33 @@ +include "lex_less.mzn"; + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically greater than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greater(array[int] of var bool: x, + array[int] of var bool: y) = + lex_less(y, x); + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically greater than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greater(array[int] of var int: x, + array[int] of var int: y) = + lex_less(y, x); + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically greater than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greater(array[int] of var float: x, + array[int] of var float: y) = + lex_less(y, x); + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically greater than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greater(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_less(y, x); diff --git a/share/minizinc/std/lex_greatereq.mzn b/share/minizinc/std/lex_greatereq.mzn new file mode 100644 index 0000000..52ade21 --- /dev/null +++ b/share/minizinc/std/lex_greatereq.mzn @@ -0,0 +1,33 @@ +include "lex_lesseq.mzn"; + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically greater than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greatereq(array[int] of var bool: x, + array[int] of var bool: y) = + lex_lesseq(y, x); + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically greater than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greatereq(array[int] of var int: x, + array[int] of var int: y) = + lex_lesseq(y, x); + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically greater than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greatereq(array[int] of var float: x, + array[int] of var float: y) = + lex_lesseq(y, x); + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically greater than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_greatereq(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_lesseq(y, x); diff --git a/share/minizinc/std/lex_less.mzn b/share/minizinc/std/lex_less.mzn new file mode 100644 index 0000000..c299d56 --- /dev/null +++ b/share/minizinc/std/lex_less.mzn @@ -0,0 +1,52 @@ +include "lex_less_bool.mzn"; +include "lex_less_float.mzn"; +include "lex_less_int.mzn"; +include "lex_less_set.mzn"; + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically less than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_less(array[int] of var bool: x, + array[int] of var bool: y) = + lex_less_bool(x, y); + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically less than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_less(array[int] of var int: x, + array[int] of var int: y) = + lex_less_int(x, y); + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically less than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_less(array[int] of var float: x, + array[int] of var float: y) = + lex_less_float(x, y); + +/** @group globals.lexicographic + Requires that the array \a x is strictly lexicographically less than + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_less(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_less_set(x, y); + +% Alternative names for the above. +% +predicate lex_lt(array[int] of var bool: x, array[int] of var bool: y) = + lex_less(x, y); + +predicate lex_lt(array[int] of var int: x, array[int] of var int: y) = + lex_less(x, y); + +predicate lex_lt(array[int] of var float: x, array[int] of var float: y) = + lex_less(x, y); + +predicate lex_lt(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_less(x, y); + diff --git a/share/minizinc/std/lex_less_bool.mzn b/share/minizinc/std/lex_less_bool.mzn new file mode 100644 index 0000000..37cab6c --- /dev/null +++ b/share/minizinc/std/lex_less_bool.mzn @@ -0,0 +1,18 @@ +include "lex_less.mzn"; +include "fzn_lex_less_bool.mzn"; +include "fzn_lex_less_bool_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_less_bool(array[int] of var bool: x, + array[int] of var bool: y) = + fzn_lex_less_bool(x, y); + +predicate lex_lt_bool(array[int] of var bool: x, + array[int] of var bool: y) = + lex_less(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_less_float.mzn b/share/minizinc/std/lex_less_float.mzn new file mode 100644 index 0000000..3f50402 --- /dev/null +++ b/share/minizinc/std/lex_less_float.mzn @@ -0,0 +1,18 @@ +include "lex_less.mzn"; +include "fzn_lex_less_float.mzn"; +include "fzn_lex_less_float_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_less_float(array[int] of var float: x, + array[int] of var float: y) = + fzn_lex_less_float(x, y); + +predicate lex_lt_float(array[int] of var float: x, + array[int] of var float: y) = + lex_less(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_less_int.mzn b/share/minizinc/std/lex_less_int.mzn new file mode 100644 index 0000000..6f2fbcd --- /dev/null +++ b/share/minizinc/std/lex_less_int.mzn @@ -0,0 +1,18 @@ +include "lex_less.mzn"; +include "fzn_lex_less_int.mzn"; +include "fzn_lex_less_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_less_int(array[int] of var int: x, + array[int] of var int: y) = + fzn_lex_less_int(x, y); + +predicate lex_lt_int(array[int] of var int: x, + array[int] of var int: y) = + lex_less(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_less_set.mzn b/share/minizinc/std/lex_less_set.mzn new file mode 100644 index 0000000..de768c2 --- /dev/null +++ b/share/minizinc/std/lex_less_set.mzn @@ -0,0 +1,18 @@ +include "lex_less.mzn"; +include "fzn_lex_less_set.mzn"; +include "fzn_lex_less_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is strictly lexicographically less than array 'y'. +% Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_less_set(array[int] of var set of int: x, + array[int] of var set of int: y) = + fzn_lex_less_set(x, y); + +predicate lex_lt_set(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_less(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_lesseq.mzn b/share/minizinc/std/lex_lesseq.mzn new file mode 100644 index 0000000..f6df4d1 --- /dev/null +++ b/share/minizinc/std/lex_lesseq.mzn @@ -0,0 +1,52 @@ +include "lex_lesseq_bool.mzn"; +include "lex_lesseq_float.mzn"; +include "lex_lesseq_int.mzn"; +include "lex_lesseq_set.mzn"; + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically less than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_lesseq(array[int] of var bool: x, + array[int] of var bool: y) = + lex_lesseq_bool(x, y); + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically less than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_lesseq(array[int] of var float: x, + array[int] of var float: y) = + lex_lesseq_float(x, y); + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically less than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_lesseq(array[int] of var int: x, + array[int] of var int: y) = + lex_lesseq_int(x, y); + +/** @group globals.lexicographic + Requires that the array \a x is lexicographically less than or equal to + array \a y. Compares them from first to last element, regardless of indices. +*/ +predicate lex_lesseq(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_lesseq_set(x, y); + +% Alternative names for the above. +% +predicate lex_leq(array[int] of var bool: x, array[int] of var bool: y) = + lex_lesseq(x, y); + +predicate lex_leq(array[int] of var int: x, array[int] of var int: y) = + lex_lesseq(x, y); + +predicate lex_leq(array[int] of var float: x, array[int] of var float: y) = + lex_lesseq(x, y); + +predicate lex_leq(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_lesseq(x, y); + diff --git a/share/minizinc/std/lex_lesseq_bool.mzn b/share/minizinc/std/lex_lesseq_bool.mzn new file mode 100644 index 0000000..84697fb --- /dev/null +++ b/share/minizinc/std/lex_lesseq_bool.mzn @@ -0,0 +1,17 @@ +include "fzn_lex_lesseq_bool.mzn"; +include "fzn_lex_lesseq_bool_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_lesseq_bool(array[int] of var bool: x, + array[int] of var bool: y) = + fzn_lex_lesseq_bool(x, y); + +predicate lex_leq_bool(array[int] of var bool: x, + array[int] of var bool: y) = + lex_lesseq_bool(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_lesseq_float.mzn b/share/minizinc/std/lex_lesseq_float.mzn new file mode 100644 index 0000000..95310d3 --- /dev/null +++ b/share/minizinc/std/lex_lesseq_float.mzn @@ -0,0 +1,17 @@ +include "fzn_lex_lesseq_float.mzn"; +include "fzn_lex_lesseq_float_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_lesseq_float(array[int] of var float: x, + array[int] of var float: y) = + fzn_lex_lesseq_float(x, y); + +predicate lex_leq_float(array[int] of var float: x, + array[int] of var float: y) = + lex_lesseq_float(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_lesseq_int.mzn b/share/minizinc/std/lex_lesseq_int.mzn new file mode 100644 index 0000000..8ebce30 --- /dev/null +++ b/share/minizinc/std/lex_lesseq_int.mzn @@ -0,0 +1,17 @@ +include "fzn_lex_lesseq_int.mzn"; +include "fzn_lex_lesseq_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_lesseq_int(array[int] of var int: x, + array[int] of var int: y) = + fzn_lex_lesseq_int(x, y); + +predicate lex_leq_int(array[int] of var int: x, + array[int] of var int: y) = + lex_lesseq_int(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/lex_lesseq_set.mzn b/share/minizinc/std/lex_lesseq_set.mzn new file mode 100644 index 0000000..5bfef91 --- /dev/null +++ b/share/minizinc/std/lex_lesseq_set.mzn @@ -0,0 +1,17 @@ +include "fzn_lex_lesseq_set.mzn"; +include "fzn_lex_lesseq_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is lexicographically less than or equal to +% array 'y'. Compares them from first to last element, regardless of indices +%-----------------------------------------------------------------------------% + +predicate lex_lesseq_set(array[int] of var set of int: x, + array[int] of var set of int: y) = + fzn_lex_lesseq_set(x, y); + +predicate lex_leq_set(array[int] of var set of int: x, + array[int] of var set of int: y) = + lex_lesseq_set(x, y); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/link_set_to_booleans.mzn b/share/minizinc/std/link_set_to_booleans.mzn new file mode 100644 index 0000000..e8e463f --- /dev/null +++ b/share/minizinc/std/link_set_to_booleans.mzn @@ -0,0 +1,14 @@ +include "fzn_link_set_to_booleans.mzn"; +include "fzn_link_set_to_booleans_reif.mzn"; + +/** @group globals.channeling + Constrain the array of Booleans \a b to be a representation of the set \a s: + \p i in \a s ↔ \a b[\p i]. + + The index set of \a b must be a superset of the possible values of \a s. +*/ +predicate link_set_to_booleans(var set of int: s, array[int] of var bool: b) = + assert(ub(s) subset index_set(b), + "link_set_to_booleans: the index set of b must be a superset of the possible values of s", + fzn_link_set_to_booleans(s,b) + ); diff --git a/share/minizinc/std/maximum.mzn b/share/minizinc/std/maximum.mzn new file mode 100644 index 0000000..6f28d7c --- /dev/null +++ b/share/minizinc/std/maximum.mzn @@ -0,0 +1,13 @@ +/** @group globals Constrains \a m to be the maximum of the values in \a x. + + Assumptions: |\a x| > 0. +*/ +predicate maximum(var int: m, array[int] of var int: x) = + array_int_maximum(m, x); + +/** @group globals Constrains \a m to be the maximum of the values in \a x. + + Assumptions: |\a x| > 0. +*/ +predicate maximum(var float: m, array[int] of var float: x) = + array_float_maximum(m, x); diff --git a/share/minizinc/std/mdd.mzn b/share/minizinc/std/mdd.mzn new file mode 100644 index 0000000..351dd88 --- /dev/null +++ b/share/minizinc/std/mdd.mzn @@ -0,0 +1,60 @@ +include "fzn_mdd.mzn"; +include "fzn_mdd_reif.mzn"; + +/** @group globals.extensional + Requires that \a x defines a path from root to true node T through the (deterministic) MDD defined by + + \a N is the number of nodes, the root node is node 1 + \a level is the level of each node, the root is level 1, T is level \a length(x)+1 + \a E is the number of edges + \a from is the leaving node (1..\a N)for each edge + \a label is the set of values of the \a x variable for each edge + \a to is the entering node for each edge, where 0 = T node + + The MDD must be deterministic, i.e., there cannot be two edges + with the same label leaving the same node. +*/ +predicate mdd(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % possible values of variable + array[int] of int: to % edge entering node 0..N where 0 = T node + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of int: levele = array1d(0..N,[L+1]++level); } in + assert(index_set(level) = NODE, + "mdd: third argument must be of length N = \(N)") /\ + assert(index_set(from) = EDGE, + "mdd: 5th argument must be of length E = \(E)") /\ + assert(index_set(to) = EDGE, + "mdd: 7th argument must be of length E = \(E)") /\ + forall(e in EDGE)(assert(from[e] in NODE, + "mdd: from[\(e)] must be in \(NODE)")) /\ + forall(e in EDGE)(assert(to[e] in 0..N, + "mdd: to[\(e)] must be in 0..\(N)")) /\ + forall(e in EDGE)(assert(level[from[e]]+1 = levele[to[e]], + "mdd level of from[\(e)] = \(level[from[e]])" ++ + "must be 1 less than level of to[\(e)] = \(levele[to[e]])")) /\ + forall(e1,e2 in EDGE where e1 < e2 /\ from[e1] = from[e2]) + (assert(label[e1] intersect label[e2] = {}, + "mdd: Two edges \(e1) and \(e2) leaving node \(from[e1]) with overlapping labels")) /\ + fzn_mdd(x, N, level, E, from, label, to); + +% Example consider an MDD over 3 variables +% 5 nodes and 12 edges +% level 1 root = 1 +% level 2 2 3 +% level 3 4 5 +% level 4 T +% with edges (from,label,to) given by +% (1,1,2), (1,2,3), (1,3,2) +% (2,2,4), (2,3,5) +% (3,3,4), (3,2,5) +% (4,1,0), (4,5,0) +% (5,2,0), (5,4,0), (5,6,0) +% this is defined by the call +% mdd([x1,x2,x3],5,[1,2,2,3,3],12,[1,1,1,2,2,3,3,4,4,5,5,5],[1,3,2,2,3,3,2,1,5,2,4,6],[2,2,3,4,5,4,5,0,0,0,0,0]) diff --git a/share/minizinc/std/mdd_nondet.mzn b/share/minizinc/std/mdd_nondet.mzn new file mode 100644 index 0000000..b7582fa --- /dev/null +++ b/share/minizinc/std/mdd_nondet.mzn @@ -0,0 +1,57 @@ +include "fzn_mdd_nondet.mzn"; +include "fzn_mdd_nondet_reif.mzn"; + +/** @group globals.extensional + Requires that \a x defines a path from root to true node T through the (nondeterministic) MDD defined by + + \a N is the number of nodes, the root node is node 1 + \a level is the level of each node, the root is level 1, T is level \a length(x)+1 + \a E is the number of edges + \a from is the leaving node (1..\a N)for each edge + \a label is the set of values of the \a x variable for each edge + \a to is the entering node for each edge, where 0 = T node + + The MDD can be nondeterministic, i.e., there can be two edges + with the same label leaving the same node. +*/ +predicate mdd_nondet(array[int] of var int: x, % variables constrained by MDD + int: N, % number of nodes root is node 1 + array[int] of int: level, % level of each node root is level 1, T is level length(x)+1 + int: E, % number of edges + array[int] of int: from, % edge leaving node 1..N + array[int] of set of int: label, % possible values of variable + array[int] of int: to % edge entering node 0..N where 0 = T node + ) = + let { set of int: NODE = 1..N; + set of int: EDGE = 1..E; + int: L = length(x); + array[0..N] of int: levele = array1d(0..N,[L+1]++level); } in + assert(index_set(level) = NODE, + "mdd: third argument must be of length N = \(N)") /\ + assert(index_set(from) = EDGE, + "mdd: 5th argument must be of length E = \(E)") /\ + assert(index_set(to) = EDGE, + "mdd: 7th argument must be of length E = \(E)") /\ + forall(e in EDGE)(assert(from[e] in NODE, + "mdd: from[\(e)] must be in \(NODE)")) /\ + forall(e in EDGE)(assert(to[e] in 0..N, + "mdd: to[\(e)] must be in 0..\(N)")) /\ + forall(e in EDGE)(assert(level[from[e]]+1 = levele[to[e]], + "mdd level of from[\(e)] = \(level[from[e]])" ++ + "must be 1 less than level of to[\(e)] = \(levele[to[e]])")) /\ + fzn_mdd_nondet(x, N, level, E, from, label, to); + +% Example consider an MDD over 3 variables +% 5 nodes and 12 edges +% level 1 root = 1 +% level 2 2 3 +% level 3 4 5 +% level 4 T +% with edges (from,label,to) given by +% (1,1,2), (1,2,3), (1,3,2) +% (2,2,4), (2,3,5) +% (3,3,4), (3,2,5) +% (4,1,0), (4,5,0) +% (5,2,0), (5,4,0), (5,6,0) +% this is defined by the call +% mdd([x1,x2,x3],5,[1,2,2,3,3],12,[1,1,1,2,2,3,3,4,4,5,5,5],[1,3,2,2,3,3,2,1,5,2,4,6],[2,2,3,4,5,4,5,0,0,0,0,0]) diff --git a/share/minizinc/std/member.mzn b/share/minizinc/std/member.mzn new file mode 100644 index 0000000..c7805ed --- /dev/null +++ b/share/minizinc/std/member.mzn @@ -0,0 +1,35 @@ +include "member_bool.mzn"; +include "member_float.mzn"; +include "member_int.mzn"; +include "member_set.mzn"; +include "set_member.mzn"; + +/** @group globals + Requires that \a y occurs in the array \a x. +*/ +predicate member(array[int] of var bool: x, var bool: y) = + member_bool(x, y); + +/** @group globals + Requires that \a y occurs in the array \a x. +*/ +predicate member(array[int] of var float: x, var float: y) = + member_float(x, y); + +/** @group globals + Requires that \a y occurs in the array \a x. +*/ +predicate member(array[int] of var int: x, var int: y) = + member_int(x, y); + +/** @group globals + Requires that \a y occurs in the array \a x. +*/ +predicate member(array[int] of var set of int: x, var set of int: y) = + member_set(x, y); + +/** @group globals + Requires that \a y occurs in the set \a x. +*/ +predicate member(var set of int: x, var int: y) = + set_member(x, y); diff --git a/share/minizinc/std/member_bool.mzn b/share/minizinc/std/member_bool.mzn new file mode 100644 index 0000000..644b3da --- /dev/null +++ b/share/minizinc/std/member_bool.mzn @@ -0,0 +1,9 @@ +include "fzn_member_bool.mzn"; +include "fzn_member_bool_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate member_bool(array[int] of var bool: x, var bool: y) = + fzn_member_bool(x, y); diff --git a/share/minizinc/std/member_float.mzn b/share/minizinc/std/member_float.mzn new file mode 100644 index 0000000..3d70e76 --- /dev/null +++ b/share/minizinc/std/member_float.mzn @@ -0,0 +1,9 @@ +include "fzn_member_float.mzn"; +include "fzn_member_float_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate member_float(array[int] of var float: x, var float: y) = + fzn_member_float(x, y); diff --git a/share/minizinc/std/member_int.mzn b/share/minizinc/std/member_int.mzn new file mode 100644 index 0000000..396e622 --- /dev/null +++ b/share/minizinc/std/member_int.mzn @@ -0,0 +1,9 @@ +include "fzn_member_int.mzn"; +include "fzn_member_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate member_int(array[int] of var int: x, var int: y) = + fzn_member_int(x, y); diff --git a/share/minizinc/std/member_set.mzn b/share/minizinc/std/member_set.mzn new file mode 100644 index 0000000..648800b --- /dev/null +++ b/share/minizinc/std/member_set.mzn @@ -0,0 +1,9 @@ +include "fzn_member_set.mzn"; +include "fzn_member_set_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate member_set(array[int] of var set of int: x, var set of int: y) = + fzn_member_set(x, y); diff --git a/share/minizinc/std/minimum.mzn b/share/minizinc/std/minimum.mzn new file mode 100644 index 0000000..1633a44 --- /dev/null +++ b/share/minizinc/std/minimum.mzn @@ -0,0 +1,13 @@ +/** @group globals Constrains \a m to be the minimum of the values in \a x. + + Assumptions: |\a x| > 0. +*/ +predicate minimum(var float: m, array[int] of var float: x) = + array_float_minimum(m, x); + +/** @group globals Constrains \a m to be the minimum of the values in \a x. + + Assumptions: |\a x| > 0. +*/ +predicate minimum(var int: m, array[int] of var int: x) = + array_int_minimum(m, x); diff --git a/share/minizinc/std/mznasm_builtins.mzn b/share/minizinc/std/mznasm_builtins.mzn new file mode 100644 index 0000000..32240d9 --- /dev/null +++ b/share/minizinc/std/mznasm_builtins.mzn @@ -0,0 +1,15 @@ +/** @group flatzinc.ann Mark declaration as being stored in register \a r */ +ann: global_register(int: r); + +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \bigwedge_i \a as[i]\) */ +predicate array_bool_and(array[int] of var bool: as, var bool: r); +/** @group flatzinc.bool Constrains \( \a r \leftrightarrow \bigvee_i \a as[i]\) */ +predicate array_bool_or(array[int] of var bool: as, var bool: r); +/** @group flatzinc.bool Constrains \( \bigvee_i \a as[i] \lor \bigvee_j \lnot \a bs[j] \) */ +predicate bool_clause(array[int] of var bool: as, array[int] of var bool: bs); +/** @group flatzinc.two Reified clause constraint. + Constrains \( \a b \leftrightarrow \bigvee_i \a as[i] \lor \bigvee_j \lnot \a bs[j] \) + */ +predicate bool_clause_reif(array[int] of var bool: as, array[int] of var bool: bs, var bool: b); +/** @group flatzinc.int Constrains \( \a c = \sum_i \a as[i]*\a bs[i] \) */ +predicate int_lin_eq(array[int] of int: as, array[int] of var int: bs, int: c); diff --git a/share/minizinc/std/mzncc_builtins.mzn b/share/minizinc/std/mzncc_builtins.mzn new file mode 100644 index 0000000..fe736af --- /dev/null +++ b/share/minizinc/std/mzncc_builtins.mzn @@ -0,0 +1,298 @@ +/*** + @groupdef builtins.logic Logical operations + + Logical operations are the standard operators of Boolean logic. +*/ + +/** @group builtins.logic Return truth value of the negation of \a x */ +function bool: op_not(bool: x) = not x; +/** @group builtins.logic Return truth value of the negation of \a x */ +function var bool: op_not(var bool: x) ::promise_total = + let { + var bool: y; + constraint bool_not(x, y); + } in y; + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function bool: op_equals(array[$T] of int: x,array[$T] of int: y) = + let { + array[int] of int: xx = array1d(x); + array[int] of int: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function var bool: op_equals(array[$T] of var int: x, array[$T] of var int: y) = + let { + array[int] of var int: xx = array1d(x); + array[int] of var int: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function bool: op_equals(array[$T] of bool: x, array[$T] of bool: y) = + let { + array[int] of bool: xx = array1d(x); + array[int] of bool: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + +/** @group builtins.compare Return if array \a x is equal to array \a y */ +function var bool: op_equals(array[$T] of var bool: x,array[$T] of var bool: y) = + let { + array[int] of var bool: xx = array1d(x); + array[int] of var bool: yy = array1d(y); + } in + assert(index_sets_agree(x,y), "array index sets do not match", + forall (i in index_set(xx)) (xx[i]=yy[i]) + ); + + +/*** + @groupdef builtins.arithmetic Arithmetic Builtins + + These builtins implement arithmetic operations. +*/ + +predicate int_minus(var int:x, var int:y, var int:z); + +/** @group builtins.arithmetic Return \a -x */ +function int: op_minus(int: x) ::promise_total = -x; +/** @group builtins.arithmetic Return \a x - \a y */ +function var int: op_minus(var int: x) ::promise_total = + let { + var int: y; + constraint int_minus(0,x,y); + } in y; + +/** @group builtins.arithmetic Return \a x - \a y */ +function int: op_minus(int: x, int: y) ::promise_total = x - y; +/** @group builtins.arithmetic Return \a x - \a y */ +function var int: op_minus(var int: x, int: y) ::promise_total = + if y = 0 then + x + else + let { + var int: z; + constraint int_minus(x,y,z); + } in z + endif; +/** @group builtins.arithmetic Return \a x - \a y */ +function var int: op_minus(var int: x, var int: y) ::promise_total = + let { + var int: z; + constraint int_minus(x, y, z); + } in z; +/** @group builtins.arithmetic Return \a x + \a y */ +function int: op_plus(int: x, int: y) = x + y; +/** @group builtins.arithmetic Return \a x + \a y */ +function var int: op_plus(int: x, var int: y) ::promise_total = + if x = 0 then + y + else + let { + var int: z; + constraint int_plus(x, y, z); + } in z + endif; +/** @group builtins.arithmetic Return \a x + \a y */ +function var int: op_plus(var int: x, int: y) = op_plus(y, x); +/** @group builtins.arithmetic Return \a x + \a y */ +function var int: op_plus(var int: x, var int: y) ::promise_total = + let { + var int: z; + constraint int_plus(x, y, z); + } in z; + +/** @group builtins.arithmetic Return \a x * \a y */ +function int: op_times(int: x, int: y) = x * y; +function var int: op_times(int: x, var int: y) ::promise_total = + if x = 1 then + y + else + let { + var int: z; + constraint int_times(x, y, z); + } in z + endif; +function var int: op_times(var int: x, int: y) = op_times(y, x); +/** @group builtins.arithmetic Return \a x * \a y */ +function var int: op_times(var int: x, var int: y) ::promise_total = + let { + var int: z; + constraint int_times(x, y, z); + } in z; + +/* Negated versions of FlatZinc builtins */ +predicate int_lin_le_neg(array[int] of int: as, array[int] of var int: bs, int: c) = + int_lin_le([-a | a in as], bs, -c-1); +predicate int_lin_eq_neg(array[int] of int: as, array[int] of var int: bs, int: c) = + int_lin_ne(as, bs, c); + +predicate pre_int_lin_eq(array[int] of int: as, array[int] of var int: bs, int: c) = if length(as)=0 then c=0 else int_lin_eq(as,bs,c) endif; + +predicate pre_int_lin_eq_neg(array[int] of int: as, array[int] of var int: bs, int: c) = if length(as)=0 then c!=0 else int_lin_ne(as,bs,c) endif; + +predicate pre_int_lin_le(array[int] of int: as, array[int] of var int: bs, int: c) = if length(as)=0 then 0<=c else int_lin_le(as,bs,c) endif; + +predicate pre_int_lin_le_neg(array[int] of int: as, array[int] of var int: bs, int: c) = if length(as)=0 then 0>c else int_lin_le_neg(as,bs,c) endif; + + +/** @group builtins.arithmetic Return result of integer division \a x / \a y */ +function int: op_int_division(int: x, int: y) = x div y; +/** @group builtins.arithmetic Return result of integer division \a x / \a y */ +function var int: op_int_division(var int: x, var int: y) = + if mzn_in_root_context(y) \/ not (0 in dom(y)) then div_t(x,y) else + let { constraint y != 0 } in div_mt(x,y) endif; + +/** @group builtins.arithmetic Return result of integer division \a x / \a y */ +function int: op_modulus(int: x, int: y) = x mod y; +/** @group builtins.arithmetic Return result of integer division \a x / \a y */ +function var int: op_modulus(var int: x, var int: y) = + if mzn_in_root_context(y) \/ not (0 in dom(y)) then mod_t(x,y) else + let { constraint y != 0 } in mod_mt(x,y) endif; + +/** @group builtins.arithmetic Return result of floating point division \a x / \a y */ +function float: op_float_division(float: x, float: y) = x / y; +/** @group builtins.arithmetic Return result of floating point division \a x / \a y */ +function var float: op_float_division(var float: x, var float: y) = + if mzn_in_root_context(y) \/ lb(y) > 0.0 \/ ub(y) < 0.0 then fldiv_t(x,y) else + let { constraint y != 0.0 } in fldiv_mt(x,y) endif; + + +function int: internal_max(array[$U] of int: x); +function int: internal_set_max(set of int: x); +function bool: internal_max(array[$U] of bool: x); +/** @group builtins.arithmetic Return maximum of \a x and \a y */ +function $T: max($T: x, $T: y) = + if x > y then + x + else + y + endif; +/** @group builtins.arithmetic Return maximum of elements in set \a x */ +function int: max(set of int: x) = internal_set_max(x); +/** @group builtins.arithmetic Return maximum of elements in array \a x */ +function int: max(array[$U] of int: x) = internal_max(array1d(x)); +/** @group builtins.arithmetic Return maximum of elements in array \a x */ +function bool: max(array[$U] of bool: x) = internal_max(array1d(x)); + +function int: internal_min(array[$U] of int: x); +function int: internal_set_min(set of int: x); +function bool: internal_min(array[$U] of bool: x); +/** @group builtins.arithmetic Return minimum of \a x and \a y */ +function $T: min($T: x, $T: y) = + if x < y then + x + else + y + endif; +function int: min(set of int: x) = internal_set_min(x); +function int: min(array[$U] of int: x) = internal_min(array1d(x)); +function bool: min(array[$U] of bool: x) = internal_min(array1d(x)); + +predicate int_sum(array[int] of var int: xs, var int: x); +/** @group builtins.arithmetic Return sum of elements in array \a x */ +function var int: sum_cc(array[$T] of var int: x) ::promise_total = + let { + array[int] of var int: xs = array1d(x); + var int: res; + constraint int_sum(xs, res); + } in res; + +function int: abs(int: x) = + if x<0 then + -x + else + x + endif; + +% --------------- + +/*** + @groupdef builtins.ifthenelse Conditionals + + These functions implement conditional (if-then-else-endif) constraints. +*/ + +/** @group builtins.ifthenelse Conditional constraint \(\{\a c[i]\land\not\exists \a c[1..i-1]\ \rightarrow\ y=x[i] \}\) + + This constraint is generated by the compiler for if-then-else expressions. + The last entry in the \a c array is always the constant true, corresponding + to the else case. +*/ +function var int: if_then_else(array[int] of var bool: c, array[int] of int: x) ::promise_total = + let { + var dom_array(x): y; + constraint fzn_if_then_else_int(c, x, y); + } in y; + +/** @group builtins.ifthenelse Conditional constraint \(\{\a c[i]\land\not\exists \a c[1..i-1]\ \rightarrow\ y=x[i] \}\) + + This constraint is generated by the compiler for if-then-else expressions. + The last entry in the \a c array is always the constant true, corresponding + to the else case. +*/ +function var int: if_then_else(array[int] of var bool: c, array[int] of var int: x) ::promise_total = + let { + var dom_array(x): y; + constraint fzn_if_then_else_var_int(c, x, y); + } in y; + +/** @group builtins.ifthenelse Conditional constraint \(\{\a c[i]\land\not\exists \a c[1..i-1]\ \rightarrow\ y=x[i] \}\) + + This constraint is generated by the compiler for if-then-else expressions. + The last entry in the \a c array is always the constant true, corresponding + to the else case. +*/ +function var bool: if_then_else(array[int] of var bool: c, array[int] of bool: x) ::promise_total = + let { + var bool: y; + constraint fzn_if_then_else_bool(c, x, y); + } in y; + +/** @group builtins.ifthenelse Conditional constraint \(\{\a c[i]\land\not\exists \a c[1..i-1]\ \rightarrow\ y=x[i] \}\) + + This constraint is generated by the compiler for if-then-else expressions. + The last entry in the \a c array is always the constant true, corresponding + to the else case. +*/ +function var bool: if_then_else(array[int] of var bool: c, array[int] of var bool: x) ::promise_total = + let { + var bool: y; + constraint fzn_if_then_else_var_bool(c, x, y); + } in y; + +/** @group builtins.ifthenelse Conditional partiality constraint + + This constraint is generated by the compiler for if-then-else expressions + with potentially undefined cases. + The last entry in the \a c array is always the constant true, corresponding + to the else case. + The \a d[i] variable represents whether case + \p i is defined. Constrains that if \b is defined, then the selected case must be defined, and + if the selected case is undefined, then \a b must be undefined. +*/ +function var bool: if_then_else_partiality(array[int] of var bool: c, array[int] of var bool: d) ::promise_total = + let { + var bool: b; + constraint fzn_if_then_else_partiality(c, d, b); + } in b; + + + +% Include solver-specific redefinitions for any FlatZinc built-ins. +% +include "fzn_if_then_else_int.mzn"; +include "fzn_if_then_else_var_int.mzn"; +include "fzn_if_then_else_bool.mzn"; +include "fzn_if_then_else_var_bool.mzn"; +include "fzn_if_then_else_partiality.mzn"; +% Include interpreter definitions always to be included +include "mznasm_builtins.mzn"; diff --git a/share/minizinc/std/network_flow.mzn b/share/minizinc/std/network_flow.mzn new file mode 100644 index 0000000..85cef44 --- /dev/null +++ b/share/minizinc/std/network_flow.mzn @@ -0,0 +1,55 @@ +include "fzn_network_flow.mzn"; +include "fzn_network_flow_reif.mzn"; +include "fzn_network_flow_cost.mzn"; +include "fzn_network_flow_cost_reif.mzn"; + +/** @group globals + Defines a network flow constraint. + +@param arc: a directed arc of the flow network. Arc \p i connects node \a arc[\p i,1] to node \a arc[\p i,2]. +@param balance: the difference between input and output flow for each node. +@param flow: the flow going through each arc. +*/ + +predicate network_flow(array[int,1..2] of int: arc, + array[int] of int: balance, + array[int] of var int: flow) = + + let { + set of int: ARCS = index_set_1of2(arc); + set of int: NODES = index_set(balance); + } in + assert ( + ARCS == index_set(flow) /\ + lb_array(arc) >= min(NODES) /\ + ub_array(arc) <= max(NODES), + "network_flow: wrong sizes of input array parameters", + fzn_network_flow(arc, balance, flow) + ); + +/** @group globals + Defines a network flow constraint with cost. + +@param arc: a directed arc of the flow network. Arc \p i connects node \a arc[\p i,1] to node \a arc[\p i,2]. +@param balance: the difference between input and output flow for each node. +@param weight: the unit cost of the flow through the arc. +@param flow: the flow going through each arc. +@param cost: the overall cost of the flow. +*/ +predicate network_flow_cost(array[int,1..2] of int: arc, + array[int] of int: balance, + array[int] of int: weight, + array[int] of var int: flow, var int: cost) = + + let { + set of int: ARCS = index_set_1of2(arc); + set of int: NODES = index_set(balance); + } in + assert ( + ARCS == index_set(flow) /\ + ARCS == index_set(weight) /\ + lb_array(arc) >= min(NODES) /\ + ub_array(arc) <= max(NODES), + "network_flow: wrong sizes of input array parameters", + fzn_network_flow_cost(arc, balance, weight, flow, cost) + ); diff --git a/share/minizinc/std/neural_net.mzn b/share/minizinc/std/neural_net.mzn new file mode 100644 index 0000000..412157f --- /dev/null +++ b/share/minizinc/std/neural_net.mzn @@ -0,0 +1,76 @@ +include "fzn_neural_net.mzn"; +include "fzn_neural_net_reif.mzn"; + + +enum NEURON_TYPE = { NT_RELU, NT_STEP, NT_LINEAR, NT_SOFTPLUS }; + +/** @group globals.learning + constrain the output layer of a neural net to take the value defined by the input layer. + the arguments are + \a inputs: an array of float variables + \a input_ids: array[int] of node + \a outputs: an array of float variables + \a output_ids: array[int] of node + \a bias: array[node] of float + \a edge_weight: array[edge] of float (dummy one at end!) + \a edge_parent: array[edge] of neuron (start neuron for edge) + \a first_edge: array[node] of 1..m+1 + \a neuron_type: { NT_RELU, NT_STEP, NT_LINEAR, NT_SOFTPLUS } +*/ +predicate neural_net(array[int] of var float: inputs, + array[int] of int: input_ids, + array[int] of var float: outputs, + array[int] of int: output_ids, + array[int] of float: bias, + array[int] of float: edge_weight, + array[int] of int: edge_parent, + array[int] of int: first_edge, + NEURON_TYPE: neuron_type) = + assert(index_set(inputs) == index_set(input_ids), + "neural_net: number of input vars not equal to number of input_ids") /\ + assert(index_set(outputs) == index_set(output_ids), + "neural_net: number of output vars not equal to number of output_ids") /\ + let { set of int: node = index_set(bias) } in + let { set of int: edge = index_set(edge_weight) } in + assert(index_set(edge_parent) == edge, + "neural_net: index sets of edge_weight and edge_parent do not agree") /\ + assert(index_set(first_edge) == node, + "neural_new: index sets of bias and first_edge do not agree") /\ + forall(i in index_set(input_ids)) + (assert(input_ids[i] in node, + "neural_net: input_ids\(i) = \(input_ids[i]) not a correct node number")) + /\ + forall(i in index_set(output_ids)) + (assert(output_ids[i] in node, + "neural_net: output_ids\(i) = \(output_ids[i]) not a correct node number")) + /\ + forall(i in index_set(first_edge)) + (assert(first_edge[i] in edge, + "neural_net: first_edge\(i) = \(first_edge[i]) not a correct edge number")) + /\ + forall(i in index_set(edge_parent)) + (assert(edge_parent[i] in edge, + "neural_net: edge_parent\(i) = \(edge_parent[i]) not a correct node number")) + /\ + assert(mincreasing(first_edge), + "neural_net: first_edge array is not in increasing order") + /\ + fzn_neural_net(inputs,input_ids,outputs,output_ids,bias, + edge_weight,edge_parent,first_edge,neuron_type); + +test mincreasing(array[int] of int: x) = + forall(i in index_set(x) diff { max(index_set(x)) }) + (x[i] <= x[i+1]); + +predicate neural_net(array[int] of var float: inputs, + array[int] of int: input_ids, + array[int] of var float: outputs, + array[int] of int: output_ids, + array[int] of float: bias, + array[int] of float: edge_weight, + array[int] of int: edge_parent, + array[int] of int: first_edge) = + neural_net(inputs,input_ids,outputs,output_ids,bias, + edge_weight,edge_parent,first_edge,NT_RELU); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/nosets.mzn b/share/minizinc/std/nosets.mzn new file mode 100644 index 0000000..4950efc --- /dev/null +++ b/share/minizinc/std/nosets.mzn @@ -0,0 +1,352 @@ +%%% Inclusion of this file eliminates set variables by converting them into arrays of var bool. +% AUTHORS +% Guido Tack +% Gleb Belov +% + +predicate mzn_reverse_map_var(var set of int: x) = let { + array[int] of var bool: b = set2bools(x) +} in true; + +function var set of int: reverse_map(array[int] of var bool: b); +function set of int: reverse_map(array[int] of bool: b) ::promise_total = + { i | i in index_set(b) where b[i] }; + +array[int] of var bool: set2bools(var set of int: x) ::promise_total = + if is_fixed(x) then set2bools(fix(x)) else + let { + array[int] of var bool: b = array1d( min(ub(x))..max(ub(x)), + [ set2bools_bit( x, i ) | i in min(ub(x))..max(ub(x)) ] ); + constraint (x = reverse_map(b)) :: is_reverse_map; + } in b + endif; +array[int] of var bool: set2bools(var set of int: x, set of int: ubx) ::promise_total = + if is_fixed(x) then set2bools(fix(x), ubx) else + let { + array[int] of var bool: b0 = set2bools( x ); %% Call in any case ?? TODO + array[int] of var bool: b = array1d( min(ubx)..max(ubx), + [ if i in ubx then set2bools_bit( x, i ) else false endif + | i in min(ubx)..max(ubx) ] ); +%% Not again: constraint (x = reverse_map(b)) :: is_reverse_map; + } in b + endif; +array[int] of bool: set2bools(set of int: x) ::promise_total = + array1d(min(x)..max(x),[i in x | i in min(x)..max(x)]); +array[int] of bool: set2bools(set of int: x, set of int: ubx) ::promise_total = + array1d(min(ubx)..max(ubx),[i in x | i in min(ubx)..max(ubx)]); +function var bool: set2bools_bit( var set of int: x, int: i ) ::promise_total = + if i in ub(x) then + let { + var bool: bi; + } in bi + else false endif; + + +predicate set_eq(var set of int: x, var set of int: y) ::promise_total = + if not has_ub_set(x) /\ not has_ub_set(y) then + assert(false, "set_eq: cannot determine bounds of set variables") + elseif not has_ub_set(x) then set_eq(y,x) + else + let { + constraint y subset x, %% Constrain domains first + constraint x subset y, + } in let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in forall (i in index_set(bx) union index_set(by)) ( + if not (i in index_set(bx)) then not by[i] %% Should be impossible + elseif not (i in index_set(by)) then not bx[i] + else bx[i]=by[i] endif + ) + endif; + +predicate set_eq_reif(var set of int: x, var set of int: y, var bool: b) ::promise_total = + if is_fixed(b) then + if true==fix(b) then x==y else x!=y endif + else + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in b <-> forall (i in index_set(bx) union index_set(by)) ( + if not (i in index_set(bx)) then not by[i] + elseif not (i in index_set(by)) then not bx[i] + else bx[i]=by[i] endif + ) + endif; + +predicate set_eq_imp(var set of int: x, var set of int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x = y) else true endif + else + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in b -> forall (i in index_set(bx) union index_set(by)) ( + if not (i in index_set(bx)) then not by[i] + elseif not (i in index_set(by)) then not bx[i] + else bx[i]=by[i] endif + ) + endif; + + predicate set_ne(var set of int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in exists (i in index_set(bx) union index_set(by)) ( + if not (i in index_set(bx)) then by[i] + elseif not (i in index_set(by)) then bx[i] + else bx[i]!=by[i] endif + ); + +predicate set_ne_reif(var set of int: x, var set of int: y, var bool: b) ::promise_total = + set_eq_reif( x, y, not b ); + +predicate set_ne_imp(var set of int: x, var set of int: y, var bool: b) = + if is_fixed(b) then + if fix(b) then (x != y) else true endif + else + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in b -> exists (i in index_set(bx) union index_set(by)) ( + if not (i in index_set(bx)) then by[i] + elseif not (i in index_set(by)) then bx[i] + else (bx[i] != by[i]) endif + ) + endif; + +% Set comparisons are often used just to avoid symmetries. +% Reifs/imps are done automatically from these templates +predicate set_le(var set of int: x, var set of int: y) ::promise_total = + let { + set of int: U = ub(x) union ub(y); + int: l = min(U); + int: u = max(U); + array[U] of var bool: xb = [i in x | i in U]; + var l-1..u: xmax = max(x union {l-1}); + array[U] of var bool: yb = [i in y | i in U]; + var l-1..u: ymax = max(y union {l-1}); + array[U] of var bool: b; + constraint forall(i in l..u-1) ( + b[i] = let {var 1..4: cmp = 2*xb[i] + yb[i] + 1} in + [b[i+1], xmax < i, ymax > i, b[i+1]][cmp] + ); + constraint b[u] = (xb[u] -> yb[u]); + } in b[l]; + +predicate set_lt(var set of int: x, var set of int: y) ::promise_total = + let { + set of int: U = ub(x) union ub(y); + int: l = min(U); + int: u = max(U); + array[U] of var bool: xb = [i in x | i in U]; + var l-1..u: xmax = max(x union {l-1}); + array[U] of var bool: yb = [i in y | i in U]; + var l-1..u: ymax = max(y union {l-1}); + array[U] of var bool: b; + constraint forall(i in l..u-1) ( + b[i] = let {var 1..4: cmp = 2*xb[i] + yb[i] + 1} in + [b[i+1], xmax < i, ymax > i, b[i+1]][cmp] + ); + constraint b[u] = (not xb[u] /\ yb[u]); + } in b[l]; + +predicate set_subset(var set of int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in forall (i in index_set(bx)) ( + if not (i in index_set(by)) then not bx[i] + else bx[i] -> by[i] endif + ); + +predicate set_subset_reif(var set of int: x, var set of int: y, var bool: b) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in b <-> forall (i in index_set(bx)) ( + if not (i in index_set(by)) then not bx[i] + else bx[i] -> by[i] endif + ); + +predicate set_subset_imp(var set of int: x, var set of int: y, var bool: b) = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + } in b -> forall (i in index_set(bx)) ( + if not (i in index_set(by)) then not bx[i] + else bx[i] -> by[i] endif + ); + +%%% Map the subset operation to superset +predicate set_superset(var set of int: x, var set of int: y) ::promise_total = set_subset( y, x ); +predicate set_superset_reif(var set of int: x, var set of int: y, var bool: b) ::promise_total = + set_subset_reif( y, x, b ); +predicate set_superset_imp(var set of int: x, var set of int: y, var bool: b) = + set_subset_imp(y,x,b); + +function var set of int: set_intersect(var set of int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + var set of (index_set(bx) intersect index_set(by)): z; + array[int] of var bool: bz = set2bools(z); + constraint forall (i in index_set(bz)) ( + bz[i] = (bx[i] /\ by[i]) + ); + } in z; + +function var set of int: set_union(var set of int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + var set of (index_set(bx) union index_set(by)): z; + array[int] of var bool: bz = set2bools(z); + constraint forall (i in index_set(bx) union index_set(by)) ( + if (i in index_set(bx)) then + if (i in index_set(by)) then + bz[i] = (bx[i] \/ by[i]) + else + bz[i] = bx[i] + endif + else + bz[i] = by[i] + endif + ); + } in z; + +function var set of int: set_diff(var set of int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + var set of ub(x) diff lb(y): z; + array[int] of var bool: bz = set2bools(z); + constraint forall (i in ub(z)) ( + bz[i] = (bx[i] /\ (not by[i])) + ); + } in z; + +function var set of int: set_symdiff(var set of int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + array[int] of var bool: by = set2bools(y); + var set of (ub(x) diff lb(y)) union (ub(y) diff lb(x)): z; + array[int] of var bool: bz = set2bools(z); + constraint forall (i in ub(z)) ( + bz[i] = (bx[i] xor by[i]) + ); + } in z; + +function var int: card(var set of int: x) ::promise_total = + let { + array[int] of var bool: bx = set2bools(x); + var 0..length(bx) : c; + constraint bool_lin_eq([1 | i in index_set(bx)],[bx[i] | i in index_set(bx)],c); + } in c; + +predicate set_in(int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: by = set2bools(y); + } in by[x]; +predicate set_in(var int: x, var set of int: y) ::promise_total = + let { + array[int] of var bool: by = set2bools(y); + } in by[x]; + +predicate set_in_reif(int: x, var set of int: y, var bool: b) ::promise_total = + if x in ub(y) then + b <-> set2bools(y)[x] + else + not b + endif; +predicate set_in_reif(var int: x, var set of int: y, var bool: b) ::promise_total = + b <-> set2bools(y)[x]; +predicate set_in_imp(var int: x, var set of int: y, var bool: b) = + b -> set2bools(y)[x]; + +function array[int] of var bool: setarray2bools(array[int] of var set of int: x) = + if length(x)=0 then [] else + set2bools(x[1])++setarray2bools([x[i]|i in 2..length(x)]) + endif; +%% Par version no sense + + +predicate array_var_set_element(var int: x, array[int] of var set of int: y, var set of int: z) ::promise_total = + let { + constraint x in { i | i in index_set( y ) where + lb(y[i]) subset ub(z) /\ lb(z) subset ub(y[i]) + }; + set of int: sUB = array_union( [ ub(y[i]) | i in dom(x) ] ); + set of int: sLB = array_intersect( [ lb(y[i]) | i in dom(x) ] ); + constraint z subset sUB, + constraint sLB subset z, + } in + forall (k in ub(z)) ( + set2bools(z)[k] == + if k in sUB then + if k in sLB then + true + else + array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + else + false + endif + ) /\ + forall (k in sUB diff ub(z))( + if k in sLB then + false %% fail the constraint + else + not array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + ); + +predicate array_set_element(var int: x, array[int] of set of int: y, var set of int: z) ::promise_total = + let { + constraint x in { i | i in index_set( y ) where + y[i] subset ub(z) /\ lb(z) subset y[i] + }; + set of int: sUB = array_union( [ y[i] | i in dom(x) ] ); + set of int: sLB = array_intersect( [ y[i] | i in dom(x) ] ); + constraint z subset sUB, + constraint sLB subset z, + } in + forall (k in ub(z)) ( + set2bools(z)[k] == + if k in sUB then + if k in sLB then + true + else + array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + else + false + endif + ) /\ + forall (k in sUB diff ub(z))( + if k in sLB then + false %% fail the constraint + else + not array1d( lb(x)..ub(x), + [ if k in ub(y[i]) then set2bools(y[i])[k] else false endif | i in dom(x) ] )[x] + endif + ); + +annotation set_search(array[$X] of var set of int: x, ann: a1, ann: a2, ann: a3) = + let { array[int] of var set of int: xx = array1d(x) } in + seq_search( [ bool_search(set2bools(xx[i]),a1,a2,a3) | i in index_set(xx) ] ); + +annotation warm_start( array[int] of var set of int: x, array[int] of set of int: v ) = + warm_start_array( [ + let { + array[int] of var bool: xb = set2bools(x[i]), + set of int: is_var = ub(x[i]) diff lb(x[i]), + int: iV = i - min(index_set(x)) + if 0 b) /\ + forall (i in index_set(bs)) (bs[i] \/ b); + +/** @group flatzinc.two Constrains \a m to be the maximum value in array \a x. */ +predicate array_int_maximum(var int: m, array[int] of var int: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: ly = lb_array(x), + int: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +/** @group flatzinc.two Constrains \a m to be the maximum value in array \a x. */ +predicate array_float_maximum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == max(x[i],y[i-1]) ); + +/** @group flatzinc.two Constrains \a m to be the minimum value in array \a x. */ +predicate array_int_minimum(var int: m, array[int] of var int: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + int: ly = lb_array(x), + int: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); + +/** @group flatzinc.two Constrains \a m to be the minimum value in array \a x. */ +predicate array_float_minimum(var float: m, array[int] of var float: x) = + let { int: l = min(index_set(x)), + int: u = max(index_set(x)), + float: ly = lb_array(x), + float: uy = ub_array(x), + array[l..u] of var ly..uy: y } in + y[l] = x[l] /\ + m = y[u] /\ + forall (i in l+1 .. u) ( y[i] == min(x[i],y[i-1]) ); diff --git a/share/minizinc/std/redefinitions-2.1.1.mzn b/share/minizinc/std/redefinitions-2.1.1.mzn new file mode 100644 index 0000000..a6463ae --- /dev/null +++ b/share/minizinc/std/redefinitions-2.1.1.mzn @@ -0,0 +1,75 @@ +% This file contains redefinitions of standard builtins for version 2.1.1 +% that can be overridden by solvers. + +/*** + @groupdef flatzinc.twooneone FlatZinc builtins added in MiniZinc 2.1.1. + + These functions and predicates define built-in operations of the MiniZinc language + that have been added in MiniZinc 2.1.1. Solvers that support these natively need + to include a file called redefinitions-2.1.1.mzn in their solver library that + redefines these predicates as builtins. + +*/ + +/** @group flatzinc.twooneone Returns variable constrained to be equal to the minimum of the set \a s. + An alternative implementation can be found in the comments of the source code. +*/ + +function var $$E: min(var set of $$E: s) = + let { var min(ub(s)) .. max(ub(s)): m = + min([ e + (max(ub(s))+1-e)*(1 - (e in s)) | e in ub(s) ]) } in m; + +%% The following can be used as an alternative if the solver supports +%% a FlatZinc builtin set_min: + +% predicate set_min(var set of int: s, var int: m); +% +% function var $$E: min(var set of $$E: s) = +% if mzn_in_root_context(s) then min_t(s) else +% let { constraint card(s) > 0 } in min_mt(s) endif; +% +% function var $$E: min_t(var set of $$E: s) ::promise_total = +% let { +% var min(ub(s))..max(ub(s)): ms ::is_defined_var; +% constraint card(s) > 0; +% constraint set_min(s,ms) ::defines_var(ms); +% } in ms; +% +% function var $$E: min_mt(var set of $$E: s) ::promise_total = +% let { +% var set of ub(s) union {1}: x; +% var bool: b = card(s) > 0; +% constraint b -> x=s; +% constraint b \/ 1 in x; +% } in min_t(x); + +/** @group flatzinc.twooneone Returns variable constrained to be equal to the maximum of the set \a s. + An alternative implementation can be found in the comments of the source code. +*/ +function var $$E: max(var set of $$E: s) = + let { var min(ub(s)) .. max(ub(s)): m = + max([ e + (min(ub(s))-1-e)*(1 - (e in s)) | e in ub(s) ]) } in m; + +%% The following can be used as an alternative if the solver supports +%% a FlatZinc builtin set_max: + +% predicate set_max(var set of int: s, var int: m); +% +% function var $$E: max(var set of $$E: s) = +% if mzn_in_root_context(s) then max_t(s) else +% let { constraint card(s) > 0 } in max_mt(s) endif; +% +% function var $$E: max_t(var set of $$E: s) ::promise_total = +% let { +% var min(ub(s))..max(ub(s)): ms ::is_defined_var; +% constraint card(s) > 0; +% constraint set_max(s,ms) ::defines_var(ms); +% } in ms; +% +% function var $$E: max_mt(var set of $$E: s) ::promise_total = +% let { +% var set of ub(s) union {1}: x; +% var bool: b = card(s) > 0; +% constraint b -> x=s; +% constraint b \/ 1 in x; +% } in max_t(x); diff --git a/share/minizinc/std/redefinitions-2.1.mzn b/share/minizinc/std/redefinitions-2.1.mzn new file mode 100644 index 0000000..0371915 --- /dev/null +++ b/share/minizinc/std/redefinitions-2.1.mzn @@ -0,0 +1,20 @@ +/*** + @groupdef flatzinc.twoone FlatZinc builtins added in MiniZinc 2.1.0. + + These functions and predicates define built-in operations of the MiniZinc language + that have been added in MiniZinc 2.1.0. Solvers that support these natively need + to include a file called redefinitions-2.1.0.mzn in their solver library that + redefines these predicates as builtins. + +*/ + + +/** @group flatzinc.twoone Constrains \a a ≤ \a x ≤ \a b */ +predicate float_in(var float: x, float: a, float: b) = + x >= a /\ x <= b; + +/** @group flatzinc.twoone Constrains \a x to take one of the values in \a as */ +predicate float_dom(var float: x, array[int] of float: as) = + let { + array[int] of var bool: b = [float_in(x,as[2*i-1],as[2*i]) | i in 1..length(as) div 2] + } in exists(b); diff --git a/share/minizinc/std/redefinitions-2.2.1.mzn b/share/minizinc/std/redefinitions-2.2.1.mzn new file mode 100644 index 0000000..7a0c189 --- /dev/null +++ b/share/minizinc/std/redefinitions-2.2.1.mzn @@ -0,0 +1,7 @@ +% This file contains redefinitions of standard builtins for version 2.2.1 +% that can be overridden by solvers. + +/** @group flatzinc.int Constrains \a z = \(\a x ^ {\a y}\) */ +predicate int_pow_fixed(var int: x, int: y, var int: z) = + if y=0 then z=1 elseif y=1 then z=x else z=product([x | i in 1..y]) endif; + diff --git a/share/minizinc/std/redefinitions.mzn b/share/minizinc/std/redefinitions.mzn new file mode 100644 index 0000000..55bab26 --- /dev/null +++ b/share/minizinc/std/redefinitions.mzn @@ -0,0 +1,2 @@ +% This file contains redefinitions of standard builtins that can be overridden +% by solvers. diff --git a/share/minizinc/std/regular.mzn b/share/minizinc/std/regular.mzn new file mode 100644 index 0000000..cab601c --- /dev/null +++ b/share/minizinc/std/regular.mzn @@ -0,0 +1,35 @@ +include "fzn_regular.mzn"; +include "fzn_regular_reif.mzn"; + +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the range 1..\a S) + is accepted by the DFA of \a Q states with input 1..\a S and transition + function \a d (which maps (1..\a Q, 1..\a S) -> 0..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). We reserve state 0 to be an always failing state. +*/ +predicate regular(array[int] of var int: x, int: Q, int: S, + array[int,int] of int: d, int: q0, set of int: F) = + assert(Q > 0, + "regular: 'Q' must be greater than zero", + + assert(S > 0, + "regular: 'S' must be greater than zero", + + assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) == 1..S, + "regular: the transition function 'd' must be [1..Q,1..S]", + + assert(forall([d[i, j] in 0..Q | i in 1..Q, j in 1..S]), + "regular: transition function 'd' points to states outside 0..Q", + + % Nb: we need the parentheses around the expression otherwise the + % parser thinks it's a generator call! + assert((q0 in 1..Q), + "regular: start state 'q0' not in 1..Q", + assert(F subset 1..Q, + "regular: final states in 'F' contain states outside 1..Q", + + fzn_regular(x,Q,S,d,q0,F) + + )))))); + diff --git a/share/minizinc/std/regular_nfa.mzn b/share/minizinc/std/regular_nfa.mzn new file mode 100644 index 0000000..56a20f8 --- /dev/null +++ b/share/minizinc/std/regular_nfa.mzn @@ -0,0 +1,73 @@ +include "fzn_regular_nfa.mzn"; +include "fzn_regular_nfa_reif.mzn"; +include "fzn_regular_nfa_set.mzn"; +include "fzn_regular_nfa_set_reif.mzn"; + +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the range 1..\a S) + is accepted by the NFA of \a Q states with input 1..\a S and transition + function \a d (which maps (1..\a Q, 1..\a S) -> set of 1..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). +*/ +predicate regular_nfa(array[int] of var int: x, int: Q, int: S, + array[int,int] of set of int: d, int: q0, set of int: F) = + assert(Q > 0, + "regular_nfa: 'Q' must be greater than zero", + + assert(S > 0, + "regular_nfa: 'S' must be greater than zero", + + assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) == 1..S, + "regular_nfa: the transition function 'd' must be [1..Q,1..S]", + + assert(forall([d[i, j] subset 1..Q | i in 1..Q, j in 1..S]), + "regular_nfa: transition function 'd' points to states outside 1..Q", + + % Nb: we need the parentheses around the expression otherwise the + % parser thinks it's a generator call! + assert((q0 in 1..Q), + "regular: start state 'q0' not in 1..Q", + + assert(F subset 1..Q, + "regular: final states in 'F' contain states outside 1..Q", + + fzn_regular_nfa(x,Q,S,d,q0,F) + + )))))); + +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the range \a S) + is accepted by the NFA of \a Q states with input \a S and transition + function \a d (which maps (1..\a Q, \a S) -> set of 1..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). +*/ +predicate regular_nfa(array[int] of var int: x, int: Q, set of int: S, + array[int,int] of set of int: d, int: q0, set of int: F) = + assert(Q > 0, + "regular_nfa: 'Q' must be greater than zero", + + assert(card(S) > 0, + "regular_nfa: 'S' must be non empty", + + assert(S = min(S)..max(S), + "regular_nfa: 'S' must be a range", + + assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) == S, + "regular_nfa: the transition function 'd' must be [1..Q,S]", + + assert(forall([d[i, j] subset 1..Q | i in 1..Q, j in S]), + "regular_nfa: transition function 'd' points to states outside 1..Q", + + % Nb: we need the parentheses around the expression otherwise the + % parser thinks it's a generator call! + assert((q0 in 1..Q), + "regular: start state 'q0' not in 1..Q", + + assert(F subset 1..Q, + "regular: final states in 'F' contain states outside 1..Q", + + fzn_regular_nfa(x,Q,S,d,q0,F) + + ))))))); diff --git a/share/minizinc/std/regular_regexp.mzn b/share/minizinc/std/regular_regexp.mzn new file mode 100644 index 0000000..c1dd4ff --- /dev/null +++ b/share/minizinc/std/regular_regexp.mzn @@ -0,0 +1,33 @@ +include "fzn_regular_regexp.mzn"; + +/** @group globals.extensional + The sequence of values in array \a x is accepted by the regular + expression \a r. This constraint generates it's DFA equivalent. + + Regular expressions can use the following syntax: + + - Selection: + + - Concatenation: "12 34", 12 followed by 34. + (Characters are assumed to be the part of the same number unless split + by syntax or whitespace.) + - Union: "7|11", a 7 or 11. + - Groups: "7(6|8)", a 7 followed by a 6 or an 8. + - Wildcard: ".", any value within the domain. + - Classes: "[3-6 7]", a 3,4,5,6, or 7. + - Negated classes: "[^3 5]", any value within the domain except for a 3 or a 5. + + - Quantifiers: + + - Asterisk: "12*", 0 or more times a 12. + - Question mark: "5?", 0 or 1 times a 5. (optional) + - Plus sign: "42+", 1 or more time a 42. + - Exact: "1{3}", exactly 3 times a 1. + - At least: "9{5,}", 5 or more times a 9. + - Between: "7{3,5}", at least 3 times, but at most 5 times a 7. + + Members of enumerated types can be used in place of any integer (e.g., "A B", + A followed by B). Enumerated identifiers still use whitespace for concatenation. +*/ +predicate regular(array[int] of var int: x, string: r) = + fzn_regular(x, r); diff --git a/share/minizinc/std/regular_set.mzn b/share/minizinc/std/regular_set.mzn new file mode 100644 index 0000000..218ad1c --- /dev/null +++ b/share/minizinc/std/regular_set.mzn @@ -0,0 +1,34 @@ +include "fzn_regular_set.mzn"; +include "fzn_regular_set_reif.mzn"; + +/** @group globals.extensional + The sequence of values in array \a x (which must all be in the set \a S) + is accepted by the DFA of \a Q states with input \a S and transition + function \a d (which maps (1..\a Q, \a S) -> 0..\a Q)) and initial state \a q0 + (which must be in 1..\a Q) and accepting states \a F (which all must be in + 1..\a Q). We reserve state 0 to be an always failing state. +*/ +predicate regular(array[int] of var int: x, int: Q, set of int: S, + array[int,int] of int: d, int: q0, set of int: F) = + assert(Q > 0, + "regular: 'Q' must be greater than zero", + + assert(card(S) > 0, + "regular: 'S' must be non empty", + + assert(index_set_1of2(d) = 1..Q /\ index_set_2of2(d) == S, + "regular: the transition function 'd' must be [1..Q,S]", + + assert(forall([d[i, j] in 0..Q | i in 1..Q, j in S]), + "regular: transition function 'd' points to states outside 0..Q", + + % Nb: we need the parentheses around the expression otherwise the + % parser thinks it's a generator call! + assert((q0 in 1..Q), + "regular: start state 'q0' not in 1..Q", + assert(F subset 1..Q, + "regular: final states in 'F' contain states outside 1..Q", + + fzn_regular(x,Q,S,d,q0,F) + + )))))); diff --git a/share/minizinc/std/roots.mzn b/share/minizinc/std/roots.mzn new file mode 100644 index 0000000..0557ede --- /dev/null +++ b/share/minizinc/std/roots.mzn @@ -0,0 +1,12 @@ +include "fzn_roots.mzn"; +include "fzn_roots_reif.mzn"; + +/** @group globals + Requires that \a x[\p i] in \a t for all \p i in \a s +*/ +predicate roots(array[int] of var int: x, var set of int: s, + var set of int: t) = + assert(ub(s) subset index_set(x), + "roots: upper bound of 's' must be a subset of the index set of 'x'", + fzn_roots(x,s,t) + ); diff --git a/share/minizinc/std/roots_fn.mzn b/share/minizinc/std/roots_fn.mzn new file mode 100644 index 0000000..b0e649a --- /dev/null +++ b/share/minizinc/std/roots_fn.mzn @@ -0,0 +1,10 @@ +include "roots.mzn"; + +/** @group globals + Returns \a s such that \a x[\p i] in \a t for all \p i in \a s +*/ +function var set of int: roots(array[int] of var int: x, + var set of int: t) :: promise_total = + let { var set of index_set(x): s; + constraint roots(x,s,t) } + in s; diff --git a/share/minizinc/std/seq_precede_chain.mzn b/share/minizinc/std/seq_precede_chain.mzn new file mode 100644 index 0000000..a7ee02d --- /dev/null +++ b/share/minizinc/std/seq_precede_chain.mzn @@ -0,0 +1,16 @@ +include "fzn_seq_precede_chain_int.mzn"; +include "fzn_seq_precede_chain_int_reif.mzn"; +include "fzn_seq_precede_chain_set.mzn"; +include "fzn_seq_precede_chain_set_reif.mzn"; + +/** @group globals.lexicographic + Requires that \a i precedes \a i+1 in the array \a x for all positive \a i. +*/ +predicate seq_precede_chain(array[int] of var int: x) = + fzn_seq_precede_chain_int(x); + +/** @group globals.lexicographic + Requires that \a i appears in a set in array \a x before \a i+1 for all positive \a i +*/ +predicate seq_precede_chain(array[int] of var set of int: x) = + fzn_seq_precede_chain_set(x); diff --git a/share/minizinc/std/set_member.mzn b/share/minizinc/std/set_member.mzn new file mode 100644 index 0000000..78f42cc --- /dev/null +++ b/share/minizinc/std/set_member.mzn @@ -0,0 +1,9 @@ +include "fzn_set_member.mzn"; +include "fzn_set_member_reif.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that 'y' occurs in the array or set 'x'. +%-----------------------------------------------------------------------------% + +predicate set_member(var set of int: x, var int: y) = + fzn_set_member(x, y); diff --git a/share/minizinc/std/sliding_sum.mzn b/share/minizinc/std/sliding_sum.mzn new file mode 100644 index 0000000..d982bc0 --- /dev/null +++ b/share/minizinc/std/sliding_sum.mzn @@ -0,0 +1,9 @@ +include "fzn_sliding_sum.mzn"; +include "fzn_sliding_sum_reif.mzn"; + +/** @group globals + Requires that in each subsequence \a vs[\p i], ..., \a vs[\p i + \a seq - 1] the sum of the + values belongs to the interval [\a low, \a up]. +*/ +predicate sliding_sum(int: low, int: up, int: seq, array[int] of var int: vs) = + fzn_sliding_sum(low, up, seq, vs); diff --git a/share/minizinc/std/sort.mzn b/share/minizinc/std/sort.mzn new file mode 100644 index 0000000..20139ad --- /dev/null +++ b/share/minizinc/std/sort.mzn @@ -0,0 +1,12 @@ +include "fzn_sort.mzn"; +include "fzn_sort_reif.mzn"; + +/** @group globals.sort + Requires that the multiset of values in \a x are the same as the + multiset of values in \a y but \a y is in sorted order. +*/ +predicate sort(array[int] of var int: x, array[int] of var int: y) = + assert(card(index_set(x)) == card(index_set(y)), + "sort: x and y must be same sized arrays", + fzn_sort(x,y) + ); diff --git a/share/minizinc/std/sort_fn.mzn b/share/minizinc/std/sort_fn.mzn new file mode 100644 index 0000000..ffc3f76 --- /dev/null +++ b/share/minizinc/std/sort_fn.mzn @@ -0,0 +1,11 @@ +include "sort.mzn"; + +/** @group globals.sort + Return a multiset of values that is the same as the + multiset of values in \a x but in sorted order. +*/ +function array[int] of var int: sort(array[int] of var int: x) ::promise_total = + let { + array[1..length(x)] of var lb_array(x)..ub_array(x): y; + constraint sort(x,y); + } in y; diff --git a/share/minizinc/std/span.mzn b/share/minizinc/std/span.mzn new file mode 100644 index 0000000..cbcf34f --- /dev/null +++ b/share/minizinc/std/span.mzn @@ -0,0 +1,14 @@ +include "fzn_span.mzn"; +include "fzn_span_reif.mzn"; + +/** @group globals.scheduling + Span constraint for optional tasks. Task (\a s0,\a d0) spans the optional + tasks (\a s[\p i],\a d[\p i]) in the array arguments. +*/ +predicate span(var opt int: s0, var int: d0, + array[int] of var opt int: s, + array[int] of var int: d) = + assert(index_set(s) = index_set(d), + "span: index sets of third and fourth argument must be identical", + fzn_span(s0,d0,s,d) + ); diff --git a/share/minizinc/std/stdlib.mzn b/share/minizinc/std/stdlib.mzn new file mode 100644 index 0000000..d5f826f --- /dev/null +++ b/share/minizinc/std/stdlib.mzn @@ -0,0 +1,803 @@ +%-----------------------------------------------------------------------------% +% MiniZinc standard library. +%-----------------------------------------------------------------------------% +% This file contains declarations of all functions, predicates and annotations +% available in the base MiniZinc language. + +/*** + @groupdef MAIN The MiniZinc library +*/ + +/*** + @groupdef annotations Annotations + + These annotations control evaluation and solving behaviour. +*/ + + +/*** + @groupdef annotations.general General annotations +*/ + +/** @group annotations.general Declare function as total, i.e. it does not put + any constraints on its arguments. */ +annotation promise_total; + +/** @group annotations.export Always export function to intermediate representation to ensure + availability for later use. */ +annotation export; + +/** @group annotations.general Declare that expression may have undefined result (to avoid warnings) */ +annotation maybe_partial; + +/** @group annotations.general Declare that the annotated variable should be added to the output +of the model. This annotation only has an effect when the model does not have an output item. */ +annotation add_to_output; + +/** @group annotations.general Declare that the annotated variable should be only used for output. + This annotation can be used to define variables that are required for solution checkers, or + that are necessary for the output item. The annotated variable must be par. + */ +annotation output_only; + +/** @group annotations.general Declare that the annotated variable is required for checking solutions. */ +annotation mzn_check_var; + +/** @group annotations.general Declare that the annotated variable is required for checking solutions and has an enum type. */ +annotation mzn_check_enum_var(set of int); + +/** @group annotations.general Declare a name for the annotated expression. */ +annotation mzn_expression_name(string); + +/** @group annotations.general Declare a name for the annotated constraint. */ +annotation mzn_constraint_name(string); + + +/** @group annotations.general Declare the annotated variable as being functionally defined. + This annotation is introduced into FlatZinc code by the compiler. */ +annotation is_defined_var; +/** @group annotations.general Declare a variable as being introduced by the compiler. */ +annotation var_is_introduced; +/** @group annotations.general Declare variable: \a c as being functionally defined + by the annotated constraint. This annotation is introduced into FlatZinc code by the compiler. */ +annotation defines_var(var $t: c); +/** @group annotations.general Declare that the annotated array should be printed by + the solver with the given index sets \a a. This annotation is introduced into FlatZinc code by + the compiler. */ +annotation output_array(array[$u] of set of int:a); +/** @group annotations.general Declare that the annotated variable should be printed by + the solver. This annotation is introduced into FlatZinc code by + the compiler. */ +annotation output_var; +/** @group annotations.general Declare that the annotated expression is used to map + an expression back from FlatZinc to MiniZinc. */ +annotation is_reverse_map; +/** @group annotations.general Document the function or variable declaration item with + the string \a s. */ +annotation doc_comment(string: s); +/** @group annotations.general Representation of the call-stack when the annotated item + was introduced, as a string \a s. Can be used to uniquely identify variables and constraints across different + compilations of a model that may have different names. This annotations is introduced into + FlatZinc code by the compiler and is retained if --keep-paths argument is used. */ +annotation mzn_path(string: s); +/** @group annotations.general With debug build of mzn2fzn, call MiniZinc::mzn_break_here when + flattening this expression to make debugging easier. This annotation is ignored by the + release build.*/ +annotation mzn_break_here; +/** @group annotations.general Used to attach a name \a s to an expression, this should also propagate to + any sub-expressions or decomposition of the annotated expression. String annotations on expressions + are re-written as expression_name annotations */ +annotation expression_name(string: s); +/** @group annotations.general Used to attach a name \a s to a constraint and its decomposition. String + annotations on constraint keywords are re-written as constraint_name annotations */ +annotation constraint_name(string: s); + +/** @group annotations.general Used internally by the compiler */ +annotation mzn_rhs_from_assignment; + +/** @group annotations.general Marks a constraint as a recorded domain changing constraint (when mzn2fzn + called with -g flag */ +annotation domain_change_constraint; + +/*** + @groupdef annotations.prop Propagation strength annotations +*/ + +/** @group annotations.prop Annotate a constraint to use domain propagation */ +annotation domain; +/** @group annotations.prop Annotate a constraint to use bounds propagation */ +annotation bounds; + + +/*** + @groupdef annotations.search Search annotations +*/ + +/** @group annotations.search Sequentially perform the searches specified in array \a s */ +annotation seq_search(array[int] of ann: s); + +/** @group annotations.search Specify search on variables \a x, with variable selection + strategy \a select, value choice strategy \a choice, and exploration strategy + \a explore. If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation int_search( + array[$X] of var int: x, + ann: select, + ann: choice, + ann: explore, +); + +/** @group annotations.search Specify search on variables \a x, with variable selection + strategy \a select, and value choice strategy \a choice. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation int_search( + array[$X] of var int: x, + ann: select, + ann: choice +) = int_search(x,select,choice,complete); + +/** @group annotations.search Specify search on variables \a x, with variable selection + strategy \a select, value choice strategy \a choice, and exploration strategy + \a explore. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation bool_search( + array[$X] of var bool: x, + ann: select, + ann: choice, + ann: explore +); + +/** @group annotations.search Specify search on variables \a x, with variable selection + strategy \a select, and value choice strategy \a choice. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation bool_search( + array[$X] of var bool: x, + ann: select, + ann: choice +) = bool_search(x,select,choice,complete); + +/** @group annotations.search Specify search on variables \a x, + with precision \a prec, variable selection + strategy \a select, value choice strategy \a choice, and exploration strategy + \a explore. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation float_search( + array[$X] of var float: x, + float: prec, + ann: select, + ann: choice, + ann: explore +); + +/** @group annotations.search Specify search on variables \a x, + with precision \a prec, variable selection + strategy \a select, and value choice strategy \a choice. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation float_search( + array[$X] of var float: x, + float: prec, + ann: select, + ann: choice +) = float_search(x,prec,select,choice,complete); + +/** @group annotations.search Specify search on variables \a x, with variable selection + strategy \a select, value choice strategy \a choice, and exploration strategy + \a explore. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation set_search( + array[$X] of var set of int: x, + ann: select, + ann: choice, + ann: explore +); + +/** @group annotations.search Specify search on variables \a x, with variable selection + strategy \a select, and value choice strategy \a choice. + If \a x is a multi-dimensional array, it is coerced to one-dimensional + in row-major order (as with the array1d function). +*/ +annotation set_search( + array[$X] of var set of int: x, + ann: select, + ann: choice +) = set_search(x,select,choice,complete); + +/*** + @groupdef annotations.search.varsel Variable selection annotations +*/ + +/** @group annotations.search.varsel Search variables in the given order */ +annotation input_order; +/** @group annotations.search.varsel Choose the variable with the smallest domain */ +annotation first_fail; +/** @group annotations.search.varsel Choose the variable with the largest domain */ +annotation anti_first_fail; +/** @group annotations.search.varsel Choose the variable with the smallest value in its domain */ +annotation smallest; +/** @group annotations.search.varsel Choose the variable with the largest value in its domain */ +annotation largest; +/** @group annotations.search.varsel Choose the variable with the largest number of attached constraints */ +annotation occurrence; +/** @group annotations.search.varsel Choose the variable with the smallest domain, + breaking ties using the number of attached constraints */ +annotation most_constrained; +/** @group annotations.search.varsel Choose the variable with largest difference + between the two smallest values in its domain */ +annotation max_regret; +/** @group annotations.search.varsel Choose the variable with largest domain, divided + by the number of attached constraints weighted by how often they have caused failure */ +annotation dom_w_deg; +/** @group annotations.search.varsel Choose the variable with the highest impact so + far during the search */ +annotation impact; + +/*** + @groupdef annotations.search.choice Value choice annotations +*/ + +/** @group annotations.search.choice Assign values in ascending order */ +annotation indomain; +/** @group annotations.search.choice Assign the smallest value in the domain */ +annotation indomain_min; +/** @group annotations.search.choice Assign the largest value in the domain */ +annotation indomain_max; +/** @group annotations.search.choice Assign the value in the domain closest to + the mean of its current bounds */ +annotation indomain_middle; +/** @group annotations.search.choice Assign the middle value in the domain */ +annotation indomain_median; +/** @group annotations.search.choice Assign a random value from the domain */ +annotation indomain_random; +/** @group annotations.search.choice Bisect the domain, excluding the upper half first */ +annotation indomain_split; +/** @group annotations.search.choice Bisect the domain, randomly selecting which half to exclude first */ +annotation indomain_split_random; +/** @group annotations.search.choice Bisect the domain, excluding the lower half first */ +annotation indomain_reverse_split; +/** @group annotations.search.choice + If the domain consists of several contiguous intervals, reduce the + domain to the first interval. Otherwise bisect the domain. + */ +annotation indomain_interval; +/** @group annotations.search.choice Exclude the smallest value from the domain */ +annotation outdomain_min; +/** @group annotations.search.choice Exclude the largest value from the domain */ +annotation outdomain_max; +/** @group annotations.search.choice Exclude the middle value from the domain */ +annotation outdomain_median; +/** @group annotations.search.choice Exclude a random value from the domain */ +annotation outdomain_random; + +/*** + @groupdef annotations.search.explore Exploration strategy annotations +*/ + +/** @group annotations.search.explore Perform a complete search */ +annotation complete; + +/*** + @groupdef annotations.search.restart Restart annotations +*/ + +/** @group annotations.search.restart Restart with Luby sequence scaled by \a scale */ +annotation restart_luby(int: scale); +/** @group annotations.search.restart Restart with geometric sequence with parameters \a base and \a scale */ +annotation restart_geometric(float: base, int: scale); +/** @group annotations.search.restart Restart with linear sequence scaled by \a scale */ +annotation restart_linear(int: scale); +/** @group annotations.search.restart Restart after constant number of nodes \a scale */ +annotation restart_constant(int: scale); +/** @group annotations.search.restart Do not restart */ +annotation restart_none; + +/*** + @groupdef annotations.warmstart Warm start annotations + + To be put on the solve item, similar to search annotations. + A variable can be mentioned several times and in different + annotations but only one of the values is taken +*/ + +/** @group annotations.warmstart Specify an array \a w of warm_start annotations + or other warm_start_array annotations. Can be useful to keep the annotation + order in FlatZinc for manual updating. + + Note: if you have search annotations as well, put warm_starts into seq_search + in order to have precedence between both, which may matter. +*/ +annotation warm_start_array( array[int] of ann: w ); + +/** @group annotations.warmstart Specify warm start values \a v for an array of booleans \a x */ +annotation warm_start( array[int] of var bool: x, array[int] of bool: v ); +/** @group annotations.warmstart Specify warm start values \a v for an array of integers \a x */ +annotation warm_start( array[int] of var int: x, array[int] of int: v ); +/** @group annotations.warmstart Specify warm start values \a v for an array of floats \a x */ +annotation warm_start( array[int] of var float: x, array[int] of float: v ); +/** @group annotations.warmstart Specify warm start values \a v for an array of sets \a x */ +annotation warm_start( array[int] of var set of int: x, array[int] of set of int: v ); + + +/*** + @groupdef annotations.warmstart.optvals Warm start annotations with optional values + + The value arrays can contain <> elements (absent values). + The following decompositions eliminate those elements + because FlatZinc 1.6 does not support optionals. +*/ + +/** @group annotations.warmstart.optvals Specify warm start values \a v for an array of booleans \a x */ +annotation warm_start( array[int] of var bool: x, array[int] of opt bool: v ) = + if 0==length(x) \/ 0==length(v) then + warm_start( x, [] ) + else + warm_start( [ x[i] | i in index_set(x) where + i-min(index_set(x))+min(index_set(v))>length(v) \/ + occurs( v[ i-min(index_set(x))+min(index_set(v)) ] ) ], + [ deopt(v[i]) | i in index_set(v) where occurs(v[i]) ] ) + endif; +/** @group annotations.warmstart.optvals Specify warm start values \a v for an array of integers \a x */ +annotation warm_start( array[int] of var int: x, array[int] of opt int: v ) = + if 0==length(x) \/ 0==length(v) then + warm_start( x, [] ) + else + warm_start( [ x[i] | i in index_set(x) where + i-min(index_set(x))+min(index_set(v))>length(v) \/ + occurs( v[ i-min(index_set(x))+min(index_set(v)) ] ) ], + [ deopt(v[i]) | i in index_set(v) where occurs(v[i]) ] ) + endif; +/** @group annotations.warmstart.optvals Specify warm start values \a v for an array of floats \a x */ +annotation warm_start( array[int] of var float: x, array[int] of opt float: v ) = + if 0==length(x) \/ 0==length(v) then + warm_start( x, [] ) + else + warm_start( [ x[i] | i in index_set(x) where + i-min(index_set(x))+min(index_set(v))>length(v) \/ + occurs( v[ i-min(index_set(x))+min(index_set(v)) ] ) ], + [ deopt(v[i]) | i in index_set(v) where occurs(v[i]) ] ) + endif; +/** @group annotations.warmstart.optvals Specify warm start values \a v for an array of sets \a x */ +annotation warm_start( array[int] of var set of int: x, array[int] of opt set of int: v ) = + if 0==length(x) \/ 0==length(v) then + warm_start( x, [] ) + else + warm_start( [ x[i] | i in index_set(x) where + i-min(index_set(x))+min(index_set(v))>length(v) \/ + occurs( v[ i-min(index_set(x))+min(index_set(v)) ] ) ], + [ deopt(v[i]) | i in index_set(v) where occurs(v[i]) ] ) + endif; + + + +/*** + @groupdef optiontypes Option type support + + These functions and predicates implement the standard library for working + with option types. Note that option type support is still incomplete. +*/ + +/** @group optiontypes Return value of \a x if \a x is not absent. Aborts + when evaluated on absent value. */ +function $T: deopt(opt $T: x); + +/** @group optiontypes Return value \a x unchanged (since \a x is guaranteed + to be non-optional). */ +function var $T: deopt(var $T: x) = x; + +/** @group optiontypes Test if \a x is not absent (always returns true) */ +test occurs(var $T: x) = true; +/** @group optiontypes Test if \a x is not absent */ +test occurs(opt $T: x); +/** @group optiontypes Test if \a x is absent (always returns false) */ +test absent(var $T: x) = false; +/** @group optiontypes Test if \a x is absent */ +test absent(opt $T: x) = not occurs(x); + +/*** + @groupdef optiontypes.bool Option type support for Booleans +*/ + +/** @group optiontypes.bool True iff \a x is not absent */ +function var bool : occurs(var opt bool: x) ::promise_total = + let { + var bool : b = occurs_internal(x); + var bool : dx = deopt_internal(x); + constraint (x = reverse_map(b,dx)) :: is_reverse_map; + } in b; + +/** @group optiontypes.bool Return value of \a x (assumes that \a x is not absent) */ +function var bool : deopt(var opt bool : x) ::promise_total = + let { + var bool : b = occurs_internal(x); + var bool : dx = deopt_internal(x); + constraint (x = reverse_map(b,dx)) :: is_reverse_map; + } in dx; + +/** @group optiontypes.bool True iff \a x is absent */ +predicate absent(var opt bool: x) = not occurs(x); + +function var bool: occurs_internal(var opt bool: x) ::promise_total = + let { var bool : b; } in b; +function var bool : deopt_internal(var opt bool : x) ::promise_total = + let { var bool: y } in y; + +function var opt bool: reverse_map(var bool: occ, var bool: d); +function opt bool: reverse_map(bool: occ, bool: d) ::promise_total = + if occ then d else <> endif; + +predicate bool_opt_eq(var opt bool: x, var opt bool: y) = + deopt(x)=deopt(y) /\ occurs(x)=occurs(y); + +/** @group optiontypes.bool True iff both \a b0 and \a b1 are absent or + both are present and have the same value. */ +predicate bool_eq(var opt bool: b0, var opt bool: b1) = + (absent(b0) /\ absent(b1)) + \/ (occurs(b0) /\ occurs(b1) /\ deopt(b0)=deopt(b1)); + +/** @group optiontypes.bool True iff \a b0 occurs and is equal to \a b1 */ +predicate bool_eq(var opt bool: b0, var bool: b1) = + occurs(b0) /\ deopt(b0)=b1; + + /** @group optiontypes.bool True iff \a b1 occurs and is equal to \a b0 */ +predicate bool_eq(var bool: b0, var opt bool: b1) = + occurs(b1) /\ deopt(b1)=b0; + +/** @group optiontypes.bool True iff for any \p i, \a x[i] is absent or true */ +predicate forall (array[int] of var opt bool: x) = + forall ([absent(x[i]) \/ deopt(x[i]) | i in index_set(x)]); + +/** @group optiontypes.bool True iff for at least one \p i, \a x[i] occurs and is true */ +predicate exists (array[int] of var opt bool: x) = + exists ([occurs(x[i]) /\ deopt(x[i]) | i in index_set(x)]); + +% /** @group optiontypes.bool True iff \a x is absent or false */ +% function var bool: 'not'(var opt bool: x) = absent(x) \/ not deopt(x); + +% /** @group optiontypes.bool Return absent if \a idx is absent, otherwise return \a x[\a idx] */ +% function var opt bool: element(var opt int: idx, array[int] of var bool: x) = +% if absent(idx) then <> else element(deopt(idx),x) endif; + +% /** @group optiontypes.bool Return absent if \a idx1 or \a idx2 is absent, otherwise return \a x[\a idx1, \a idx2] */ +% function var opt bool: element(var opt int: idx1, var opt int: idx2, array[int,int] of var bool: x) = +% if absent(idx1) \/ absent(idx2) then <> else element(deopt(idx1),deopt(idx2),x) endif; + +% /** @group optiontypes.bool Return \a x[\a idx] */ +% function var opt bool: element(var int: idx, array[int] of var opt bool: x) = +% let { +% var opt bool: r; +% constraint occurs(r) = element(idx,array1d(index_set(x),[occurs(x[i]) | i in index_set(x)])); +% constraint deopt(r) = element(idx,array1d(index_set(x),[deopt(x[i]) | i in index_set(x)])); +% } in r; + +% /** @group optiontypes.bool Return \a x[\a idx1, \a idx2] */ +% function var opt bool: element(var int: idx1, var int: idx2, array[int,int] of var opt bool: x) = +% let { +% var opt bool: r; +% constraint occurs(r) = element(idx1,idx2, +% array2d(index_set_1of2(x),index_set_2of2(x),[occurs(x[i,j]) | i in index_set_1of2(x), j in index_set_2of2(x)])); +% constraint deopt(r) = element(idx1,idx2, +% array2d(index_set_1of2(x),index_set_2of2(x),[deopt(x[i,j]) | i in index_set_1of2(x), j in index_set_2of2(x)])); +% } in r; + +% /** @group optiontypes.bool Return absent if \a idx is absent, otherwise return \a x[\a idx] */ +% function var opt bool: element(var opt int: idx, array[int] of var opt bool: x) = +% if absent(idx) then <> else element(deopt(idx),x) endif; + +% /** @group optiontypes.bool Return absent if \a idx1 or \a idx2 is absent, otherwise return \a x[\a idx1, \a idx2] */ +% function var opt bool: element(var opt int: idx1, var opt int: idx2, array[int,int] of var opt bool: x) = +% if absent(idx1) \/ absent(idx2) then <> else element(deopt(idx1),deopt(idx2),x) endif; + + + +/*** + @groupdef optiontypes.int Option type support for integers +*/ + +/** @group optiontypes.int True iff \a x is not absent */ +function var bool : occurs(var opt int : x) ::promise_total = + let { + var bool : b = occurs_internal(x); + var int : dx = deopt_internal(x); + constraint (x = reverse_map(b,dx)) :: is_reverse_map; + } in b; + +/** @group optiontypes.bool Return value of \a x (assumes that \a x is not absent) */ +function var int : deopt(var opt int : x) ::promise_total = + let { + var bool : b = occurs_internal(x); + var int : dx = deopt_internal(x); + constraint (x = reverse_map(b,dx)) :: is_reverse_map; + } in dx; + +/** @group optiontypes.bool True iff \a x is absent */ +function var bool: absent(var opt int: x) ::promise_total = not occurs(x); + +function var bool: occurs_internal(var opt int: x) ::promise_total = + let { var bool : b; } in b; +function var int : deopt_internal(var opt int : x) ::promise_total = + let { var lb(x)..ub(x): y } in y; + +function var opt int: reverse_map(var bool: occ, var int: d); +function opt int: reverse_map(bool: occ, int: d) ::promise_total = + if occ then d else <> endif; + +predicate var_dom(var opt int:x, set of int: s) = + let { + var int: dx = deopt(x); + set of int: new_dom = dom(dx) intersect s; + } in if new_dom = {} then absent(x) else dx in new_dom endif; + +predicate var_dom(array[$T] of var opt int: x, set of int: d) = + let { array[int] of var opt int: xx = array1d(x) } + in forall (i in index_set(xx)) (var_dom(xx[i],d)); + +predicate int_opt_eq(var opt int: x, var opt int: y) = + deopt(x) = deopt(y) /\ occurs(x) = occurs(y); + +/** @group optiontypes.bool Search annotation for optional Boolean variables */ +annotation bool_search(array[int] of var opt bool: x, ann: a1, ann: a2, ann: a3) = + bool_search([if occurs(x[i]) then deopt(x[i]) else false endif | i in index_set(x)],a1,a2,a3); + +/** @group optiontypes.bool Search annotation for optional Boolean variables */ +annotation bool_search(array[int] of var opt bool: x, ann: a1, ann: a2) = + bool_search([if occurs(x[i]) then deopt(x[i]) else false endif | i in index_set(x)],a1,a2); + +/** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both + occur and the value of \a x is greater than the value of \a y. */ +function var bool: '>'(var opt int: x, var opt int: y) = absent(x) \/ absent(y) \/ deopt(x) > deopt(y); +/** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both + occur and the value of \a x is greater than or equal to the value of \a y. */ +function var bool: '>='(var opt int: x, var opt int: y) = absent(x) \/ absent(y) \/ deopt(x) >= deopt(y); +% /** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both +% occur and the value of \a x is less than the value of \a y. */ +% function var bool: '<'(var opt int: x, var opt int: y) = absent(x) \/ absent(y) \/ deopt(x) < deopt(y); +% /** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both +% occur and the value of \a x is less than or equal to the value of \a y. */ +% function var bool: '<='(var opt int: x, var opt int: y) = absent(x) \/ absent(y) \/ deopt(x) <= deopt(y); + +/** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both + occur and the value of \a x is greater than the value of \a y. */ +function bool: '>'(opt int: x, opt int: y) = absent(x) \/ absent(y) \/ deopt(x) > deopt(y); +/** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both + occur and the value of \a x is greater than or equal to the value of \a y. */ +function bool: '>='(opt int: x, opt int: y) = absent(x) \/ absent(y) \/ deopt(x) >= deopt(y); +% /** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both +% occur and the value of \a x is less than the value of \a y. */ +% function bool: '<'(opt int: x, opt int: y) = absent(x) \/ absent(y) \/ deopt(x) < deopt(y); +% /** @group optiontypes.int Weak comparison: true iff either \a x or \a y is absent, or both +% occur and the value of \a x is less than or equal to the value of \a y. */ +% function bool: '<='(opt int: x, opt int: y) = absent(x) \/ absent(y) \/ deopt(x) <= deopt(y); + +% /** @group optiontypes.int Return minimum of elements in \a x that are not absent, or +% absent if all elements in \a x are absent. */ +/* +function var opt int: min(array[int] of var opt int: x) ::promise_total = + let { + var opt lb_array(x)..ub_array(x): m; + var lb_array(x)..ub_array(x): xmax; + constraint if ub(xmax) != infinity then xmax = ub(xmax) else forall (i in index_set(x)) (xmax >= deopt(x[i])) endif; + constraint occurs(m) <-> exists (i in index_set(x)) (occurs(x[i])); + constraint occurs(m) -> + deopt(m) = min([if occurs(xi) then deopt(xi) else xmax endif | xi in x]); + } in m; +*/ + +% /** @group optiontypes.int Return maximum of elements in \a x that are not absent, or +% absent if all elements in \a x are absent. +/* +function var opt int: max(array[int] of var opt int: x) ::promise_total = + let { + var opt lb_array(x)..ub_array(x): m; + var lb_array(x)..ub_array(x): xmin; + constraint if lb(xmin) != -infinity then xmin = lb(xmin) else forall (i in index_set(x)) (xmin <= deopt(x[i])) endif; + constraint occurs(m) <-> exists (i in index_set(x)) (occurs(x[i])); + constraint occurs(m) -> + deopt(m) = max([if occurs(xi) then deopt(xi) else xmin endif | xi in x]); + } in m; +*/ + +/** @group optiontypes.int Weak addition. Return sum of \a x and \a y if both +are present, otherwise return absent. */ +function var opt int: '~+'(var opt int: x, var opt int: y) ::promise_total = + let { + int: l = if lb(x)=-infinity \/ lb(y)=-infinity then -infinity else lb(x)+lb(y) endif; + int: u = if ub(x)=infinity \/ ub(y)=infinity then infinity else ub(x)+ub(y) endif; + var opt l..u: result; + constraint absent(x) \/ absent(y) -> result = <>; + constraint absent(x) \/ absent(y) \/ result = deopt(x)+deopt(y); + } in result; + +/** @group optiontypes.int Weak subtraction. Return difference of \a x and \a y if both +are present, otherwise return absent. */ +function var opt int: '~-'(var opt int: x, var opt int: y) ::promise_total = + let { + int: l = if lb(x)=-infinity \/ ub(y)=infinity then -infinity else lb(x)-ub(y) endif; + int: u = if ub(x)=infinity \/ lb(y)=-infinity then infinity else ub(x)-lb(y) endif; + var opt l..u: result; + constraint absent(x) \/ absent(y) -> result = <>; + constraint absent(x) \/ absent(y) \/ result = deopt(x)-deopt(y); + } in result; + +/** @group optiontypes.int Weak multiplication. Return product of \a x and \a y if both +are present, otherwise return absent. */ +function var opt int: '~*'(var opt int: x, var opt int: y) ::promise_total = + if absent(x) \/ absent(y) then <> + else deopt(x)*deopt(y) endif; + +/** @group optiontypes.int Weak equality. True if either \a x or \a y are absent, or +present and equal.*/ +function var bool: '~='(var opt int: x, var opt int: y) ::promise_total = + absent(x) \/ absent(y) \/ deopt(x)=deopt(y); + +/** @group optiontypes.int Return optional 0/1 integer that is absent iff \a x + is absent, and 1 iff \a x occurs and is true. */ +function var opt int: bool2int(var opt bool: x) ::promise_total = + let { + var opt 0..1: xi; + constraint absent(xi)=absent(x); + constraint deopt(xi)=bool2int(deopt(x)); + } in xi; + +/** @group optiontypes.int True iff both \a x and \a y are absent or + both are present and have the same value. */ +predicate int_eq(var opt int: x, var opt int: y) = + (absent(x) /\ absent(y)) + \/ (occurs(x) /\ occurs(y) /\ (deopt(x)=deopt(y))::maybe_partial); + +/** @group optiontypes.int True iff only one of \a x and \a y is absent or + both are present and have different values. */ +predicate int_ne(var opt int : x, var opt int : y) = + (absent(x) != absent(y)) + \/ (occurs(x) /\ occurs(y) /\ (deopt(x)!=deopt(y))::maybe_partial); + +% /** @group optiontypes.int Optional addition. Return sum of \a x and \a y, with absent replaced by 0. */ +% function var int: '+'(var opt int: x, var opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 0 endif + if occurs(y) then deopt(y) else 0 endif; + +% /** @group optiontypes.int Optional addition. Return sum of \a x and \a y, with absent replaced by 0. */ +% function int: '+'(opt int: x, opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 0 endif + if occurs(y) then deopt(y) else 0 endif; + +% /** @group optiontypes.int Optional subtraction. Return difference of \a x and \a y, with absent replaced by 0. */ +% function var int: '-'(var opt int: x, var opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 0 endif - if occurs(y) then deopt(y) else 0 endif; + +% /** @group optiontypes.int Optional subtraction. Return difference of \a x and \a y, with absent replaced by 0. */ +% function int: '-'(opt int: x, opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 0 endif - if occurs(y) then deopt(y) else 0 endif; + +% /** @group optiontypes.int Optional multiplication. Return product of \a x and \a y, with absent replaced by 1. */ +% function var int: '*'(var opt int: x, var opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 1 endif * if occurs(y) then deopt(y) else 1 endif; + +% /** @group optiontypes.int Optional multiplication. Return product of \a x and \a y, with absent replaced by 1. */ +% function int: '*'(opt int: x, opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 1 endif * if occurs(y) then deopt(y) else 1 endif; + +% /** @group optiontypes.int Optional division. Return \a x div \a y, with absent replaced by 1. */ +% function var int: 'div'(var opt int: x, var opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 1 endif div if occurs(y) then deopt(y) else 1 endif; + +% /** @group optiontypes.int Optional division. Return \a x div \a y, with absent replaced by 1. */ +% function int: 'div'(opt int: x, opt int: y) ::promise_total = +% if occurs(x) then deopt(x) else 1 endif div if occurs(y) then deopt(y) else 1 endif; + +/** @group optiontypes.int Optional modulo. Return \a x mod \a y, with absent replaced by 1. */ +function var int: 'mod'(var opt int: x, var opt int: y) ::promise_total = + if occurs(x) then deopt(x) else 1 endif mod if occurs(y) then deopt(y) else 1 endif; + +/** @group optiontypes.int Optional modulo. Return \a x mod \a y, with absent replaced by 1. */ +function int: 'mod'(opt int: x, opt int: y) ::promise_total = + if occurs(x) then deopt(x) else 1 endif mod if occurs(y) then deopt(y) else 1 endif; + +/** @group optiontypes.int Return sum of non-absent elements of \a x. */ +function var int: sum(array[int] of var opt int: x) = + sum (i in index_set(x)) (let { var int: dx = deopt(x[i]) } in if occurs(x[i]) then dx else 0 endif); + +/** @group optiontypes.int Return sum of non-absent elements of \a x. */ +function int: sum(array[int] of opt int: x) = + sum (i in index_set(x)) (if occurs(x[i]) then deopt(x[i]) else 0 endif); + +/** @group optiontypes.int Return product of non-absent elements of \a x. */ +function var int: product(array[int] of var opt int: x) = + product (i in index_set(x)) (let { var int: dx = deopt(x[i]) } in if occurs(x[i]) then dx else 1 endif); + +/** @group optiontypes.int Return product of non-absent elements of \a x. */ +function int: product(array[int] of opt int: x) = + product (i in index_set(x)) (if occurs(x[i]) then deopt(x[i]) else 1 endif); + +% /** @group optiontypes.int Return absent if \a idx is absent, otherwise return \a x[\a idx] */ +% function var opt int: element(var opt int: idx, array[int] of var int: x) = +% if absent(idx) then <> else element(deopt(idx),x) endif; +% +% /** @group optiontypes.int Return absent if \a idx1 or \a idx2 is absent, otherwise return \a x[\a idx1, \a idx2] */ +% function var opt int: element(var opt int: idx1, var opt int: idx2, array[int,int] of var int: x) = +% if absent(idx1) \/ absent(idx2) then <> else element(deopt(idx1),deopt(idx2),x) endif; +% +% /** @group optiontypes.int Return \a x[\a idx] */ +% function var opt int: element(var int: idx, array[int] of var opt int: x) = +% let { +% var opt int: r; +% constraint occurs(r) = element(idx,array1d(index_set(x),[occurs(x[i]) | i in index_set(x)])); +% constraint deopt(r) = element(idx,array1d(index_set(x),[deopt(x[i]) | i in index_set(x)])); +% } in r; +% +% /** @group optiontypes.int Return \a x[\a idx1, \a idx2] */ +% function var opt int: element(var int: idx1, var int: idx2, array[int,int] of var opt int: x) = +% let { +% var opt int: r; +% constraint occurs(r) = element(idx1,idx2, +% array2d(index_set_1of2(x),index_set_2of2(x),[occurs(x[i,j]) | i in index_set_1of2(x), j in index_set_2of2(x)])); +% constraint deopt(r) = element(idx1,idx2, +% array2d(index_set_1of2(x),index_set_2of2(x),[deopt(x[i,j]) | i in index_set_1of2(x), j in index_set_2of2(x)])); +% } in r; +% +% /** @group optiontypes.int Return absent if \a idx is absent, otherwise return \a x[\a idx] */ +% function var opt int: element(var opt int: idx, array[int] of var opt int: x) = +% if absent(idx) then <> else element(deopt(idx),x) endif; +% +% /** @group optiontypes.int Return absent if \a idx1 or \a idx2 is absent, otherwise return \a x[\a idx1, \a idx2] */ +% function var opt int: element(var opt int: idx1, var opt int: idx2, array[int,int] of var opt int: x) = +% if absent(idx1) \/ absent(idx2) then <> else element(deopt(idx1),deopt(idx2),x) endif; + +/** @group optiontypes.int Search annotation for optional integer variables */ +annotation int_search(array[int] of var opt int: x, ann: a1, ann: a2, ann: a3) = + int_search([if occurs(x[i]) then deopt(x[i]) else 0 endif | i in index_set(x)],a1,a2,a3); + +/** @group optiontypes.int Search annotation for optional integer variables */ +annotation int_search(array[int] of var opt int: x, ann: a1, ann: a2) = + int_search([if occurs(x[i]) then deopt(x[i]) else 0 endif | i in index_set(x)],a1,a2); + +/* Internal function used to optimize over option type objective */ + +function var int: objective_deopt_(var opt int: x, bool: direction) = + let { + int: worst = if direction then lb(x)-1 else ub(x)+1 endif; + } in if occurs(x) then deopt(x) else worst endif; + +/* TODO: predicate to temporarily replace the output items */ + +predicate output_this(array[int] of var int: arr); +predicate solve_this(int: mode, var int: objective, array[int] of var int: x, int: varsel, int: valsel); +predicate solve_this(int: mode, var int: objective); + +function int: sol(var int: x); + +/*** + @groupdef options Compiler options +*/ + +/* @group options Whether to only generate domains that are contiguous ranges */ +/* opt bool: mzn_opt_only_range_domains; */ + +/* @group options Check whether to only generate domains that are contiguous ranges */ +/* test mzn_check_only_range_domains() = */ +/* if absent(mzn_opt_only_range_domains) then false */ +/* else deopt(mzn_opt_only_range_domains) endif; */ + +/* @group options If defined, this can be used to check that the MiniZinc compiler supports all the features used in the model. */ +/* opt int: mzn_min_version_required; */ + +/* constraint assert(absent(mzn_min_version_required) \/ deopt(mzn_min_version_required) <= mzn_compiler_version(), "This model requires MiniZinc version "++mzn_version_to_string(deopt(mzn_min_version_required))++" but you are running version "++mzn_version_to_string(mzn_compiler_version())); */ + +predicate set_in(array[$T] of var int: X, set of int: s) = forall(x in array1d(X)) (x in s); +predicate int_eq(array[$T] of var int: X, int: s) = forall(x in array1d(X)) (x = s); +predicate float_eq(array[$T] of var int: X, float: s) = forall(x in array1d(X)) (x = s); +predicate int_le(array[$T] of var int: X, int: s) = forall(x in array1d(X)) (x <= s); +predicate int_le(int:s, array[$T] of var int: X) = forall(x in array1d(X)) (x >= s); +predicate float_le(array[$T] of var float: X, float: s) = forall(x in array1d(X)) (x <= s); +predicate float_le(float:s, array[$T] of var float: X) = forall(x in array1d(X)) (x >= s); + +include "builtins.mzn"; diff --git a/share/minizinc/std/steiner.mzn b/share/minizinc/std/steiner.mzn new file mode 100644 index 0000000..d84885d --- /dev/null +++ b/share/minizinc/std/steiner.mzn @@ -0,0 +1,58 @@ +include "fzn_steiner.mzn"; +include "fzn_steiner_reif.mzn"; +include "fzn_dsteiner.mzn"; +include "fzn_dsteiner_reif.mzn"; +include "weighted_spanning_tree.mzn"; + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be a weighted spanning tree rooted at \a r of weight \a W. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a w is the weight of each edge + \a r is the root node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the weight of the tree +*/ +predicate dsteiner(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: r, array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + assert(index_set(from) = 1..E,"dsteiner: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"dsteiner: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"dsteiner: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"dsteiner: index set of es must be 1..\(E)") /\ + assert(index_set(w) = 1..E,"dsteiner: index set of w must be 1..\(E)") /\ + if forall(n in 1..N)(is_fixed(ns[n]) /\ fix(ns[n])) then + d_weighted_spanning_tree(N,E,from,to,w,r,es,K) + else + fzn_dsteiner(N,E,from,to,w,r,ns,es,K) + endif; + +/** @group globals.graph + Constrains the set of edges \a es of a given undirected graph to be a weighted spanning tree of weight \a W. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a w is the weight of each edge + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the weight of the tree +**/ +predicate steiner(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + array[int] of var bool: ns, array[int] of var bool: es, var int: K) = + assert(index_set(from) = 1..E,"steiner: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"steiner: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"steiner: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"steiner: index set of es must be 1..\(E)") /\ + assert(index_set(w) = 1..E,"steiner: index set of w must be 1..\(E)") /\ + if forall(n in 1..N)(is_fixed(ns[n]) /\ fix(ns[n])) then + weighted_spanning_tree(N,E,from,to,w,es,K) + else + fzn_steiner(N,E,from,to,w,ns,es,K) + endif; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/strict_lex2.mzn b/share/minizinc/std/strict_lex2.mzn new file mode 100644 index 0000000..2cacad7 --- /dev/null +++ b/share/minizinc/std/strict_lex2.mzn @@ -0,0 +1,9 @@ +include "fzn_strict_lex2.mzn"; +include "fzn_strict_lex2_reif.mzn"; + +/** @group globals.lexicographic + Require adjacent rows and adjacent columns in the array \a x to be + lexicographically ordered. Adjacent rows and adjacent columns cannot be equal. +*/ +predicate strict_lex2(array[int, int] of var int: x) = + fzn_strict_lex2(x); diff --git a/share/minizinc/std/strictly_decreasing.mzn b/share/minizinc/std/strictly_decreasing.mzn new file mode 100644 index 0000000..70c4eb3 --- /dev/null +++ b/share/minizinc/std/strictly_decreasing.mzn @@ -0,0 +1,23 @@ +include "fzn_strictly_decreasing_int.mzn"; +include "fzn_strictly_decreasing_int_reif.mzn"; +include "fzn_strictly_decreasing_bool.mzn"; +include "fzn_strictly_decreasing_bool_reif.mzn"; +include "analyse_all_different.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict decreasing order +%-----------------------------------------------------------------------------% + +predicate strictly_decreasing(array[int] of var bool: x) = + analyse_all_different(x) /\ + fzn_strictly_decreasing_bool(x); + +predicate strictly_decreasing_reif(array[int] of var bool: x, var bool: b) = + fzn_strictly_decreasing_bool_reif(x,b); + +predicate strictly_decreasing(array[int] of var int: x) = + analyse_all_different(x) /\ + fzn_strictly_decreasing_int(x); + +predicate strictly_decreasing_reif(array[int] of var int: x, var bool: b) = + fzn_strictly_decreasing_int_reif(x,b); diff --git a/share/minizinc/std/strictly_increasing.mzn b/share/minizinc/std/strictly_increasing.mzn new file mode 100644 index 0000000..58360ba --- /dev/null +++ b/share/minizinc/std/strictly_increasing.mzn @@ -0,0 +1,24 @@ +include "fzn_strictly_increasing_int.mzn"; +include "fzn_strictly_increasing_int_reif.mzn"; +include "fzn_strictly_increasing_bool.mzn"; +include "fzn_strictly_increasing_bool_reif.mzn"; +include "analyse_all_different.mzn"; + +%-----------------------------------------------------------------------------% +% Requires that the array 'x' is in strict increasing order +%-----------------------------------------------------------------------------% + +predicate strictly_increasing(array[int] of var bool: x) = + analyse_all_different(x) /\ + fzn_strictly_increasing_bool(x); + +predicate strictly_increasing_reif(array[int] of var bool: x, var bool: b) = + fzn_strictly_increasing_bool_reif(x,b); + +predicate strictly_increasing(array[int] of var int: x) = + analyse_all_different(x) /\ + fzn_strictly_increasing_int(x); + +predicate strictly_increasing_reif(array[int] of var int: x, var bool: b) = + fzn_strictly_increasing_int_reif(x,b); + diff --git a/share/minizinc/std/subcircuit.mzn b/share/minizinc/std/subcircuit.mzn new file mode 100644 index 0000000..30a0ab3 --- /dev/null +++ b/share/minizinc/std/subcircuit.mzn @@ -0,0 +1,15 @@ +include "fzn_subcircuit.mzn"; +include "fzn_subcircuit_reif.mzn"; + +/** @group globals + Constrains the elements of \a x to define a subcircuit where \a x[\p i] = \p j + means that \p j is the successor of \p i and \a x[\p i] = \p i means that \p i + is not in the circuit. +*/ +predicate subcircuit(array[int] of var int: x) = + fzn_subcircuit(x); + +predicate subcircuit_reif(array[int] of var int: x, var bool: b) = + fzn_subcircuit_reif(x, b); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/subgraph.mzn b/share/minizinc/std/subgraph.mzn new file mode 100644 index 0000000..56de660 --- /dev/null +++ b/share/minizinc/std/subgraph.mzn @@ -0,0 +1,40 @@ +include "fzn_subgraph_int.mzn"; +include "fzn_subgraph_int_reif.mzn"; +include "fzn_subgraph_enum.mzn"; +include "fzn_subgraph_enum_reif.mzn"; + +/** @group globals.graph + Constrains that \a ns and \a es is a subgraph of a given directed graph. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate subgraph(int: N, int: E, array[int] of int: from, array[int] of int: to, + array[int] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = 1..E,"subgraph: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"subgraph: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"subgraph: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"subgraph: index set of es must be 1..\(E)") /\ + fzn_subgraph(N,E,from,to,ns,es); + +/** @group globals.graph + Constrains that \a ns and \a es is a subgraph of a given directed graph. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate subgraph(array[int] of $$N: from, array[int] of $$N: to, + array[$$N] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = index_set(to),"subgraph: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"subgraph: index set of from and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"subgraph: elements in from must be in index set of ns") /\ + assert(dom_array(to) subset index_set(ns),"subgraph: elements in to must be in index set of ns") /\ + fzn_subgraph(from,to,ns,es); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/sum_pred.mzn b/share/minizinc/std/sum_pred.mzn new file mode 100644 index 0000000..46eacd5 --- /dev/null +++ b/share/minizinc/std/sum_pred.mzn @@ -0,0 +1,13 @@ +include "fzn_sum_pred.mzn"; +include "fzn_sum_pred_reif.mzn"; + +/** @group globals + Requires that the sum of \a cs[\p i1]..\a cs[\p iN] equals \a s, + where \p i1..\p iN are the elements of the \a i th set in \a sets. + + Nb: not called 'sum' as in the constraints catalog because 'sum' is a + MiniZinc built-in function. +*/ +predicate sum_pred(var int: i, array[int] of set of int: sets, + array[int] of int: cs, var int: s) = + fzn_sum_pred(i, sets, cs, s); diff --git a/share/minizinc/std/sum_set.mzn b/share/minizinc/std/sum_set.mzn new file mode 100644 index 0000000..3ede3e2 --- /dev/null +++ b/share/minizinc/std/sum_set.mzn @@ -0,0 +1,11 @@ +include "fzn_sum_set.mzn"; +include "fzn_sum_set_reif.mzn"; + +/** @group globals + Requires that the sum of the weights \a ws[\p i1]..\a ws[\p iN] equals \a s, + where \a vs[\p i1]..\a vs[\p iN] are the elements appearing in set \a x +*/ + +predicate sum_set(array[int] of int: vs, array[int] of int: ws, + var set of int: x, var int: s) = + fzn_sum_set(vs, ws, x, s); diff --git a/share/minizinc/std/symmetric_all_different.mzn b/share/minizinc/std/symmetric_all_different.mzn new file mode 100644 index 0000000..0e6150e --- /dev/null +++ b/share/minizinc/std/symmetric_all_different.mzn @@ -0,0 +1,14 @@ +include "analyse_all_different.mzn"; +include "fzn_symmetric_all_different.mzn"; +include "fzn_symmetric_all_different_reif.mzn"; + +/** @group globals.alldifferent + Requires the array of integers \a x to be all different, and for all \p i, + \a x[\p i]=j \(\rightarrow\) \a x[\p j]=\p i. +*/ +predicate symmetric_all_different(array[int] of var int:x) = + analyse_all_different(x) /\ + fzn_symmetric_all_different(x); + +predicate symmetric_all_different_reif(array[int] of var int:x, var bool: b) = + fzn_symmetric_all_different_reif(x,b); diff --git a/share/minizinc/std/table.mzn b/share/minizinc/std/table.mzn new file mode 100644 index 0000000..9447fdf --- /dev/null +++ b/share/minizinc/std/table.mzn @@ -0,0 +1,24 @@ +include "table_bool.mzn"; +include "table_int.mzn"; + +/** @group globals.extensional + Represents the constraint \a x in \a t where we + consider each row in \a t to be a tuple and \a t as a set of tuples. +*/ +predicate table(array[int] of var bool: x, array[int, int] of bool: t) = + assert (index_set_2of2(t) == index_set(x), + "The second dimension of the table must equal the number of variables " + ++ "in the first argument", + table_bool(x, t) + ); + +/** @group globals.extensional + Represents the constraint \a x in \a t where we + consider each row in \a t to be a tuple and \a t as a set of tuples. +*/ +predicate table(array[int] of var int: x, array[int, int] of int: t) = + assert (index_set_2of2(t) == index_set(x), + "The second dimension of the table must equal the number of variables " + ++ "in the first argument", + table_int(x, t) + ); diff --git a/share/minizinc/std/table_bool.mzn b/share/minizinc/std/table_bool.mzn new file mode 100644 index 0000000..812fa9a --- /dev/null +++ b/share/minizinc/std/table_bool.mzn @@ -0,0 +1,16 @@ +include "fzn_table_bool.mzn"; +include "fzn_table_bool_reif.mzn"; + +%-----------------------------------------------------------------------------% +% A table constraint: table(x, t) represents the constraint x in t where we +% consider each row in t to be a tuple and t as a set of tuples. +%-----------------------------------------------------------------------------% + +predicate table_bool(array[int] of var bool: x, array[int, int] of bool: t) = + fzn_table_bool(x, t); + +predicate table_bool_reif(array[int] of var bool: x, array[int, int] of bool: t, + var bool: b) = + fzn_table_bool_reif(x, t, b); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/table_int.mzn b/share/minizinc/std/table_int.mzn new file mode 100644 index 0000000..153b297 --- /dev/null +++ b/share/minizinc/std/table_int.mzn @@ -0,0 +1,16 @@ +include "fzn_table_int.mzn"; +include "fzn_table_int_reif.mzn"; + +%-----------------------------------------------------------------------------% +% A table constraint table(x, t) represents the constraint x in t where we +% consider each row in t to be a tuple and t as a set of tuples. +%-----------------------------------------------------------------------------% + +predicate table_int(array[int] of var int: x, array[int, int] of int: t) = + fzn_table_int(x, t); + +predicate table_int_reif(array[int] of var int: x, array[int, int] of int: t, + var bool: b) = + fzn_table_int_reif(x, t, b); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/tree.mzn b/share/minizinc/std/tree.mzn new file mode 100644 index 0000000..655279d --- /dev/null +++ b/share/minizinc/std/tree.mzn @@ -0,0 +1,82 @@ +include "fzn_tree_int.mzn"; +include "fzn_tree_int_reif.mzn"; +include "fzn_tree_enum.mzn"; +include "fzn_tree_enum_reif.mzn"; +include "fzn_dtree_int.mzn"; +include "fzn_dtree_int_reif.mzn"; +include "fzn_dtree_enum.mzn"; +include "fzn_dtree_enum_reif.mzn"; + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be a tree rooted at \a r. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a r is the root node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate dtree(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = 1..E,"dtree: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"dtree: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"dtree: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"dtree: index set of es must be 1..\(E)") /\ + fzn_dtree(N,E,from,to,r,ns,es); + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given directed graph to be at tree rooted at \a r. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a r is the root node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate dtree(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = index_set(to),"dreachable: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"dreachable: index set of from and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"dreachable: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"dreachable: nodes in to must be in index set of ns") /\ + fzn_dtree(from,to,r,ns,es); + +%-----------------------------------------------------------------------------% + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given undirected graph to be a tree rooted at \a r. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a r is the root node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate tree(int: N, int: E, array[int] of int: from, array[int] of int: to, + var int: r, array[int] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = 1..E,"tree: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"tree: index set of to must be 1..\(E)") /\ + assert(index_set(ns) = 1..N,"tree: index set of ns must be 1..\(N)") /\ + assert(index_set(es) = 1..E,"tree: index set of es must be 1..\(E)") /\ + fzn_tree(N,E,from,to,r,ns,es); + +/** @group globals.graph + Constrains the subgraph \a ns and \a es of a given undirected graph to be at tree rooted at \a r. + + \a from is the leaving node for each edge + \a to is the entering node for each edge + \a r is the root node (which may be variable) + \a ns is a Boolean for each node whether it is in the subgraph + \a es is a Boolean for each edge whether it is in the subgraph +*/ +predicate tree(array[int] of $$N: from, array[int] of $$N: to, + var $$N: r, array[$$N] of var bool: ns, array[int] of var bool: es) = + assert(index_set(from) = index_set(to),"dreachable: index set of from and to must be identical") /\ + assert(index_set(from) = index_set(es),"dreachable: index set of from and es must be identical") /\ + assert(dom_array(from) subset index_set(ns),"dreachable: nodes in from must be in index set of ns") /\ + assert(dom_array(from) subset index_set(ns),"dreachable: nodes in to must be in index set of ns") /\ + fzn_tree(from,to,r,ns,es); \ No newline at end of file diff --git a/share/minizinc/std/value_precede.mzn b/share/minizinc/std/value_precede.mzn new file mode 100644 index 0000000..3eb10a9 --- /dev/null +++ b/share/minizinc/std/value_precede.mzn @@ -0,0 +1,22 @@ +include "value_precede_int.mzn"; +include "value_precede_set.mzn"; + +/** @group globals.lexicographic + Requires that \a s precede \a t in the array \a x. + + Precedence means that if any element of \a x + is equal to \a t, then another element of \a x with a lower index is equal + to \a s. +*/ +predicate value_precede(int: s, int: t, array[int] of var int: x) = + value_precede_int(s, t, x); + +/** @group globals.lexicographic + Requires that \a s precede \a t in the array \a x. + + Precedence means that if an element of \a x + contains \a t but not \a s, then another element of \a x with lower index contains + \a s but not \a t. +*/ +predicate value_precede(int: s, int: t, array[int] of var set of int: x) = + value_precede_set(s, t, x); diff --git a/share/minizinc/std/value_precede_chain.mzn b/share/minizinc/std/value_precede_chain.mzn new file mode 100644 index 0000000..1c142dc --- /dev/null +++ b/share/minizinc/std/value_precede_chain.mzn @@ -0,0 +1,22 @@ +include "value_precede_chain_int.mzn"; +include "value_precede_chain_set.mzn"; + +/** @group globals.lexicographic + Requires that \a c[\p i] precedes \a c[\p i +1] in the array \a x. + + Precedence means that if any element of \a x + is equal to \a c[\p i +1], then another element of \a x with a lower index is equal + to \a c[\p i]. +*/ +predicate value_precede_chain(array[int] of int: c, array[int] of var int: x) = + value_precede_chain_int(c, x); + +/** @group globals.lexicographic + Requires that \a c[\p i] precedes \a c[\p i +1] in the array \a x. + + Precedence means that if an element of \a x + contains \a c[\p i +1] but not \a c[\p i], then another element of \a x with lower index contains + \a c[\p i] but not \a c[\p i +1]. +*/ +predicate value_precede_chain(array[int] of int: c, array[int] of var set of int: x) = + value_precede_chain_set(c, x); diff --git a/share/minizinc/std/value_precede_chain_int.mzn b/share/minizinc/std/value_precede_chain_int.mzn new file mode 100644 index 0000000..549fadf --- /dev/null +++ b/share/minizinc/std/value_precede_chain_int.mzn @@ -0,0 +1,5 @@ +include "fzn_value_precede_chain_int.mzn"; +include "fzn_value_precede_chain_int_reif.mzn"; + +predicate value_precede_chain_int(array[int] of int: c, array[int] of var int: x) = + fzn_value_precede_chain_int(c, x); diff --git a/share/minizinc/std/value_precede_chain_set.mzn b/share/minizinc/std/value_precede_chain_set.mzn new file mode 100644 index 0000000..4edb2c2 --- /dev/null +++ b/share/minizinc/std/value_precede_chain_set.mzn @@ -0,0 +1,5 @@ +include "fzn_value_precede_chain_set.mzn"; +include "fzn_value_precede_chain_set_reif.mzn"; + +predicate value_precede_chain_set(array[int] of int: c, array[int] of var set of int: x) = + fzn_value_precede_chain_set(c, x); diff --git a/share/minizinc/std/value_precede_int.mzn b/share/minizinc/std/value_precede_int.mzn new file mode 100644 index 0000000..1e3695b --- /dev/null +++ b/share/minizinc/std/value_precede_int.mzn @@ -0,0 +1,5 @@ +include "fzn_value_precede_int.mzn"; +include "fzn_value_precede_int_reif.mzn"; + +predicate value_precede_int(int: s, int: t, array[int] of var int: x) = + fzn_value_precede_int(s, t, x); \ No newline at end of file diff --git a/share/minizinc/std/value_precede_set.mzn b/share/minizinc/std/value_precede_set.mzn new file mode 100644 index 0000000..f4d27f6 --- /dev/null +++ b/share/minizinc/std/value_precede_set.mzn @@ -0,0 +1,5 @@ +include "fzn_value_precede_set.mzn"; +include "fzn_value_precede_set_reif.mzn"; + +predicate value_precede_set(int: s, int: t, array[int] of var set of int: x) = + fzn_value_precede_set(s, t, x); diff --git a/share/minizinc/std/weighted_spanning_tree.mzn b/share/minizinc/std/weighted_spanning_tree.mzn new file mode 100644 index 0000000..7c8bf9d --- /dev/null +++ b/share/minizinc/std/weighted_spanning_tree.mzn @@ -0,0 +1,45 @@ +include "fzn_wst.mzn"; +include "fzn_wst_reif.mzn"; +include "fzn_dwst.mzn"; +include "fzn_dwst_reif.mzn"; + +/** @group globals.graph + Constrains the set of edges \a es of a given directed graph to be a weighted spanning tree rooted at \a r of weight \a W. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a w is the weight of each edge + \a r is the root node (which may be variable) + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the weight of the tree +*/ +predicate d_weighted_spanning_tree(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + var int: r, array[int] of var bool: es, var int: K) = + assert(index_set(from) = 1..E,"dwst: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"dwst: index set of to must be 1..\(E)") /\ + assert(index_set(es) = 1..E,"dwst: index set of es must be 1..\(E)") /\ + assert(index_set(w) = 1..E,"dwst: index set of w must be 1..\(E)") /\ + fzn_dwst(N,E,from,to,w,r,es,K); + +/** @group globals.graph + Constrains the set of edges \a es of a given undirected graph to be a weighted spanning tree of weight \a W. + + \a N is the number of nodes in the given graph + \a E is the number of edges in the given graph + \a from is the leaving node 1..\a N for each edge + \a to is the entering node 1..\a N for each edge + \a w is the weight of each edge + \a es is a Boolean for each edge whether it is in the subgraph + \a K is the weight of the tree +**/ +predicate weighted_spanning_tree(int: N, int: E, array[int] of int: from, array[int] of int: to, array[int] of int: w, + array[int] of var bool: es, var int: K) = + assert(index_set(from) = 1..E,"wst: index set of from must be 1..\(E)") /\ + assert(index_set(to) = 1..E,"wst: index set of to must be 1..\(E)") /\ + assert(index_set(es) = 1..E,"wst: index set of es must be 1..\(E)") /\ + assert(index_set(w) = 1..E,"dwst: index set of w must be 1..\(E)") /\ + fzn_wst(N,E,from,to,w,es,K); + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/write.mzn b/share/minizinc/std/write.mzn new file mode 100644 index 0000000..5ac83b1 --- /dev/null +++ b/share/minizinc/std/write.mzn @@ -0,0 +1,22 @@ +include "fzn_write.mzn"; +include "fzn_write_reif.mzn"; + +/** @group globals.array + Creates a new array \a O from an input array \a I with a change at position \a i to take value \a v + + \a I is an array of integers + \a O is an array of integers with same index set as \a I + \a i is an index for \a I + \a v is an integer value +*/ +predicate write(array[int] of var int: I, var int: i, var int: v, + array[int] of var int: O) = + assert(index_set(O) = index_set(I),"writet: index set of I must be same as O") /\ + fzn_write(I, i, v, O); + +function array[int] of var int: write(array[int] of var int: I, var int: i, var int: v) = + let { array[index_set(I)] of var int: O; + constraint fzn_write(I,i,v,O); } + in O; + +%-----------------------------------------------------------------------------% diff --git a/share/minizinc/std/writes.mzn b/share/minizinc/std/writes.mzn new file mode 100644 index 0000000..59cd7cd --- /dev/null +++ b/share/minizinc/std/writes.mzn @@ -0,0 +1,26 @@ +include "fzn_writes.mzn"; +include "fzn_writes_reif.mzn"; + +/** @group globals.array + Creates a new array \a O from an input array \a I with a simultaneous change at positions \a P to values \a V + + \a I is an array of integers + \a O is an array of integers with same index set as \a I + \a P is an array of index values in \a I + \a V is an array of integer values +*/ +predicate writes(array[int] of var int: I, array[int] of var int: P, + array[int] of var int: V, array[int] of var int: O) = + assert(index_set(O) = index_set(I),"writes: index set of I must be same as O") /\ + assert(index_set(P) = index_set(V),"writes: index set of P must be same as V") /\ + fzn_writes(I, P, V, O); + +function array[int] of var int: writes(array[int] of var int: I, + array[int] of var int: P, + array[int] of var int: V) = + assert(index_set(P) = index_set(V),"writes: index set of P must be same as V") /\ + let { array[index_set(I)] of var int: O; + constraint fzn_writes(I,P,V,O); } + in O; + +%-----------------------------------------------------------------------------% \ No newline at end of file diff --git a/share/minizinc/std/writes_seq.mzn b/share/minizinc/std/writes_seq.mzn new file mode 100644 index 0000000..7a635a3 --- /dev/null +++ b/share/minizinc/std/writes_seq.mzn @@ -0,0 +1,27 @@ +include "fzn_writes_seq.mzn"; +include "fzn_writes_seq_reif.mzn"; + +/** @group globals.array + Creates a new array \a O from an input array \a I with a sequence of changes at positions \a P to take values \a V + + \a I is an array of integers + \a O is an array of integers with same index set as \a I + \a P is an array of index values in \a I + \a V is an array of integer values +*/ +predicate writes_seq(array[int] of var int: I, array[int] of var int: P, + array[int] of var int: V, array[int] of var int: O) = + assert(index_set(O) = index_set(I),"writes: index set of I must be same as O") /\ + assert(index_set(P) = index_set(V),"writes: index set of P must be same as V") /\ + fzn_writes_seq(I, P, V, O); + +function array[int] of var int: writes_seq(array[int] of var int: I, + array[int] of var int: P, + array[int] of var int: V) = + let { + array[index_set(I)] of var int: O; + constraint assert(index_set(P) = index_set(V),"writes: index set of P must be same as V"); + constraint fzn_writes_seq(I,P,V,O); + } in O; + +%-----------------------------------------------------------------------------% diff --git a/solvers/MIP/MIP_cplex_solverfactory.cpp b/solvers/MIP/MIP_cplex_solverfactory.cpp new file mode 100644 index 0000000..e67fba2 --- /dev/null +++ b/solvers/MIP/MIP_cplex_solverfactory.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace MiniZinc { +namespace { +void getWrapper() { + static MIP_SolverFactory _cplex_solver_factory; + return; +} +} // namespace +Cplex_SolverFactoryInitialiser::Cplex_SolverFactoryInitialiser(void) { getWrapper(); } +} // namespace MiniZinc diff --git a/solvers/MIP/MIP_cplex_wrap.cpp b/solvers/MIP/MIP_cplex_wrap.cpp new file mode 100644 index 0000000..56c2e8c --- /dev/null +++ b/solvers/MIP/MIP_cplex_wrap.cpp @@ -0,0 +1,1014 @@ +// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPLEX_PLUGIN +#ifdef HAS_DLFCN_H +#include +#elif defined HAS_WINDOWS_H +#include +#endif +#endif + +using namespace std; + +#include + +#ifdef CPLEX_PLUGIN + +namespace { +void* dll_open(const std::string& file) { +#ifdef HAS_DLFCN_H + if (MiniZinc::FileUtils::is_absolute(file)) { + return dlopen(file.c_str(), RTLD_NOW); + } + if (void* so = dlopen(("lib" + file + ".so").c_str(), RTLD_NOW)) { + return so; + } + return dlopen(("lib" + file + ".jnilib").c_str(), RTLD_NOW); +#else + if (MiniZinc::FileUtils::is_absolute(file)) { + return LoadLibrary(file.c_str()); + } else { + return LoadLibrary((file + ".dll").c_str()); + } +#endif +} +void* dll_sym(void* dll, const char* sym) { +#ifdef HAS_DLFCN_H + void* ret = dlsym(dll, sym); +#else + void* ret = GetProcAddress((HMODULE)dll, sym); +#endif + if (ret == NULL) + throw MiniZinc::InternalError("cannot load symbol " + string(sym) + " from CPLEX dll"); + return ret; +} +void dll_close(void* dll) { +#ifdef HAS_DLFCN_H + dlclose(dll); +#else + FreeLibrary((HMODULE)dll); +#endif +} +} // namespace + +#endif + +const vector& CPLEXDLLs(void) { + static const vector sCPLEXDLLs = {"cplex1290", "cplex1280", "cplex1270"}; + return sCPLEXDLLs; +} + +void MIP_cplex_wrapper::checkDLL() { +#ifdef CPLEX_PLUGIN + _cplex_dll = NULL; + if (options->sCPLEXDLL.size()) { + _cplex_dll = dll_open(options->sCPLEXDLL.c_str()); + } else { + for (const auto& s : CPLEXDLLs()) { + _cplex_dll = dll_open(s.c_str()); + if (NULL != _cplex_dll) { + break; + } + } + } + + if (_cplex_dll == NULL) { + if (options->sCPLEXDLL.empty()) { + throw MiniZinc::InternalError("cannot load cplex dll, specify --cplex-dll"); + } else { + throw MiniZinc::InternalError("cannot load cplex dll `" + options->sCPLEXDLL + "'"); + } + } + + *(void**)(&dll_CPXaddfuncdest) = dll_sym(_cplex_dll, "CPXaddfuncdest"); + *(void**)(&dll_CPXaddindconstr) = dll_sym(_cplex_dll, "CPXaddindconstr"); + *(void**)(&dll_CPXaddlazyconstraints) = dll_sym(_cplex_dll, "CPXaddlazyconstraints"); + *(void**)(&dll_CPXaddmipstarts) = dll_sym(_cplex_dll, "CPXaddmipstarts"); + *(void**)(&dll_CPXaddrows) = dll_sym(_cplex_dll, "CPXaddrows"); + *(void**)(&dll_CPXaddusercuts) = dll_sym(_cplex_dll, "CPXaddusercuts"); + *(void**)(&dll_CPXchgbds) = dll_sym(_cplex_dll, "CPXchgbds"); + *(void**)(&dll_CPXchgmipstarts) = dll_sym(_cplex_dll, "CPXchgmipstarts"); + *(void**)(&dll_CPXchgobjsen) = dll_sym(_cplex_dll, "CPXchgobjsen"); + *(void**)(&dll_CPXcloseCPLEX) = dll_sym(_cplex_dll, "CPXcloseCPLEX"); + *(void**)(&dll_CPXcreateprob) = dll_sym(_cplex_dll, "CPXcreateprob"); + *(void**)(&dll_CPXcutcallbackadd) = dll_sym(_cplex_dll, "CPXcutcallbackadd"); + *(void**)(&dll_CPXfreeprob) = dll_sym(_cplex_dll, "CPXfreeprob"); + *(void**)(&dll_CPXgetbestobjval) = dll_sym(_cplex_dll, "CPXgetbestobjval"); + *(void**)(&dll_CPXgetcallbackincumbent) = dll_sym(_cplex_dll, "CPXgetcallbackincumbent"); + *(void**)(&dll_CPXgetcallbackinfo) = dll_sym(_cplex_dll, "CPXgetcallbackinfo"); + *(void**)(&dll_CPXgetcallbacknodeinfo) = dll_sym(_cplex_dll, "CPXgetcallbacknodeinfo"); + *(void**)(&dll_CPXgetcallbacknodex) = dll_sym(_cplex_dll, "CPXgetcallbacknodex"); + *(void**)(&dll_CPXgetchannels) = dll_sym(_cplex_dll, "CPXgetchannels"); + *(void**)(&dll_CPXgetdettime) = dll_sym(_cplex_dll, "CPXgetdettime"); + *(void**)(&dll_CPXgeterrorstring) = dll_sym(_cplex_dll, "CPXgeterrorstring"); + *(void**)(&dll_CPXgetmipstartindex) = dll_sym(_cplex_dll, "CPXgetmipstartindex"); + *(void**)(&dll_CPXgetnodecnt) = dll_sym(_cplex_dll, "CPXgetnodecnt"); + *(void**)(&dll_CPXgetnodeleftcnt) = dll_sym(_cplex_dll, "CPXgetnodeleftcnt"); + *(void**)(&dll_CPXgetnumcols) = dll_sym(_cplex_dll, "CPXgetnumcols"); + *(void**)(&dll_CPXgetnumrows) = dll_sym(_cplex_dll, "CPXgetnumrows"); + *(void**)(&dll_CPXgetobjsen) = dll_sym(_cplex_dll, "CPXgetobjsen"); + *(void**)(&dll_CPXgetobjval) = dll_sym(_cplex_dll, "CPXgetobjval"); + *(void**)(&dll_CPXgetsolnpoolnumsolns) = dll_sym(_cplex_dll, "CPXgetsolnpoolnumsolns"); + *(void**)(&dll_CPXgetstat) = dll_sym(_cplex_dll, "CPXgetstat"); + *(void**)(&dll_CPXgetstatstring) = dll_sym(_cplex_dll, "CPXgetstatstring"); + *(void**)(&dll_CPXgettime) = dll_sym(_cplex_dll, "CPXgettime"); + *(void**)(&dll_CPXgetx) = dll_sym(_cplex_dll, "CPXgetx"); + *(void**)(&dll_CPXmipopt) = dll_sym(_cplex_dll, "CPXmipopt"); + *(void**)(&dll_CPXnewcols) = dll_sym(_cplex_dll, "CPXnewcols"); + *(void**)(&dll_CPXopenCPLEX) = dll_sym(_cplex_dll, "CPXopenCPLEX"); + *(void**)(&dll_CPXreadcopyparam) = dll_sym(_cplex_dll, "CPXreadcopyparam"); + *(void**)(&dll_CPXsetdblparam) = dll_sym(_cplex_dll, "CPXsetdblparam"); + *(void**)(&dll_CPXsetinfocallbackfunc) = dll_sym(_cplex_dll, "CPXsetinfocallbackfunc"); + *(void**)(&dll_CPXsetintparam) = dll_sym(_cplex_dll, "CPXsetintparam"); + *(void**)(&dll_CPXsetlazyconstraintcallbackfunc) = + dll_sym(_cplex_dll, "CPXsetlazyconstraintcallbackfunc"); + *(void**)(&dll_CPXsetusercutcallbackfunc) = dll_sym(_cplex_dll, "CPXsetusercutcallbackfunc"); + *(void**)(&dll_CPXversion) = dll_sym(_cplex_dll, "CPXversion"); + *(void**)(&dll_CPXwriteparam) = dll_sym(_cplex_dll, "CPXwriteparam"); + *(void**)(&dll_CPXwriteprob) = dll_sym(_cplex_dll, "CPXwriteprob"); + +#else + + dll_CPXaddfuncdest = CPXaddfuncdest; + dll_CPXaddindconstr = CPXaddindconstr; + dll_CPXaddlazyconstraints = CPXaddlazyconstraints; + dll_CPXaddmipstarts = CPXaddmipstarts; + dll_CPXaddrows = CPXaddrows; + dll_CPXaddusercuts = CPXaddusercuts; + dll_CPXchgbds = CPXchgbds; + dll_CPXchgmipstarts = CPXchgmipstarts; + dll_CPXchgobjsen = CPXchgobjsen; + dll_CPXcloseCPLEX = CPXcloseCPLEX; + dll_CPXcreateprob = CPXcreateprob; + dll_CPXcutcallbackadd = CPXcutcallbackadd; + dll_CPXfreeprob = CPXfreeprob; + dll_CPXgetbestobjval = CPXgetbestobjval; + dll_CPXgetcallbackincumbent = CPXgetcallbackincumbent; + dll_CPXgetcallbackinfo = CPXgetcallbackinfo; + dll_CPXgetcallbacknodeinfo = CPXgetcallbacknodeinfo; + dll_CPXgetcallbacknodex = CPXgetcallbacknodex; + dll_CPXgetchannels = CPXgetchannels; + dll_CPXgetdettime = CPXgetdettime; + dll_CPXgeterrorstring = CPXgeterrorstring; + dll_CPXgetmipstartindex = CPXgetmipstartindex; + dll_CPXgetnodecnt = CPXgetnodecnt; + dll_CPXgetnodeleftcnt = CPXgetnodeleftcnt; + dll_CPXgetnumcols = CPXgetnumcols; + dll_CPXgetnumrows = CPXgetnumrows; + dll_CPXgetobjsen = CPXgetobjsen; + dll_CPXgetobjval = CPXgetobjval; + dll_CPXgetsolnpoolnumsolns = CPXgetsolnpoolnumsolns; + dll_CPXgetstat = CPXgetstat; + dll_CPXgetstatstring = CPXgetstatstring; + dll_CPXgettime = CPXgettime; + dll_CPXgetx = CPXgetx; + dll_CPXmipopt = CPXmipopt; + dll_CPXnewcols = CPXnewcols; + dll_CPXopenCPLEX = CPXopenCPLEX; + dll_CPXreadcopyparam = CPXreadcopyparam; + dll_CPXsetdblparam = CPXsetdblparam; + dll_CPXsetinfocallbackfunc = CPXsetinfocallbackfunc; + dll_CPXsetintparam = CPXsetintparam; + dll_CPXsetlazyconstraintcallbackfunc = CPXsetlazyconstraintcallbackfunc; + dll_CPXsetusercutcallbackfunc = CPXsetusercutcallbackfunc; + dll_CPXversion = CPXversion; + dll_CPXwriteparam = CPXwriteparam; + dll_CPXwriteprob = CPXwriteprob; + +#endif +} + +string MIP_cplex_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) { + string v = "MIP wrapper for IBM ILOG CPLEX "; + int status; + Options def_options; + Options* options = opt ? static_cast(opt) : &def_options; + try { + MIP_cplex_wrapper mcw(options); + CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); + if (env) { + v += mcw.dll_CPXversion(env); + status = mcw.dll_CPXcloseCPLEX(&env); + } else + v += "[?? ...cannot open CPLEX env to query version]"; + } catch (MiniZinc::InternalError&) { + v += "[?? ...cannot open CPLEX env to query version]"; + } + v += " Compiled " __DATE__ " " __TIME__; + return v; +} + +string MIP_cplex_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) { + string v; + int status; + Options def_options; + Options* options = opt ? static_cast(opt) : &def_options; + try { + MIP_cplex_wrapper mcw(options); + CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); + if (env) { + v += mcw.dll_CPXversion(env); + status = mcw.dll_CPXcloseCPLEX(&env); + } else { + v += ""; + } + } catch (MiniZinc::InternalError&) { + v += ""; + } + return v; +} + +std::string MIP_cplex_wrapper::needDllFlag(void) { + int status; + Options options; + try { + MIP_cplex_wrapper mcw(&options); + CPXENVptr env = mcw.dll_CPXopenCPLEX(&status); + if (env) { + return ""; + } + } catch (MiniZinc::InternalError&) { + } + return "--cplex-dll"; +} + +string MIP_cplex_wrapper::getId() { return "cplex"; } + +string MIP_cplex_wrapper::getName() { return "CPLEX"; } + +vector MIP_cplex_wrapper::getTags() { return {"mip", "float", "api"}; } + +vector MIP_cplex_wrapper::getStdFlags() { return {"-a", "-n", "-p", "-s"}; } + +void MIP_cplex_wrapper::Options::printHelp(ostream& os) { + os << "IBM ILOG CPLEX MIP wrapper options:" + << std::endl + // -s print statistics + // << " --readParam read CPLEX parameters from file + // << "--writeParam write CPLEX parameters to file + // << "--tuneParam instruct CPLEX to tune parameters instead of solving + << " --mipfocus \n 1: feasibility, 2: optimality, 3: move bound (default is 0, " + "balanced)" + << std::endl + << " -a\n print intermediate solutions (use for optimization problems only TODO)" + << std::endl + << " -p \n use N threads, default: 1" + << std::endl + // << " --nomippresolve disable MIP presolving NOT IMPL" << std::endl + << " --solver-time-limit \n stop search after N milliseconds wall time" << std::endl + << " -n , --num-solutions \n" + " stop search after N solutions" + << std::endl + << " --workmem , --nodefilestart \n" + " maximal RAM for working memory used before writing to node file, GB, default: 3" + << std::endl + << " --writeModel \n write model to (.lp, .mps, .sav, ...)" << std::endl + << " --readParam \n read CPLEX parameters from file" << std::endl + << " --writeParam \n write CPLEX parameters to file" + << std::endl + // << " --tuneParam instruct CPLEX to tune parameters instead of solving NOT IMPL" + + << " --absGap \n absolute gap |primal-dual| to stop" << std::endl + << " --relGap \n relative gap |primal-dual|/ to stop. Default 1e-8, set <0 " + "to use backend's default" + << std::endl + << " --intTol \n integrality tolerance for a variable. Default 1e-8" << std::endl + << "\n --cplex-dll or \n CPLEX DLL, or base name, such as cplex1280, " + "when using plugin. Default range tried: " + << CPLEXDLLs().front() << " .. " << CPLEXDLLs().back() + << std::endl + // << " --objDiff objective function discretization. Default 1.0" << std::endl + + << std::endl; +} + +bool MIP_cplex_wrapper::Options::processOption(int& i, std::vector& argv) { + MiniZinc::CLOParser cop(i, argv); + if (string(argv[i]) == "-a" || string(argv[i]) == "--all" || + string(argv[i]) == "--all-solutions") { + flag_all_solutions = true; + } else if (string(argv[i]) == "-f") { + // std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl; + } else if (cop.get("--mipfocus --mipFocus --MIPFocus --MIPfocus", &nMIPFocus)) { + } else if (cop.get("--writeModel", &sExportModel)) { + } else if (cop.get("-p", &nThreads)) { + } else if (cop.get("--solver-time-limit", &nTimeout)) { + } else if (cop.get("-n --num-solutions", &nSolLimit)) { + } else if (cop.get("--workmem --nodefilestart", &nWorkMemLimit)) { + } else if (cop.get("--readParam", &sReadParams)) { + } else if (cop.get("--writeParam", &sWriteParams)) { + } else if (cop.get("--absGap", &absGap)) { + } else if (cop.get("--relGap", &relGap)) { + } else if (cop.get("--intTol", &intTol)) { + } else if (cop.get("--cplex-dll", &sCPLEXDLL)) { + // } else if ( cop.get( "--objDiff", &objDiff ) ) { + } else + return false; + return true; +} + +void MIP_cplex_wrapper::wrap_assert(bool cond, string msg, bool fTerm) { + if (!cond) { + strcpy(cplex_buffer, "[NO ERROR STRING GIVEN]"); + dll_CPXgeterrorstring(env, status, cplex_buffer); + string msgAll = (" MIP_cplex_wrapper runtime error: " + msg + " " + cplex_buffer); + cerr << msgAll << endl; + if (fTerm) { + cerr << "TERMINATING." << endl; + throw runtime_error(msgAll); + } + } +} + +void MIP_cplex_wrapper::openCPLEX() { + checkDLL(); + cbui.wrapper = this; + /// Cleanup first. + // cleanup(); + /* Initialize the CPLEX environment */ + env = dll_CPXopenCPLEX(&status); + /* If an error occurs, the status value indicates the reason for + failure. A call to CPXgeterrorstring will produce the text of + the error message. Note that CPXopenCPLEX produces no output, + so the only way to see the cause of the error is to use + CPXgeterrorstring. For other CPLEX routines, the errors will + be seen if the CPXPARAM_ScreenOutput indicator is set to CPX_ON. */ + wrap_assert(env, "Could not open CPLEX environment."); + /* Create the problem. */ + lp = dll_CPXcreateprob(env, &status, "MIP_cplex_wrapper"); + /* A returned pointer of NULL may mean that not enough memory + was available or there was some other problem. In the case of + failure, an error message will have been written to the error + channel from inside CPLEX. In this example, the setting of + the parameter CPXPARAM_ScreenOutput causes the error message to + appear on stdout. */ + wrap_assert(lp, "Failed to create LP."); +} + +void MIP_cplex_wrapper::closeCPLEX() { + /// Freeing the problem can be slow both in C and C++, see IBM forums. Skipping. + /* Free up the problem as allocated by CPXcreateprob, if necessary */ + // if ( lp != NULL ) { + // status = CPXfreeprob (env, &lp); + // cplex_wrap_assert ( !status, "CPXfreeprob failed." ); + // } + lp = 0; + /* Free up the CPLEX environment, if necessary */ + if (env != NULL) { + status = dll_CPXcloseCPLEX(&env); + wrap_assert(!status, "Could not close CPLEX environment."); + } + /// and at last: +// MIP_wrapper::cleanup(); +#ifdef CPLEX_PLUGIN +// dll_close(cplex_dll); +#endif +} + +void MIP_cplex_wrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, + MIP_wrapper::VarType* vt, string* names) { + /// Convert var types: + vector ctype(n); + vector pcNames(n); + for (size_t i = 0; i < n; ++i) { + pcNames[i] = (char*)names[i].c_str(); + switch (vt[i]) { + case REAL: + ctype[i] = CPX_CONTINUOUS; + break; + case INT: + ctype[i] = CPX_INTEGER; + break; + case BINARY: + ctype[i] = CPX_BINARY; + break; + default: + throw runtime_error(" MIP_wrapper: unknown variable type"); + } + } + status = dll_CPXnewcols(env, lp, n, obj, lb, ub, &ctype[0], &pcNames[0]); + wrap_assert(!status, "Failed to declare variables."); +} + +static char getCPLEXConstrSense(MIP_wrapper::LinConType sense) { + switch (sense) { + case MIP_wrapper::LQ: + return 'L'; + case MIP_wrapper::EQ: + return 'E'; + case MIP_wrapper::GQ: + return 'G'; + default: + throw runtime_error(" MIP_cplex_wrapper: unknown constraint type"); + } +} + +void MIP_cplex_wrapper::addRow(int nnz, int* rmatind, double* rmatval, + MIP_wrapper::LinConType sense, double rhs, int mask, + string rowName) { + /// Convert var types: + char ssense = getCPLEXConstrSense(sense); + const int ccnt = 0; + const int rcnt = 1; + const int rmatbeg[] = {0}; + char* pRName = (char*)rowName.c_str(); + if (MaskConsType_Normal & mask) { + status = dll_CPXaddrows(env, lp, ccnt, rcnt, nnz, &rhs, &ssense, rmatbeg, rmatind, rmatval, + NULL, &pRName); + wrap_assert(!status, "Failed to add constraint."); + } + if (MaskConsType_Usercut & mask) { + status = + dll_CPXaddusercuts(env, lp, rcnt, nnz, &rhs, &ssense, rmatbeg, rmatind, rmatval, &pRName); + wrap_assert(!status, "Failed to add usercut."); + } + if (MaskConsType_Lazy & mask) { + status = dll_CPXaddlazyconstraints(env, lp, rcnt, nnz, &rhs, &ssense, rmatbeg, rmatind, rmatval, + &pRName); + wrap_assert(!status, "Failed to add lazy constraint."); + } +} + +void MIP_cplex_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, + double* rmatval, MIP_wrapper::LinConType sense, + double rhs, string rowName) { + wrap_assert(0 <= bVal && 1 >= bVal, "mzn-cplex: addIndicatorConstraint: bVal not 0/1"); + char ssense = getCPLEXConstrSense(sense); + status = dll_CPXaddindconstr(env, lp, iBVar, 1 - bVal, nnz, rhs, ssense, rmatind, rmatval, + rowName.c_str()); + wrap_assert(!status, "Failed to add indicator constraint."); +} + +bool MIP_cplex_wrapper::addWarmStart(const std::vector& vars, + const std::vector vals) { + assert(vars.size() == vals.size()); + const char* sMSName = "MZNMS"; + int msindex = -1; + const int beg = 0; + /// Check if we already added a start + status = dll_CPXgetmipstartindex(env, lp, sMSName, &msindex); + if (status) { // not existent + // status = dll_CPXaddmipstarts (env, lp, mcnt, nzcnt, beg, varindices, + // values, effortlevel, mipstartname); + status = dll_CPXaddmipstarts(env, lp, 1, vars.size(), &beg, vars.data(), vals.data(), nullptr, + (char**)&sMSName); + wrap_assert(!status, "Failed to add warm start."); + } else { + // status = dll_CPXchgmipstarts (env, lp, mcnt, mipstartindices, nzcnt, beg, varindices, values, + // effortlevel); + status = dll_CPXchgmipstarts(env, lp, 1, &msindex, vars.size(), &beg, vars.data(), vals.data(), + nullptr); + wrap_assert(!status, "Failed to extend warm start."); + } + return true; +} + +void MIP_cplex_wrapper::setVarBounds(int iVar, double lb, double ub) { + wrap_assert(lb <= ub, "mzn-cplex: setVarBounds: lb>ub"); + char cl = 'L', cu = 'U'; + status = dll_CPXchgbds(env, lp, 1, &iVar, &cl, &lb); + wrap_assert(!status, "Failed to set lower bound."); + status = dll_CPXchgbds(env, lp, 1, &iVar, &cu, &ub); + wrap_assert(!status, "Failed to set upper bound."); +} + +void MIP_cplex_wrapper::setVarLB(int iVar, double lb) { + char cl = 'L'; + status = dll_CPXchgbds(env, lp, 1, &iVar, &cl, &lb); + wrap_assert(!status, "Failed to set lower bound."); +} + +void MIP_cplex_wrapper::setVarUB(int iVar, double ub) { + char cu = 'U'; + status = dll_CPXchgbds(env, lp, 1, &iVar, &cu, &ub); + wrap_assert(!status, "Failed to set upper bound."); +} + +/// SolutionCallback ------------------------------------------------------------------------ +/// CPLEX ensures thread-safety +static int CPXPUBLIC solcallback(CPXCENVptr env, void* cbdata, int wherefrom, void* cbhandle) { + int status = 0; + + MIP_wrapper::CBUserInfo* info = (MIP_wrapper::CBUserInfo*)cbhandle; + MIP_cplex_wrapper* cw = static_cast(info->wrapper); + int hasincumbent = 0; + int newincumbent = 0; + double objVal; + + status = cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODE_COUNT, + &info->pOutput->nNodes); + if (status) goto TERMINATE; + + status = cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_NODES_LEFT, + &info->pOutput->nOpenNodes); + if (status) goto TERMINATE; + + status = + cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_MIP_FEAS, &hasincumbent); + if (status) goto TERMINATE; + + if (hasincumbent) { + status = + cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_BEST_INTEGER, &objVal); + if (status) goto TERMINATE; + + if (fabs(info->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) { + newincumbent = 1; + info->pOutput->objVal = objVal; + info->pOutput->status = MIP_wrapper::SAT; + info->pOutput->statusName = "feasible from a callback"; + } + } + + // if ( nodecnt >= info->lastlog + 100 || newincumbent ) { + // double walltime; + // double dettime; + + status = cw->dll_CPXgetcallbackinfo(env, cbdata, wherefrom, CPX_CALLBACK_INFO_BEST_REMAINING, + &info->pOutput->bestBound); + // if ( status ) goto TERMINATE; + + // status = dll_CPXgettime (env, &walltime); + // if ( status ) goto TERMINATE; + // + // status = dll_CPXgetdettime (env, &dettime); + // if ( status ) goto TERMINATE; + // + // } + + if (newincumbent) { + assert(info->pOutput->x); + status = cw->dll_CPXgetcallbackincumbent(env, cbdata, wherefrom, (double*)info->pOutput->x, 0, + info->pOutput->nCols - 1); + if (status) goto TERMINATE; + + info->pOutput->dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - info->pOutput->dWallTime0) + .count(); + info->pOutput->dCPUTime = double(std::clock() - info->pOutput->cCPUTime0) / CLOCKS_PER_SEC; + + /// Call the user function: + if (info->solcbfn) (*info->solcbfn)(*info->pOutput, info->psi); + info->printed = true; + } + +TERMINATE: + return (status); + +} /* END logcallback */ +// end SolutionCallback --------------------------------------------------------------------- + +/// Cut callbacks, mostly copied from admipex5.c, CPLEX 12.6.3 +/* The following macro defines the smallest improvement + on the value of the objective function that is required + when adding user cuts from within a callback. + If the improvement on the value of the ojective function + is not large enough, the callback will abort the cut loop. */ + +#define EPSOBJ 0.1 + +/* The following structure will hold the information we need to + pass to the cut callback function */ + +struct cutinfo { + CPXLPptr lp; + int numcols; + int num; + double* x; + int* beg; + int* ind; + double* val; + double* rhs; + int nodeid; + double nodeobjval; + int objsen; + MIP_wrapper::CBUserInfo* info = 0; +}; +typedef struct cutinfo CUTINFO, *CUTINFOptr; + +/* Init information on the node objval for the user cut callback */ + +static void initnodeobjvalinfo(MIP_cplex_wrapper* cw, CPXENVptr env, CPXLPptr lp, + CUTINFOptr cutinfo) { + cutinfo->nodeid = -1; + cutinfo->nodeobjval = 0.0; + cutinfo->objsen = cw->dll_CPXgetobjsen(env, lp); + if (cutinfo->objsen == CPX_MIN) + cutinfo->objsen = 1; + else + cutinfo->objsen = -1; +} /* END initnodeobjvalinfo */ + +static int CPXPUBLIC myusercutcallback(CPXCENVptr env, void* cbdata, int wherefrom, void* cbhandle, + int* useraction_p) { + int status = 0; + + CUTINFOptr cutinfo = (CUTINFOptr)cbhandle; + + // int numcols = cutinfo->numcols; + // int numcuts = cutinfo->num; + // double *x = cutinfo->x; + // int *beg = cutinfo->beg; + // int *ind = cutinfo->ind; + // double *val = cutinfo->val; + // double *rhs = cutinfo->rhs; + // int *cutind = NULL; + // double *cutval = NULL; + // double cutvio; + int addedcuts = 0; + // int i, j, k; //, cutnz; + MIP_wrapper::CBUserInfo* info = cutinfo->info; + MIP_cplex_wrapper* cw = static_cast(info->wrapper); + // double *x = info->pCutOutput->x; + + *useraction_p = CPX_CALLBACK_DEFAULT; + + /* If we are called as a user cut callback, decide + first if we want to add cuts or abort the cut loop. + When adding user cuts with purgeable flag set to + CPX_USECUT_PURGE or CPX_USECUT_FILTER, it is important + to avoid the possibility of an infinite cut loop, where + the same cuts are added to the LP and then immediately + purged at every cut pass. Such a situation can be avoided, + for instance, by applying a tailing off criterion and aborting + the cut loop where no progress in the objval is observed. + Note, however, that the same approach must not be applied + with lazy constraints. In this case, if lazy constraints are + added with purgeable flag set to CPX_USECUT_PURGE, adding + the same lazy constraint more than once could be required + to ensure the correctness of the final result. */ + + bool fMIPSol = true; + if (wherefrom == CPX_CALLBACK_MIP_CUT_LOOP || wherefrom == CPX_CALLBACK_MIP_CUT_LAST) { + int oldnodeid = cutinfo->nodeid; + double oldnodeobjval = cutinfo->nodeobjval; + + fMIPSol = false; + + /* Retrieve nodeid and node objval of the current node */ + + status = cw->dll_CPXgetcallbacknodeinfo(env, cbdata, wherefrom, 0, + CPX_CALLBACK_INFO_NODE_SEQNUM, &cutinfo->nodeid); + if (status) { + fprintf(stderr, "Failed to get node id.\n"); + goto TERMINATE; + } + + status = cw->dll_CPXgetcallbacknodeinfo(env, cbdata, wherefrom, 0, + CPX_CALLBACK_INFO_NODE_OBJVAL, &cutinfo->nodeobjval); + if (status) { + fprintf(stderr, "Failed to get node objval.\n"); + goto TERMINATE; + } + + /* Abort the cut loop if we are stuck at the same node + as before and there is no progress in the node objval */ + + if (oldnodeid == cutinfo->nodeid) { + double objchg = (cutinfo->nodeobjval - oldnodeobjval); + /* Multiply objchg by objsen to normalize + the change in the objective function to + the case of a minimization problem */ + objchg *= cutinfo->objsen; + if (objchg <= EPSOBJ) { + *useraction_p = CPX_CALLBACK_ABORT_CUT_LOOP; + goto TERMINATE; + } + } + } + + /* If we reached this point, we are + .. in a lazyconstraint callback, or + .. in a user cut callback, and cuts seem to help + improving the node objval. + In both cases, we retrieve the x solution and + look for violated cuts. */ + + if (info->cutcbfn) { // if cut handler given + MIP_wrapper::Output outpRlx; + outpRlx.x = info->pOutput->x; // using the sol output storage TODO? + outpRlx.nCols = info->pOutput->nCols; + assert(outpRlx.x && outpRlx.nCols); + status = cw->dll_CPXgetcallbacknodex(env, cbdata, wherefrom, (double*)outpRlx.x, 0, + outpRlx.nCols - 1); + if (status) { + fprintf(stderr, "Cut callback: failed to get node solution.\n"); + goto TERMINATE; + } + MIP_wrapper::CutInput cutInput; + info->cutcbfn(outpRlx, cutInput, info->psi, fMIPSol); + static int nCuts = 0; + nCuts += cutInput.size(); + // if ( cutInput.size() ) + // cerr << "\n N CUTS: " << nCuts << endl; + for (auto& cd : cutInput) { + if (!(cd.mask & (MIP_wrapper::MaskConsType_Usercut | MIP_wrapper::MaskConsType_Lazy))) + throw runtime_error("Cut callback: should be user/lazy"); + /* Use a cut violation tolerance of 0.01 */ + if (true) { // cutvio > 0.01 ) { + status = cw->dll_CPXcutcallbackadd(env, cbdata, wherefrom, cd.rmatind.size(), cd.rhs, + getCPLEXConstrSense(cd.sense), cd.rmatind.data(), + cd.rmatval.data(), + CPX_USECUT_FORCE); // PURGE? + if (status) { + fprintf(stderr, "CPLEX callback: failed to add cut.\n"); + goto TERMINATE; + } + addedcuts++; + } + } + } + + /* Tell CPLEX that cuts have been created */ + if (addedcuts > 0) { + *useraction_p = CPX_CALLBACK_SET; + } + +TERMINATE: + + return (status); + +} /* END myusercutcallback */ + +// ----------------- END Cut callbacks ------------------ + +MIP_cplex_wrapper::Status MIP_cplex_wrapper::convertStatus(int cplexStatus) { + Status s = Status::UNKNOWN; + /* Converting the status. */ + switch (cplexStatus) { + case CPXMIP_OPTIMAL: + s = Status::OPT; + wrap_assert(dll_CPXgetsolnpoolnumsolns(env, lp), "Optimality reported but pool empty?", + false); + break; + case CPXMIP_INFEASIBLE: + s = Status::UNSAT; + break; + // case CPXMIP_OPTIMAL_INFEAS: + case CPXMIP_INForUNBD: + s = Status::UNSATorUNBND; + break; + case CPXMIP_SOL_LIM: + case CPXMIP_NODE_LIM_FEAS: + case CPXMIP_TIME_LIM_FEAS: + case CPXMIP_FAIL_FEAS: + case CPXMIP_MEM_LIM_FEAS: + case CPXMIP_ABORT_FEAS: + case CPXMIP_FAIL_FEAS_NO_TREE: + s = Status::SAT; + wrap_assert(dll_CPXgetsolnpoolnumsolns(env, lp), "Feasibility reported but pool empty?", + false); + break; + case CPXMIP_UNBOUNDED: + s = Status::UNBND; + break; + // case CPXMIP_ABORT_INFEAS: + case CPXMIP_FAIL_INFEAS: + s = Status::__ERROR; + break; + default: + // case CPXMIP_OPTIMAL_TOL: + // case CPXMIP_ABORT_RELAXATION_UNBOUNDED: + if (dll_CPXgetsolnpoolnumsolns(env, lp)) + s = Status::SAT; + else + s = Status::UNKNOWN; + } + return s; +} + +void msgfunction(void* handle, const char* msg_string) { cerr << msg_string << flush; } + +void MIP_cplex_wrapper::solve() { // Move into ancestor? + + /////////////// Last-minute solver options ////////////////// + if (options->flag_all_solutions && 0 == nProbType) + cerr << "WARNING. --all-solutions for SAT problems not implemented." << endl; + // Before all manual params ??? + if (options->sReadParams.size()) { + status = dll_CPXreadcopyparam(env, options->sReadParams.c_str()); + wrap_assert(!status, "Failed to read CPLEX parameters.", false); + } + + /* Turn on output to the screen */ + if (fVerbose) { + CPXCHANNELptr chnl[4]; + dll_CPXgetchannels(env, &chnl[0], &chnl[1], &chnl[2], &chnl[3]); + for (int i = 0; i < 3; ++i) { + status = dll_CPXaddfuncdest(env, chnl[i], nullptr, msgfunction); + } + // status = dll_CPXsetintparam(env, CPXPARAM_ScreenOutput, + // fVerbose ? CPX_ON : CPX_OFF); // also when flag_all_solutions? TODO + // wrap_assert(!status, " CPLEX Warning: Failure to switch screen indicator.", false); + } + status = dll_CPXsetintparam(env, CPXPARAM_MIP_Display, + fVerbose ? 2 : 0); // also when flag_all_solutions? TODO + wrap_assert(!status, " CPLEX Warning: Failure to switch logging.", false); + /// Make it wall time by default, 12.8 + // status = dll_CPXsetintparam (env, CPXPARAM_ClockType, 1); // CPU time + // wrap_assert(!status, " CPLEX Warning: Failure to measure CPU time.", false); + status = dll_CPXsetintparam(env, CPX_PARAM_MIPCBREDLP, CPX_OFF); // Access original model + wrap_assert(!status, " CPLEX Warning: Failure to set access original model in callbacks.", + false); + if (options->sExportModel.size()) { + status = dll_CPXwriteprob(env, lp, options->sExportModel.c_str(), NULL); + wrap_assert(!status, "Failed to write LP to disk.", false); + } + + /// TODO + // if(all_solutions && obj.getImpl()) { + // IloNum lastObjVal = (obj.getSense() == IloObjective::Minimize ) ? + // _ilocplex->use(SolutionCallback(_iloenv, lastObjVal, *this)); + // Turn off CPLEX logging + + if (options->nThreads > 0) { + status = dll_CPXsetintparam(env, CPXPARAM_Threads, options->nThreads); + wrap_assert(!status, "Failed to set CPXPARAM_Threads.", false); + } + + if (options->nTimeout > 0) { + status = dll_CPXsetdblparam(env, CPXPARAM_TimeLimit, + static_cast(options->nTimeout) / 1000.0); + wrap_assert(!status, "Failed to set CPXPARAM_TimeLimit.", false); + } + if (options->nSolLimit > 0) { + status = dll_CPXsetintparam(env, CPXPARAM_MIP_Limits_Solutions, options->nSolLimit); + wrap_assert(!status, "Failed to set CPXPARAM_MIP_Limits_Solutions.", false); + } + if (options->nMIPFocus > 0) { + status = dll_CPXsetintparam(env, CPXPARAM_Emphasis_MIP, options->nMIPFocus); + wrap_assert(!status, "Failed to set CPXPARAM_Emphasis_MIP.", false); + } + + if (options->nWorkMemLimit > 0) { + status = dll_CPXsetintparam(env, CPXPARAM_MIP_Strategy_File, 3); + wrap_assert(!status, "Failed to set CPXPARAM_MIP_Strategy_File.", false); + status = + dll_CPXsetdblparam(env, CPXPARAM_WorkMem, 1024.0 * options->nWorkMemLimit); // MB in CPLEX + wrap_assert(!status, "Failed to set CPXPARAM_WorkMem.", false); + } + + if (options->absGap >= 0.0) { + status = dll_CPXsetdblparam(env, CPXPARAM_MIP_Tolerances_AbsMIPGap, options->absGap); + wrap_assert(!status, "Failed to set CPXPARAM_MIP_Tolerances_AbsMIPGap.", false); + } + if (options->relGap >= 0.0) { + status = dll_CPXsetdblparam(env, CPXPARAM_MIP_Tolerances_MIPGap, options->relGap); + wrap_assert(!status, "Failed to set CPXPARAM_MIP_Tolerances_MIPGap.", false); + } + if (options->intTol >= 0.0) { + status = dll_CPXsetdblparam(env, CPXPARAM_MIP_Tolerances_Integrality, options->intTol); + wrap_assert(!status, "Failed to set CPXPARAM_MIP_Tolerances_Integrality.", false); + } + + // status = dll_CPXsetdblparam (env, CPXPARAM_MIP_Tolerances_ObjDifference, objDiff); + // wrap_assert(!status, "Failed to set CPXPARAM_MIP_Tolerances_ObjDifference.", false); + + /// Solution callback + output.nCols = colObj.size(); + x.resize(output.nCols); + output.x = &x[0]; + if (options->flag_all_solutions && cbui.solcbfn) { + status = dll_CPXsetinfocallbackfunc(env, solcallback, &cbui); + wrap_assert(!status, "Failed to set solution callback", false); + } + if (cbui.cutcbfn) { + assert(cbui.cutMask & (MaskConsType_Usercut | MaskConsType_Lazy)); + if (cbui.cutMask & MaskConsType_Usercut) { + // For user cuts, needs to keep some info after presolve + if (fVerbose) + cerr << " MIP_cplex_wrapper: user cut callback enabled, setting params" << endl; + CUTINFO usercutinfo; // THREADS? TODO + usercutinfo.info = &cbui; + /* Init information on the node objval for the user cut callback */ + initnodeobjvalinfo(this, env, lp, &usercutinfo); + /* Assure linear mappings between the presolved and original + models */ + status = dll_CPXsetintparam(env, CPXPARAM_Preprocessing_Linear, 0); + wrap_assert(!status, "CPLEX: setting prepro_linear"); + /* Turn on traditional search for use with control callbacks */ + status = dll_CPXsetintparam(env, CPXPARAM_MIP_Strategy_Search, CPX_MIPSEARCH_TRADITIONAL); + wrap_assert(!status, "CPLEX: setting traditional search"); + /* Let MIP callbacks work on the original model */ + status = dll_CPXsetintparam(env, CPXPARAM_MIP_Strategy_CallbackReducedLP, CPX_OFF); + wrap_assert(!status, "CPLEX: setting callbacks to work on orig model"); + /// And + /* Set up to use MIP usercut callback */ + + status = dll_CPXsetusercutcallbackfunc(env, myusercutcallback, &usercutinfo); + wrap_assert(!status, "CPLEX: setting user cut callback"); + } + if (cbui.cutMask & MaskConsType_Lazy) { + if (fVerbose) + cerr << " MIP_cplex_wrapper: lazy cut callback enabled, setting params" << endl; + CUTINFO lazyconinfo; + lazyconinfo.info = &cbui; + /* Init information on the node objval for the user cut callback. + No need to initialize the information on the node objval, + for the lazy constraint callback, because those information are + used only in the user cut callback. */ + initnodeobjvalinfo(this, env, lp, &lazyconinfo); + /* Assure linear mappings between the presolved and original + models */ + status = dll_CPXsetintparam(env, CPXPARAM_Preprocessing_Linear, 0); + wrap_assert(!status, "CPLEX: setting prepro_linear"); + /* Turn on traditional search for use with control callbacks */ + // status = dll_CPXsetintparam (env, CPXPARAM_MIP_Strategy_Search, + // CPX_MIPSEARCH_TRADITIONAL); + // wrap_assert ( !status, "CPLEX: setting traditional search" ); + /* Let MIP callbacks work on the original model */ + status = dll_CPXsetintparam(env, CPXPARAM_MIP_Strategy_CallbackReducedLP, CPX_OFF); + wrap_assert(!status, "CPLEX: setting callbacks to work on orig model"); + /* Set up to use MIP lazyconstraint callback. The callback funtion + * registered is the same, but the data will be different. */ + + status = dll_CPXsetlazyconstraintcallbackfunc(env, myusercutcallback, &lazyconinfo); + wrap_assert(!status, "CPLEX: setting lazy cut callback"); + } + } + + /// after all modifs + if (options->sWriteParams.size()) { + status = dll_CPXwriteparam(env, options->sWriteParams.c_str()); + wrap_assert(!status, "Failed to write CPLEX parameters.", false); + } + + // status = dll_CPXgettime (env, &output.dCPUTime); + // wrap_assert(!status, "Failed to get time stamp.", false); + cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); + cbui.pOutput->cCPUTime0 = std::clock(); + + /* Optimize the problem and obtain solution. */ + status = dll_CPXmipopt(env, lp); + wrap_assert(!status, "Failed to optimize MIP."); + + output.dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - output.dWallTime0).count(); + double tmNow = std::clock(); + // status = dll_CPXgettime (env, &tmNow); Buggy in 12.7.1.0 + wrap_assert(!status, "Failed to get time stamp.", false); + output.dCPUTime = (tmNow - cbui.pOutput->cCPUTime0) / CLOCKS_PER_SEC; + + int solstat = dll_CPXgetstat(env, lp); + output.status = convertStatus(solstat); + output.statusName = dll_CPXgetstatstring(env, solstat, cplex_status_buffer); + + /// Continuing to fill the output object: + if (Status::OPT == output.status || Status::SAT == output.status) { + status = dll_CPXgetobjval(env, lp, &output.objVal); + wrap_assert(!status, "No MIP objective value available."); + + /* The size of the problem should be obtained by asking CPLEX what + the actual size is, rather than using what was passed to CPXcopylp. + cur_numrows and cur_numcols store the current number of rows and + columns, respectively. */ // ?????????????? TODO + + // int cur_numrows = dll_CPXgetnumrows (env, lp); + int cur_numcols = dll_CPXgetnumcols(env, lp); + assert(cur_numcols == colObj.size()); + + x.resize(cur_numcols); + output.x = &x[0]; + status = dll_CPXgetx(env, lp, &x[0], 0, cur_numcols - 1); + wrap_assert(!status, "Failed to get variable values."); + if (cbui.solcbfn /*&& (!options->flag_all_solutions || !cbui.printed)*/) { + cbui.solcbfn(output, cbui.psi); + } + } + output.bestBound = 1e308; + status = dll_CPXgetbestobjval(env, lp, &output.bestBound); + wrap_assert(!status, "Failed to get the best bound.", false); + output.nNodes = dll_CPXgetnodecnt(env, lp); + output.nOpenNodes = dll_CPXgetnodeleftcnt(env, lp); +} + +void MIP_cplex_wrapper::setObjSense(int s) { + status = dll_CPXchgobjsen(env, lp, -s); // +1 for min in CPLEX + wrap_assert(!status, "Failed to set obj sense."); +} diff --git a/solvers/MIP/MIP_gurobi_solverfactory.cpp b/solvers/MIP/MIP_gurobi_solverfactory.cpp new file mode 100644 index 0000000..3ced3ee --- /dev/null +++ b/solvers/MIP/MIP_gurobi_solverfactory.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace MiniZinc { +namespace { +void getWrapper() { + static MIP_SolverFactory _gurobi_solver_factory; + return; +} +} // namespace +Gurobi_SolverFactoryInitialiser::Gurobi_SolverFactoryInitialiser(void) { getWrapper(); } +} // namespace MiniZinc diff --git a/solvers/MIP/MIP_gurobi_wrap.cpp b/solvers/MIP/MIP_gurobi_wrap.cpp new file mode 100644 index 0000000..a31724f --- /dev/null +++ b/solvers/MIP/MIP_gurobi_wrap.cpp @@ -0,0 +1,815 @@ +// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GUROBI_PLUGIN +#ifdef HAS_DLFCN_H +#include +#elif defined HAS_WINDOWS_H +#include +#endif +#endif + +using namespace std; + +#include +#include + +string MIP_gurobi_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) { + ostringstream oss; + oss << "MIP wrapper for Gurobi library " << getVersion(); + oss << ". Compiled " __DATE__ " " __TIME__; + return oss.str(); +} + +string MIP_gurobi_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) { + ostringstream oss; + MIP_gurobi_wrapper mgw(nullptr); // to avoid opening the env + try { + mgw.checkDLL(); + int major, minor, technical; + mgw.dll_GRBversion(&major, &minor, &technical); + oss << major << '.' << minor << '.' << technical; + return oss.str(); + } catch (MiniZinc::InternalError&) { + return ""; + } +} + +string MIP_gurobi_wrapper::needDllFlag() { + MIP_gurobi_wrapper mgw(NULL); + try { + mgw.checkDLL(); + return ""; + } catch (MiniZinc::InternalError&) { + return "--gurobi-dll"; + } +} + +string MIP_gurobi_wrapper::getId() { return "gurobi"; } + +string MIP_gurobi_wrapper::getName() { return "Gurobi"; } + +vector MIP_gurobi_wrapper::getTags() { return {"mip", "float", "api"}; } + +vector MIP_gurobi_wrapper::getStdFlags() { return {"-a", "-n", "-p", "-s"}; } + +const vector& gurobiDLLs(void) { + static const vector sGurobiDLLs = {"gurobi90", "gurobi85", "gurobi81", "gurobi80", + "gurobi75", "gurobi70", "gurobi65"}; + return sGurobiDLLs; +} + +void MIP_gurobi_wrapper::Options::printHelp(ostream& os) { + os << "GUROBI MIP wrapper options:" + << std::endl + // -s print statistics + // << " --readParam read GUROBI parameters from file + // << "--writeParam write GUROBI parameters to file + // << "--tuneParam instruct GUROBI to tune parameters instead of solving + << " -f\n free search (default)" << std::endl + << " --fixed-search\n fixed search (approximation of the model's one by branching " + "priorities)" + << std::endl + << " --uniform-search\n 'more fixed' search (all variables in the search anns get " + "priority 1)" + << std::endl + << " --mipfocus \n 1: feasibility, 2: optimality, 3: move bound (default is 0, " + "balanced)" + << std::endl + << " -a\n print intermediate solutions (use for optimization problems only TODO)" + << std::endl + << " -p \n use N threads, default: 1." + << std::endl + // << " --nomippresolve disable MIP presolving NOT IMPL" << std::endl + << " --solver-time-limit , --solver-time\n" + " stop search after N milliseconds wall time" + << std::endl + << " --solver-time-limit-feas , --solver-tlf\n" + " stop search after N milliseconds wall time after the first feasible solution" + << std::endl + << " -n , --num-solutions \n" + " stop search after N solutions" + << std::endl + << " --workmem , --nodefilestart \n" + " maximal RAM for node tree used before writing to node file, GB, default: 3" + << std::endl + << " --writeModel \n write model to (.lp, .mps, .sav, ...)" << std::endl + << " --readParam \n read GUROBI parameters from file" << std::endl + << " --writeParam \n write GUROBI parameters to file" + << std::endl + // << " --tuneParam instruct GUROBI to tune parameters instead of solving NOT + // IMPL" + + << "\n --absGap \n absolute gap |primal-dual| to stop" << std::endl + << " --relGap \n relative gap |primal-dual|/ to stop. Default 1e-8, set <0 " + "to use backend's default" + << std::endl + << " --feasTol \n primal feasibility tolerance. Default 1e-8" << std::endl + << " --intTol \n integrality tolerance for a variable. Gurobi recommends at least " + "feasTol. Default 1e-8" + << std::endl + // << " --objDiff objective function discretization. Default 1.0" << std::endl + + << "\n --gurobi-dll or \n Gurobi DLL, or base name, such as gurobi75, " + "when using plugin. Default range tried: " + << gurobiDLLs().front() << " .. " << gurobiDLLs().back() << std::endl + << std::endl; +} + +bool MIP_gurobi_wrapper::Options::processOption(int& i, std::vector& argv) { + MiniZinc::CLOParser cop(i, argv); + if (string(argv[i]) == "-a" || string(argv[i]) == "--all" || + string(argv[i]) == "--all-solutions") { + flag_all_solutions = true; + } else if (string(argv[i]) == "-f") { + } else if (string(argv[i]) == "--fixed-search") { + nFreeSearch = 0; + } else if (string(argv[i]) == "--uniform-search") { + nFreeSearch = 2; + } else if (cop.get("--mipfocus --mipFocus --MIPFocus --MIPfocus", &nMIPFocus)) { + } else if (cop.get("--writeModel", &sExportModel)) { + } else if (cop.get("-p", &nThreads)) { + } else if (cop.get("--solver-time-limit --solver-time", &nTimeout1000)) { + } else if (cop.get("--solver-time-limit-feas --solver-tlf", &nTimeoutFeas1000)) { + } else if (cop.get("-n --num-solutions", &nSolLimit)) { + } else if (cop.get("--workmem --nodefilestart", &nWorkMemLimit)) { + } else if (cop.get("--readParam", &sReadParams)) { + } else if (cop.get("--writeParam", &sWriteParams)) { + } else if (cop.get("--absGap", &absGap)) { + } else if (cop.get("--relGap", &relGap)) { + } else if (cop.get("--feasTol", &feasTol)) { + } else if (cop.get("--intTol", &intTol)) { + } else if (cop.get("--gurobi-dll", &sGurobiDLL)) { + // } else if ( cop.get( "--objDiff", &objDiff ) ) { + } else + return false; + return true; +} + +void MIP_gurobi_wrapper::wrap_assert(bool cond, string msg, bool fTerm) { + if (!cond) { + gurobi_buffer = "[NO ERROR STRING GIVEN]"; + if (error) { + gurobi_buffer = dll_GRBgeterrormsg(env); + } + string msgAll = + (" MIP_gurobi_wrapper runtime error: " + gurobi_buffer + "\nMessage from caller: " + msg); + cerr << msgAll << "\nGurobi error code: " << error << endl; + if (fTerm) { + cerr << "TERMINATING." << endl; + throw runtime_error(msgAll); + } + } +} + +#ifdef GUROBI_PLUGIN + +namespace { +void* dll_open(const char* file) { +#ifdef HAS_DLFCN_H + if (MiniZinc::FileUtils::is_absolute(file)) { + return dlopen(file, RTLD_NOW); + } else { + return dlopen((std::string("lib") + file + ".so").c_str(), RTLD_NOW); + } +#else + if (MiniZinc::FileUtils::is_absolute(file)) { + return LoadLibrary(file); + } else { + return LoadLibrary((std::string(file) + ".dll").c_str()); + } +#endif +} +void* dll_sym(void* dll, const char* sym) { +#ifdef HAS_DLFCN_H + void* ret = dlsym(dll, sym); +#else + void* ret = GetProcAddress((HMODULE)dll, sym); +#endif + if (ret == NULL) + throw MiniZinc::InternalError("cannot load symbol " + string(sym) + " from gurobi dll"); + return ret; +} +void dll_close(void* dll) { +#ifdef HAS_DLFCN_H + dlclose(dll); +#else + FreeLibrary((HMODULE)dll); +#endif +} +} // namespace + +#endif + +void MIP_gurobi_wrapper::checkDLL() { +#ifdef GUROBI_PLUGIN + gurobi_dll = NULL; + if (options && options->sGurobiDLL.size()) { + gurobi_dll = dll_open(options->sGurobiDLL.c_str()); + } else { + for (const auto& s : gurobiDLLs()) { + gurobi_dll = dll_open(s.c_str()); + if (NULL != gurobi_dll) { + break; + } + } + } + + if (gurobi_dll == NULL) { + if (options == NULL || options->sGurobiDLL.empty()) { + throw MiniZinc::InternalError("cannot load gurobi dll, specify --gurobi-dll"); + } else { + throw MiniZinc::InternalError("cannot load gurobi dll `" + options->sGurobiDLL + "'"); + } + } + + *(void**)(&dll_GRBversion) = dll_sym(gurobi_dll, "GRBversion"); + *(void**)(&dll_GRBaddconstr) = dll_sym(gurobi_dll, "GRBaddconstr"); + *(void**)(&dll_GRBaddgenconstrIndicator) = dll_sym(gurobi_dll, "GRBaddgenconstrIndicator"); + *(void**)(&dll_GRBaddvars) = dll_sym(gurobi_dll, "GRBaddvars"); + *(void**)(&dll_GRBcbcut) = dll_sym(gurobi_dll, "GRBcbcut"); + *(void**)(&dll_GRBcbget) = dll_sym(gurobi_dll, "GRBcbget"); + *(void**)(&dll_GRBcblazy) = dll_sym(gurobi_dll, "GRBcblazy"); + *(void**)(&dll_GRBfreeenv) = dll_sym(gurobi_dll, "GRBfreeenv"); + *(void**)(&dll_GRBfreemodel) = dll_sym(gurobi_dll, "GRBfreemodel"); + *(void**)(&dll_GRBgetdblattr) = dll_sym(gurobi_dll, "GRBgetdblattr"); + *(void**)(&dll_GRBgetdblattrarray) = dll_sym(gurobi_dll, "GRBgetdblattrarray"); + *(void**)(&dll_GRBgetenv) = dll_sym(gurobi_dll, "GRBgetenv"); + *(void**)(&dll_GRBgeterrormsg) = dll_sym(gurobi_dll, "GRBgeterrormsg"); + *(void**)(&dll_GRBgetintattr) = dll_sym(gurobi_dll, "GRBgetintattr"); + *(void**)(&dll_GRBloadenv) = dll_sym(gurobi_dll, "GRBloadenv"); + *(void**)(&dll_GRBnewmodel) = dll_sym(gurobi_dll, "GRBnewmodel"); + *(void**)(&dll_GRBoptimize) = dll_sym(gurobi_dll, "GRBoptimize"); + *(void**)(&dll_GRBreadparams) = dll_sym(gurobi_dll, "GRBreadparams"); + *(void**)(&dll_GRBsetcallbackfunc) = dll_sym(gurobi_dll, "GRBsetcallbackfunc"); + *(void**)(&dll_GRBsetdblparam) = dll_sym(gurobi_dll, "GRBsetdblparam"); + *(void**)(&dll_GRBsetintattr) = dll_sym(gurobi_dll, "GRBsetintattr"); + *(void**)(&dll_GRBsetintattrlist) = dll_sym(gurobi_dll, "GRBsetintattrlist"); + *(void**)(&dll_GRBsetdblattrelement) = dll_sym(gurobi_dll, "GRBsetdblattrelement"); + *(void**)(&dll_GRBsetdblattrlist) = dll_sym(gurobi_dll, "GRBsetdblattrlist"); + *(void**)(&dll_GRBsetintparam) = dll_sym(gurobi_dll, "GRBsetintparam"); + *(void**)(&dll_GRBsetstrparam) = dll_sym(gurobi_dll, "GRBsetstrparam"); + *(void**)(&dll_GRBterminate) = dll_sym(gurobi_dll, "GRBterminate"); + *(void**)(&dll_GRBupdatemodel) = dll_sym(gurobi_dll, "GRBupdatemodel"); + *(void**)(&dll_GRBwrite) = dll_sym(gurobi_dll, "GRBwrite"); + *(void**)(&dll_GRBwriteparams) = dll_sym(gurobi_dll, "GRBwriteparams"); + +#else + + dll_GRBversion = GRBversion; + dll_GRBaddconstr = GRBaddconstr; + dll_GRBaddgenconstrIndicator = GRBaddgenconstrIndicator; + dll_GRBaddvars = GRBaddvars; + dll_GRBcbcut = GRBcbcut; + dll_GRBcbget = GRBcbget; + dll_GRBcblazy = GRBcblazy; + dll_GRBfreeenv = GRBfreeenv; + dll_GRBfreemodel = GRBfreemodel; + dll_GRBgetdblattr = GRBgetdblattr; + dll_GRBgetdblattrarray = GRBgetdblattrarray; + dll_GRBgetenv = GRBgetenv; + dll_GRBgeterrormsg = GRBgeterrormsg; + dll_GRBgetintattr = GRBgetintattr; + dll_GRBloadenv = GRBloadenv; + dll_GRBnewmodel = GRBnewmodel; + dll_GRBoptimize = GRBoptimize; + dll_GRBreadparams = GRBreadparams; + dll_GRBsetcallbackfunc = GRBsetcallbackfunc; + dll_GRBsetdblparam = GRBsetdblparam; + dll_GRBsetintattr = GRBsetintattr; + dll_GRBsetintattrlist = GRBsetintattrlist; + dll_GRBsetdblattrelement = GRBsetdblattrelement; + dll_GRBsetdblattrlist = GRBsetdblattrlist; + dll_GRBsetintparam = GRBsetintparam; + dll_GRBsetstrparam = GRBsetstrparam; + dll_GRBterminate = GRBterminate; + dll_GRBupdatemodel = GRBupdatemodel; + dll_GRBwrite = GRBwrite; + dll_GRBwriteparams = GRBwriteparams; + +#endif +} + +void MIP_gurobi_wrapper::openGUROBI() { + checkDLL(); + + /* Initialize the GUROBI environment */ + { + // cout << "% " << flush; // Gurobi 7.5.2 prints "Academic License..." + MiniZinc::StreamRedir redirStdout(stdout, stderr); + error = dll_GRBloadenv(&env, "mzn-gurobi.log"); + } + wrap_assert(!error, "Could not open GUROBI environment."); + error = dll_GRBsetintparam(env, "OutputFlag", 0); // Switch off output + // error = dll_GRBsetintparam(env, "LogToConsole", + // fVerbose ? 1 : 0); // also when flag_all_solutions? TODO + /* Create the problem. */ + error = dll_GRBnewmodel(env, &model, "mzn_gurobi", 0, NULL, NULL, NULL, NULL, NULL); + wrap_assert(model != NULL, "Failed to create LP."); +} + +void MIP_gurobi_wrapper::closeGUROBI() { + /* Free model */ + + // If not allocated, skip + if (nullptr != model) { + /* Free up the problem as allocated by GRB_createprob, if necessary */ + dll_GRBfreemodel(model); + model = 0; + } + + /* Free environment */ + + if (nullptr != env) dll_GRBfreeenv(env); + /// and at last: +// MIP_wrapper::cleanup(); +#ifdef GUROBI_PLUGIN + // dll_close(gurobi_dll); // Is called too many times, disabling. 2019-05-06 +#endif +} + +void MIP_gurobi_wrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, + MIP_wrapper::VarType* vt, string* names) { + /// Convert var types: + vector ctype(n); + vector pcNames(n); + for (size_t i = 0; i < n; ++i) { + pcNames[i] = (char*)names[i].c_str(); + switch (vt[i]) { + case REAL: + ctype[i] = GRB_CONTINUOUS; + break; + case INT: + ctype[i] = GRB_INTEGER; + break; + case BINARY: + ctype[i] = GRB_BINARY; + break; + default: + throw runtime_error(" MIP_wrapper: unknown variable type"); + } + } + error = dll_GRBaddvars(model, static_cast(n), 0, NULL, NULL, NULL, obj, lb, ub, &ctype[0], + &pcNames[0]); + wrap_assert(!error, "Failed to declare variables."); + error = dll_GRBupdatemodel(model); + wrap_assert(!error, "Failed to update model."); +} + +static char getGRBSense(MIP_wrapper::LinConType s) { + switch (s) { + case MIP_wrapper::LQ: + return GRB_LESS_EQUAL; + case MIP_wrapper::EQ: + return GRB_EQUAL; + case MIP_wrapper::GQ: + return GRB_GREATER_EQUAL; + default: + throw runtime_error(" MIP_gurobi_wrapper: unknown constraint sense"); + } +} + +void MIP_gurobi_wrapper::addRow(int nnz, int* rmatind, double* rmatval, + MIP_wrapper::LinConType sense, double rhs, int mask, + string rowName) { + //// Make sure in order to notice the indices of lazy constr: + ++nRows; + /// Convert var types: + char ssense = getGRBSense(sense); + const char* pRName = rowName.c_str(); + error = dll_GRBaddconstr(model, nnz, rmatind, rmatval, ssense, rhs, pRName); + wrap_assert(!error, "Failed to add constraint."); + int nLazyAttr = 0; + const bool fUser = (MaskConsType_Usercut & mask) != 0; + const bool fLazy = (MaskConsType_Lazy & mask) != 0; + /// Gurobi 6.5.2 has lazyness 1-3. + if (fUser) { + if (fLazy) + nLazyAttr = 2; // just active lazy + else + nLazyAttr = 3; // even LP-active + } else if (fLazy) + nLazyAttr = 1; // very lazy + if (nLazyAttr) { + nLazyIdx.push_back(nRows - 1); + nLazyValue.push_back(nLazyAttr); + } +} + +void MIP_gurobi_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, + double* rmatval, MIP_wrapper::LinConType sense, + double rhs, string rowName) { + wrap_assert(0 <= bVal && 1 >= bVal, "Gurobi: addIndicatorConstraint: bVal not 0/1"); + //// Make sure in order to notice the indices of lazy constr: also here? TODO + ++nRows; + char ssense = getGRBSense(sense); + error = dll_GRBaddgenconstrIndicator(model, rowName.c_str(), iBVar, bVal, nnz, rmatind, rmatval, + ssense, rhs); + wrap_assert(!error, "Failed to add indicator constraint."); +} + +bool MIP_gurobi_wrapper::addSearch(const std::vector& vars, const std::vector pri) { + assert(vars.size() == pri.size()); + static_assert(sizeof(VarId) == sizeof(int), "VarId should be (u)int currently"); + error = dll_GRBsetintattrlist(model, "BranchPriority", static_cast(vars.size()), + (int*)vars.data(), (int*)pri.data()); + wrap_assert(!error, "Failed to add branching priorities"); + return true; +} + +int MIP_gurobi_wrapper::getFreeSearch() { return options->nFreeSearch; } + +bool MIP_gurobi_wrapper::addWarmStart(const std::vector& vars, + const std::vector vals) { + assert(vars.size() == vals.size()); + static_assert(sizeof(VarId) == sizeof(int), "VarId should be (u)int currently"); + // error = GRBsetdblattrelement(model, "Start", 0, 1.0); + error = dll_GRBsetdblattrlist(model, "Start", static_cast(vars.size()), (int*)vars.data(), + (double*)vals.data()); + wrap_assert(!error, "Failed to add warm start"); + return true; +} + +void MIP_gurobi_wrapper::setVarBounds(int iVar, double lb, double ub) { + wrap_assert(lb <= ub, "mzn-gurobi: setVarBounds: lb>ub"); + error = dll_GRBsetdblattrelement(model, GRB_DBL_ATTR_LB, iVar, lb); + wrap_assert(!error, "mzn-gurobi: failed to set var lb."); + error = dll_GRBsetdblattrelement(model, GRB_DBL_ATTR_UB, iVar, ub); + wrap_assert(!error, "mzn-gurobi: failed to set var ub."); +} + +void MIP_gurobi_wrapper::setVarLB(int iVar, double lb) { + error = dll_GRBsetdblattrelement(model, GRB_DBL_ATTR_LB, iVar, lb); + wrap_assert(!error, "mzn-gurobi: failed to set var lb."); +} + +void MIP_gurobi_wrapper::setVarUB(int iVar, double ub) { + error = dll_GRBsetdblattrelement(model, GRB_DBL_ATTR_UB, iVar, ub); + wrap_assert(!error, "mzn-gurobi: failed to set var ub."); +} + +/// SolutionCallback ------------------------------------------------------------------------ +/// Gurobi ensures thread-safety +static int __stdcall solcallback(GRBmodel* model, void* cbdata, int where, void* usrdata) { + MIP_wrapper::CBUserInfo* info = (MIP_wrapper::CBUserInfo*)usrdata; + MIP_gurobi_wrapper* gw = static_cast(info->wrapper); + + double nodecnt = 0.0, actnodes = 0.0, objVal = 0.0; + int solcnt = 0; + int newincumbent = 0; + + if (GRB_CB_MIP == where) { + /* General MIP callback */ + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIP_OBJBND, &info->pOutput->bestBound); + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIP_NODLFT, &actnodes); + info->pOutput->nOpenNodes = static_cast(actnodes); + /// Check time after the 1st feas + if (-1e100 != info->nTime1Feas) { + double tNow; + gw->dll_GRBcbget(cbdata, where, GRB_CB_RUNTIME, (void*)&tNow); + if (tNow - info->nTime1Feas >= info->nTimeoutFeas) gw->dll_GRBterminate(model); + } + } else if (GRB_CB_MESSAGE == where) { + /* Message callback */ + if (info->fVerb) { + char* msg; + gw->dll_GRBcbget(cbdata, where, GRB_CB_MSG_STRING, &msg); + cerr << msg << flush; + } + } else if (GRB_CB_MIPSOL == where) { + /* MIP solution callback */ + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_NODCNT, &nodecnt); + info->pOutput->nNodes = static_cast(nodecnt); + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_OBJ, &objVal); + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_SOLCNT, &solcnt); + + if (fabs(info->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) { + newincumbent = 1; + // Not confirmed yet, see lazy cuts + // info->pOutput->objVal = objVal; + // info->pOutput->status = MIP_wrapper::SAT; + // info->pOutput->statusName = "feasible from a callback"; + } + if (newincumbent) { + assert(info->pOutput->x); + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_SOL, (void*)info->pOutput->x); + + info->pOutput->dWallTime = std::chrono::duration(std::chrono::steady_clock::now() - + info->pOutput->dWallTime0) + .count(); + info->pOutput->dCPUTime = double(std::clock() - info->pOutput->cCPUTime0) / CLOCKS_PER_SEC; + } + + /// Callback for lazy cuts + /// Before printing + if (info->cutcbfn && info->cutMask & MIP_wrapper::MaskConsType_Lazy) { + MIP_wrapper::CutInput cutInput; + cerr << " GRB: GRB_CB_MIPSOL (" << objVal << ") -> cut callback " << endl; + info->cutcbfn(*info->pOutput, cutInput, info->psi, true); + for (auto& cd : cutInput) { + // assert( cd.mask & MIP_wrapper::MaskConsType_Lazy ); + if (cd.mask & MIP_wrapper::MaskConsType_Lazy) { // take only lazy constr generators + int error = + gw->dll_GRBcblazy(cbdata, static_cast(cd.rmatind.size()), cd.rmatind.data(), + cd.rmatval.data(), getGRBSense(cd.sense), cd.rhs); + if (error) + cerr << " GRB_wrapper: failed to add lazy cut. " << endl; + else + newincumbent = -1; + // info->pOutput->objVal = 1e100; // to mark that we can get a new incumbent + // which should be printed + } + } + } + if (solcnt >= 0 /*This is solution number for Gurobi*/ && newincumbent >= 0) { + if (fabs(info->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) { + newincumbent = 1; + info->pOutput->objVal = objVal; + info->pOutput->status = MIP_wrapper::SAT; + info->pOutput->statusName = "feasible from a callback"; + } + } + if (newincumbent > 0) { + info->pOutput->dCPUTime = double(std::clock() - info->pOutput->cCPUTime0) / CLOCKS_PER_SEC; + + /// Set time for the 1st feas + if (0 <= info->nTimeoutFeas && -1e100 == info->nTime1Feas) + gw->dll_GRBcbget(cbdata, where, GRB_CB_RUNTIME, (void*)&info->nTime1Feas); + + /// Call the user function: + if (info->solcbfn) (*info->solcbfn)(*info->pOutput, info->psi); + + if (0 == info->nTimeoutFeas) gw->dll_GRBterminate(model); // Straight after feas + } + } else if (GRB_CB_MIPNODE == where) { + int status; + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPNODE_STATUS, &status); + if (status == GRB_OPTIMAL && info->cutcbfn) { // if cut handler given + MIP_wrapper::Output outpRlx; + outpRlx.x = info->pOutput->x; // using the sol output storage TODO? + outpRlx.nCols = info->pOutput->nCols; + assert(outpRlx.x && outpRlx.nCols); + // dll_GRBcbget(cbdata, where, GRB_CB_MIPNODE_RELOBJ, outpRlx.objVal); + gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPNODE_REL, (void*)outpRlx.x); + // cerr << " GRB: GRB_CB_MIPNODE -> cut callback " << endl; + MIP_wrapper::CutInput cutInput; + info->cutcbfn(outpRlx, cutInput, info->psi, false); + // static int nCuts=0; + // nCuts += cutInput.size(); + // if ( cutInput.size() ) + // cerr << "\n N CUTS: " << nCuts << endl; + for (auto& cd : cutInput) { + if (!(cd.mask & (MIP_wrapper::MaskConsType_Usercut | MIP_wrapper::MaskConsType_Lazy))) + throw runtime_error("Cut callback: should be user/lazy"); + if (cd.mask & MIP_wrapper::MaskConsType_Usercut) { + int error = + gw->dll_GRBcbcut(cbdata, static_cast(cd.rmatind.size()), cd.rmatind.data(), + cd.rmatval.data(), getGRBSense(cd.sense), cd.rhs); + if (error) cerr << " GRB_wrapper: failed to add user cut. " << endl; + } + if (cd.mask & MIP_wrapper::MaskConsType_Lazy) { + int error = + gw->dll_GRBcblazy(cbdata, static_cast(cd.rmatind.size()), cd.rmatind.data(), + cd.rmatval.data(), getGRBSense(cd.sense), cd.rhs); + if (error) cerr << " GRB_wrapper: failed to add lazy cut. " << endl; + } + } + } + } + return 0; +} /* END logcallback */ +// end SolutionCallback --------------------------------------------------------------------- + +MIP_gurobi_wrapper::Status MIP_gurobi_wrapper::convertStatus(int gurobiStatus) { + Status s = Status::UNKNOWN; + ostringstream oss; + /* Converting the status. */ + if (gurobiStatus == GRB_OPTIMAL) { + s = Status::OPT; + oss << "Optimal"; + } else if (gurobiStatus == GRB_INF_OR_UNBD) { + s = Status::UNSATorUNBND; + oss << "Infeasible or unbounded"; + } else if (gurobiStatus == GRB_INFEASIBLE) { + s = Status::UNSAT; + oss << "Infeasible"; + } else if (gurobiStatus == GRB_UNBOUNDED) { + oss << "Unbounded"; + s = Status::UNBND; + } else { + int solcount = 0; + error = dll_GRBgetintattr(model, "SolCount", &solcount); + wrap_assert(!error, " Failure to access solution count.", false); + if (solcount) s = Status::SAT; + oss << "Gurobi stopped with status " << gurobiStatus; + } + output.statusName = gurobi_status_buffer = oss.str(); + return s; +} + +void MIP_gurobi_wrapper::solve() { // Move into ancestor? + if (options->flag_all_solutions && 0 == nProbType) + cerr << "WARNING. --all-solutions for SAT problems not implemented." << endl; + + error = dll_GRBupdatemodel(model); // for model export + wrap_assert(!error, "Failed to update model."); + + /// ADDING LAZY CONSTRAINTS IF ANY + if (nLazyIdx.size()) { + assert(nLazyIdx.size() == nLazyValue.size()); + if (fVerbose) + cerr << " MIP_gurobi_wrapper: marking " << nLazyIdx.size() << " lazy cuts." << endl; + error = dll_GRBsetintattrlist(model, "Lazy", static_cast(nLazyIdx.size()), nLazyIdx.data(), + nLazyValue.data()); + wrap_assert(!error, "Failed to set constraint attribute."); + nLazyIdx.clear(); + nLazyValue.clear(); + error = dll_GRBupdatemodel(model); // for model export + wrap_assert(!error, "Failed to update model after modifying some constraint attr."); + } + + /////////////// Last-minute solver options ////////////////// + /* Turn on output to file */ + error = dll_GRBsetstrparam(dll_GRBgetenv(model), "LogFile", + ""); // FAILS to switch off in Ubuntu 15.04 + /* Turn on output to the screen */ + error = dll_GRBsetintparam(dll_GRBgetenv(model), "OutputFlag", + /*fVerbose ? 1 :*/ 0); // switch off, redirect in callback + // error = dll_GRBsetintparam(dll_GRBgetenv(model), "LogToConsole", + // fVerbose ? 1 : 0); // also when flag_all_solutions? TODO + wrap_assert(!error, " GUROBI Warning: Failure to switch screen indicator.", false); + // error = dll_GRB_setintparam (env, GRB_PARAM_ClockType, 1); // CPU time + // error = dll_GRB_setintparam (env, GRB_PARAM_MIP_Strategy_CallbackReducedLP, GRB__OFF); // + // Access original model + if (options->sExportModel.size()) { + error = dll_GRBwrite(model, options->sExportModel.c_str()); + wrap_assert(!error, "Failed to write LP to disk.", false); + } + + /// TODO + // if(all_solutions && obj.getImpl()) { + // IloNum lastObjVal = (obj.getSense() == IloObjective::Minimize ) ? + // _ilogurobi->use(SolutionCallback(_iloenv, lastObjVal, *this)); + // Turn off GUROBI logging + + if (options->nThreads > 0) { + error = dll_GRBsetintparam(dll_GRBgetenv(model), GRB_INT_PAR_THREADS, options->nThreads); + // int nn; // THE SETTING FAILS TO WORK IN 6.0.5. + // error = dll_getintparam(env, GRB_INT_PAR_THREADS, &nn); + // cerr << "Set " << nThreads << " threads, reported " << nn << endl; + wrap_assert(!error, "Failed to set GRB_INT_PAR_THREADS.", false); + } + + if (options->nTimeout1000 > 0) { + error = dll_GRBsetdblparam(dll_GRBgetenv(model), GRB_DBL_PAR_TIMELIMIT, + static_cast(options->nTimeout1000) / 1000.0); + wrap_assert(!error, "Failed to set GRB_PARAM_TimeLimit.", false); + } + + if (options->nSolLimit > 0) { + error = dll_GRBsetintparam(dll_GRBgetenv(model), GRB_INT_PAR_SOLUTIONLIMIT, options->nSolLimit); + wrap_assert(!error, "Failed to set GRB_INT_PAR_SOLLIMIT.", false); + } + + if (options->nWorkMemLimit > 0 && options->nWorkMemLimit < 1e200) { + error = dll_GRBsetdblparam(dll_GRBgetenv(model), "NodefileStart", options->nWorkMemLimit); + wrap_assert(!error, "Failed to set NodefileStart.", false); + } + + if (options->absGap >= 0.0) { + error = dll_GRBsetdblparam(dll_GRBgetenv(model), "MIPGapAbs", options->absGap); + wrap_assert(!error, "Failed to set MIPGapAbs.", false); + } + if (options->nMIPFocus > 0) { + error = dll_GRBsetintparam(dll_GRBgetenv(model), GRB_INT_PAR_MIPFOCUS, options->nMIPFocus); + wrap_assert(!error, "Failed to set GRB_INT_PAR_MIPFOCUS.", false); + } + + if (options->relGap >= 0.0) { + error = dll_GRBsetdblparam(dll_GRBgetenv(model), "MIPGap", options->relGap); + wrap_assert(!error, "Failed to set MIPGap.", false); + } + if (options->intTol >= 0.0) { + error = dll_GRBsetdblparam(dll_GRBgetenv(model), "IntFeasTol", options->intTol); + wrap_assert(!error, "Failed to set IntFeasTol.", false); + } + if (options->feasTol >= 0.0) { + error = dll_GRBsetdblparam(dll_GRBgetenv(model), "FeasibilityTol", options->feasTol); + wrap_assert(!error, "Failed to set FeasTol.", false); + } + + /// Solution callback + output.nCols = static_cast(colObj.size()); + x.resize(output.nCols); + output.x = &x[0]; + SolCallbackFn solcbfn = cbui.solcbfn; + if (true) { // Need for logging + cbui.fVerb = fVerbose; + cbui.nTimeoutFeas = options->nTimeoutFeas1000 / 1000.0; + if (!options->flag_all_solutions) cbui.solcbfn = 0; + if (cbui.cutcbfn) { + assert(cbui.cutMask & (MaskConsType_Usercut | MaskConsType_Lazy)); + if (cbui.cutMask & MaskConsType_Usercut) { + // For user cuts, needs to keep some info after presolve + if (fVerbose) + cerr << " MIP_gurobi_wrapper: user cut callback enabled, setting PreCrush=1" << endl; + error = dll_GRBsetintparam(dll_GRBgetenv(model), GRB_INT_PAR_PRECRUSH, 1); + wrap_assert(!error, "Failed to set GRB_INT_PAR_PRECRUSH.", false); + } + if (cbui.cutMask & MaskConsType_Lazy) { + // For lazy cuts, Gurobi disables some presolves + if (fVerbose) + cerr << " MIP_gurobi_wrapper: lazy cut callback enabled, setting LazyConstraints=1" + << endl; + error = dll_GRBsetintparam(dll_GRBgetenv(model), GRB_INT_PAR_LAZYCONSTRAINTS, 1); + wrap_assert(!error, "Failed to set GRB_INT_PAR_LAZYCONSTRAINTS.", false); + } + } + error = dll_GRBsetcallbackfunc(model, solcallback, (void*)&cbui); + wrap_assert(!error, "Failed to set callback", false); + } + + /// after all modifs + if (options->sReadParams.size()) { + error = dll_GRBreadparams(dll_GRBgetenv(model), options->sReadParams.c_str()); + wrap_assert(!error, "Failed to read GUROBI parameters.", false); + } + + if (options->sWriteParams.size()) { + error = dll_GRBwriteparams(dll_GRBgetenv(model), options->sWriteParams.c_str()); + wrap_assert(!error, "Failed to write GUROBI parameters.", false); + } + + cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); + output.dCPUTime = cbui.pOutput->cCPUTime0 = std::clock(); + + /* Optimize the problem and obtain solution. */ + error = dll_GRBoptimize(model); + wrap_assert(!error, "Failed to optimize MIP."); + + output.dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - output.dWallTime0).count(); + output.dCPUTime = (std::clock() - output.dCPUTime) / CLOCKS_PER_SEC; + + int solstat; + error = dll_GRBgetintattr(model, GRB_INT_ATTR_STATUS, &solstat); + wrap_assert(!error, "Failed to get MIP status.", false); + output.status = convertStatus(solstat); + + /// Continuing to fill the output object: + if (Status::OPT == output.status || Status::SAT == output.status) { + error = dll_GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &output.objVal); + wrap_assert(!error, "No MIP objective value available."); + + // int cur_numrows = dll_GRB_getnumrows (env, lp); + int cur_numcols = getNCols(); + assert(cur_numcols == colObj.size()); + + x.resize(cur_numcols); + output.x = &x[0]; + error = dll_GRBgetdblattrarray(model, GRB_DBL_ATTR_X, 0, cur_numcols, (double*)output.x); + wrap_assert(!error, "Failed to get variable values."); + if (!options->flag_all_solutions && solcbfn) { + solcbfn(output, cbui.psi); + } + } + output.bestBound = 1e308; + error = dll_GRBgetdblattr(model, GRB_DBL_ATTR_OBJBOUNDC, &output.bestBound); + wrap_assert(!error, "Failed to get the best bound.", false); + double nNodes = -1; + error = dll_GRBgetdblattr(model, GRB_DBL_ATTR_NODECOUNT, &nNodes); + output.nNodes = static_cast(nNodes); + output.nOpenNodes = 0; +} + +void MIP_gurobi_wrapper::setObjSense(int s) { + error = dll_GRBsetintattr(model, GRB_INT_ATTR_MODELSENSE, s > 0 ? GRB_MAXIMIZE : GRB_MINIMIZE); + wrap_assert(!error, "Failed to set obj sense."); +} diff --git a/solvers/MIP/MIP_osicbc_solverfactory.cpp b/solvers/MIP/MIP_osicbc_solverfactory.cpp new file mode 100644 index 0000000..57e6679 --- /dev/null +++ b/solvers/MIP/MIP_osicbc_solverfactory.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace MiniZinc { +namespace { +void getWrapper() { + static MIP_SolverFactory _osicbc_solver_factory; + return; +} +} // namespace +OSICBC_SolverFactoryInitialiser::OSICBC_SolverFactoryInitialiser(void) { getWrapper(); } +} // namespace MiniZinc diff --git a/solvers/MIP/MIP_osicbc_wrap.cpp b/solvers/MIP/MIP_osicbc_wrap.cpp new file mode 100644 index 0000000..1b80525 --- /dev/null +++ b/solvers/MIP/MIP_osicbc_wrap.cpp @@ -0,0 +1,1081 @@ +// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define WANT_SOLUTION + +string MIP_osicbc_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options*) { + string v = "MIP wrapper for COIN-BC "; + v += CBC_VERSION; // E.g., 2.9 stable or 2.9.7 latest release + v += ", using CLP "; + v += CLP_VERSION; + v += " Compiled " __DATE__ " " __TIME__; + return v; +} + +string MIP_osicbc_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options*) { + return string(CBC_VERSION) + "/" + string(CLP_VERSION); +} + +string MIP_osicbc_wrapper::getId() { return "coin-bc"; } + +string MIP_osicbc_wrapper::getName() { return "COIN-BC"; } + +vector MIP_osicbc_wrapper::getTags() { + return {"mip", "float", "api", "osicbc", "coinbc", "cbc"}; +} + +vector MIP_osicbc_wrapper::getStdFlags() { return {"-a", "-p", "-s"}; } + +void MIP_osicbc_wrapper::Options::printHelp(ostream& os) { + os << "COIN-BC MIP wrapper options:" + << std::endl + // -s print statistics + // << " --readParam read OSICBC parameters from file + // << "--writeParam write OSICBC parameters to file + // << "--tuneParam instruct OSICBC to tune parameters instead of solving + << " --cbcArgs, --cbcFlags, --cbc-flags \"args\"\n" + " command-line args passed to callCbc, e.g., \"-cuts off -preprocess off -passc 1\"." + << std::endl + // \"-preprocess off\" recommended in 2.9.6 + << " --writeModel " << endl + << " write model to (.mps)" << std::endl + << " -a, --all\n print intermediate solutions for optimization problems\n" + " (not from FeasPump. Can be slow.)" + << std::endl + << " -p \n use N threads, default: 1. CBC should be configured with " + "--enable-cbc-parallel" + << std::endl + // << "--nomippresolve disable MIP presolving NOT IMPL" << std::endl + << " --solver-time-limit \n stop search after N milliseconds" + << std::endl + // << "--workmem maximal amount of RAM used, MB" << std::endl + // << "--readParam read OSICBC parameters from file" << std::endl + // << "--writeParam write OSICBC parameters to file" << std::endl + // << "--tuneParam instruct OSICBC to tune parameters instead of solving NOT IMPL" + + << " --absGap \n absolute gap |primal-dual| to stop" << std::endl + << " --relGap \n relative gap |primal-dual|/ to stop. Default 1e-8, set <0 " + "to use backend's default" + << std::endl + << " --intTol \n integrality tolerance for a variable. Default 1e-8" + << std::endl + // << "--objDiff objective function discretization. Default 1.0" << std::endl + + << std::endl; +} + +bool MIP_osicbc_wrapper::Options::processOption(int& i, std::vector& argv) { + MiniZinc::CLOParser cop(i, argv); + if (string(argv[i]) == "-a" || string(argv[i]) == "--all" || + string(argv[i]) == "--all-solutions") { + flag_all_solutions = true; + } else if (string(argv[i]) == "-f") { + // std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl; + } else if (cop.get("--writeModel", &sExportModel)) { + } else if (cop.get("-p", &nThreads)) { + } else if (cop.get("--solver-time-limit", &nTimeout)) { + } else if (cop.get("--workmem", &nWorkMemLimit)) { + } else if (cop.get("--readParam", &sReadParams)) { + } else if (cop.get("--writeParam", &sWriteParams)) { + } else if (cop.get("--cbcArgs --cbcFlags --cbc-flags --solver-flags", &cbc_cmdOptions)) { + } else if (cop.get("--absGap", &absGap)) { + } else if (cop.get("--relGap", &relGap)) { + } else if (cop.get("--intTol", &intTol)) { + // } else if ( cop.get( "--objDiff", &objDiff ) ) { + } else + return false; + return true; +} + +void MIP_osicbc_wrapper::wrap_assert(bool cond, string msg, bool fTerm) { + if (!cond) { + // strcpy(osicbc_buffer, "[NO ERROR STRING GIVEN]"); + // CBCgeterrorstring (env, status, osicbc_buffer); + string msgAll = (" MIP_osicbc_wrapper runtime error: " + msg + " " + osicbc_buffer); + cerr << msgAll << endl; + if (fTerm) { + cerr << "TERMINATING." << endl; + throw runtime_error(msgAll); + } + } +} + +void MIP_osicbc_wrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, + MIP_wrapper::VarType* vt, string* names) { + /// Convert var types: + // vector ctype(n); + // vector pcNames(n); + CoinPackedVector cpv; + vector pCpv(n, &cpv); + osi.addCols(n, pCpv.data(), lb, ub, obj); // setting integer & names later + // status = CBCnewcols (env, lp, n, obj, lb, ub, &ctype[0], &pcNames[0]); + // wrap_assert( !status, "Failed to declare variables." ); +} + +void MIP_osicbc_wrapper::addRow(int nnz, int* rmatind, double* rmatval, + MIP_wrapper::LinConType sense, double rhs, int mask, + string rowName) { + /// Convert var types: + double rlb = rhs, rub = rhs; + switch (sense) { + case LQ: + rlb = -osi.getInfinity(); + break; + case EQ: + break; + case GQ: + rub = osi.getInfinity(); + break; + default: + throw runtime_error(" MIP_wrapper: unknown constraint type"); + } + // ignoring mask for now. TODO + // 1-by-1 too slow: + // try { + // CoinPackedVector cpv(nnz, rmatind, rmatval); + // osi.addRow(cpv, rlb, rub); + // } catch (const CoinError& err) { + // cerr << " COIN-OR Error: " << err.message() << endl; + // throw runtime_error(err.message()); + // } + /// Segfault: + // rowStarts.push_back(columns.size()); + // columns.insert(columns.end(), rmatind, rmatind + nnz); + // element.insert(element.end(), rmatval, rmatval + nnz); + rows.push_back(CoinPackedVector(nnz, rmatind, rmatval)); + rowlb.push_back(rlb); + rowub.push_back(rub); +} + +bool MIP_osicbc_wrapper::addWarmStart(const std::vector& vars, + const std::vector vals) { + assert(vars.size() == vals.size()); + static_assert(sizeof(VarId) == sizeof(int), "VarId should be (u)int currently"); + for (int i = 0; i < vars.size(); ++i) warmstart[vars[i]] = vals[i]; + return true; +} + +/// SolutionCallback ------------------------------------------------------------------------ +/// OSICBC ensures thread-safety?? TODO +/// Event handling copied from examples/interrupt.cpp, Cbc 2.9.8 rev 2272 + +/************************************************************************ + +This main program shows how to take advantage of the standalone cbc in your program, +while still making major modifications. +This is very like driver4 but allows interrupts in clp for faster stopping +It would be up to user to clean up output as stopping in Clp seems to +give correct results but can think it is stopping in an odd way. +To make cleaner would need more events defined (in Cbc AND Clp) +First it reads in an integer model from an mps file +Then it initializes the integer model with cbc defaults +Then it calls CbcMain1 passing all parameters apart from first but with callBack to modify stuff +Finally it prints solution + +************************************************************************/ +/* Meaning of whereFrom: + 1 after initial solve by dualsimplex etc + 2 after preprocessing + 3 just before branchAndBound (so user can override) + 4 just after branchAndBound (before postprocessing) + 5 after postprocessing +*/ +/* Meaning of model status is as normal + status + -1 before branchAndBound + 0 finished - check isProvenOptimal or isProvenInfeasible to see if solution found + (or check value of best solution) + 1 stopped - on maxnodes, maxsols, maxtime + 2 difficulties so run was abandoned + (5 event user programmed event occurred) + + cbc secondary status of problem + -1 unset (status_ will also be -1) + 0 search completed with solution + 1 linear relaxation not feasible (or worse than cutoff) + 2 stopped on gap + 3 stopped on nodes + 4 stopped on time + 5 stopped on user event + 6 stopped on solutions + 7 linear relaxation unbounded + + but initially check if status is 0 and secondary status is 1 -> infeasible + or you can check solver status. +*/ +/* Return non-zero to return quickly */ +// static int callBack(CbcModel * model, int whereFrom) +//{ +// int returnCode=0; +// switch (whereFrom) { +// case 1: +// case 2: +// if (!model->status()&&model->secondaryStatus()) +// returnCode=1; +// break; +// case 3: +// { +// //CbcCompareUser compare; +// //model->setNodeComparison(compare); +// } +// break; +// case 4: +// // If not good enough could skip postprocessing +// break; +// case 5: +// break; +// default: +// abort(); +// } +// return returnCode; +//} +static int cancelAsap = 0; +/* + 0 - not yet in Cbc + 1 - in Cbc with new signal handler + 2 - ending Cbc +*/ +static int statusOfCbc = 0; +static CoinSighandler_t saveSignal = static_cast(0); + +extern "C" { +static void +#if defined(_MSC_VER) + __cdecl +#endif // _MSC_VER + signal_handler(int /*whichSignal*/) { + cancelAsap = 3; + return; +} +} +/** This is so user can trap events and do useful stuff. + + CbcModel model_ is available as well as anything else you care + to pass in +*/ + +struct EventUserInfo { + MIP_wrapper::CBUserInfo* pCbui = 0; + CglPreProcess* pPP = 0; +}; + +extern CglPreProcess* cbcPreProcessPointer; +class MyEventHandler3 : public CbcEventHandler { +public: + /**@name Overrides */ + //@{ + virtual CbcAction event(CbcEvent whichEvent); + //@} + + /**@name Constructors, destructor etc*/ + //@{ + /** Default constructor. */ + MyEventHandler3(EventUserInfo& u_); + /// Constructor with pointer to model (redundant as setEventHandler does) + MyEventHandler3(CbcModel* model, EventUserInfo& u_); + /** Destructor */ + virtual ~MyEventHandler3(); + /** The copy constructor. */ + MyEventHandler3(const MyEventHandler3& rhs); + /// Assignment + MyEventHandler3& operator=(const MyEventHandler3& rhs); + /// Clone + virtual CbcEventHandler* clone() const; + //@} + +protected: + // data goes here + EventUserInfo ui; + double bestSolutionValue_ = DBL_MAX; // always min +}; +//------------------------------------------------------------------- +// Default Constructor +//------------------------------------------------------------------- +MyEventHandler3::MyEventHandler3(EventUserInfo& u_) : CbcEventHandler(), ui(u_) { assert(0); } + +//------------------------------------------------------------------- +// Copy constructor +//------------------------------------------------------------------- +MyEventHandler3::MyEventHandler3(const MyEventHandler3& rhs) : CbcEventHandler(rhs), ui(rhs.ui) {} + +// Constructor with pointer to model +MyEventHandler3::MyEventHandler3(CbcModel* model, EventUserInfo& u_) + : CbcEventHandler(model), ui(u_) {} + +//------------------------------------------------------------------- +// Destructor +//------------------------------------------------------------------- +MyEventHandler3::~MyEventHandler3() {} + +//---------------------------------------------------------------- +// Assignment operator +//------------------------------------------------------------------- +MyEventHandler3& MyEventHandler3::operator=(const MyEventHandler3& rhs) { + if (this != &rhs) { + CbcEventHandler::operator=(rhs); + } + ui = rhs.ui; + return *this; +} +//------------------------------------------------------------------- +// Clone +//------------------------------------------------------------------- +CbcEventHandler* MyEventHandler3::clone() const { return new MyEventHandler3(*this); } + +CbcEventHandler::CbcAction MyEventHandler3::event(CbcEvent whichEvent) { + if (!statusOfCbc) { + // override signal handler + // register signal handler + saveSignal = signal(SIGINT, signal_handler); + statusOfCbc = 1; + } + if ((cancelAsap & 2) != 0) { + // printf("Cbc got cancel\n"); + // switch off Clp cancel + cancelAsap &= 2; + return stop; + } + // If in sub tree carry on + if (!model_->parentModel()) { + if (whichEvent == endSearch && statusOfCbc == 1) { + // switch off cancel + cancelAsap = 0; + // restore signal handler + signal(SIGINT, saveSignal); + statusOfCbc = 2; + } + if (whichEvent == solution || whichEvent == heuristicSolution) { + // John Forrest 27.2.16: + // check not duplicate + if (model_->getObjValue() < bestSolutionValue_) { + bestSolutionValue_ = model_->getObjValue(); + // If preprocessing was done solution will be to processed model + // int numberColumns = model_->getNumCols(); + const double* bestSolution = model_->bestSolution(); + assert(bestSolution); + // printf("value of solution is %g\n",model_->getObjValue()); + + // Trying to obtain solution for the original model: + assert(model_ && model_->solver()); + // double objOffset=0; + // model_->solver()->getDblParam(OsiObjOffset, objOffset); + double objVal = + (model_->getObjValue()); //- objOffset); John Forrest suggested to remove, 17.11.17 + double bestBnd = (model_->getBestPossibleObjValue()); //- objOffset); + if (0 != cbcPreProcessPointer) { + if (OsiSolverInterface* cbcPreOrig = cbcPreProcessPointer->originalModel()) { + objVal *= cbcPreOrig->getObjSense(); + bestBnd *= cbcPreOrig->getObjSense(); + } + } else { + objVal *= model_->getObjSense(); + bestBnd *= model_->getObjSense(); + } + OsiSolverInterface* origModel = 0; + if (0 != cbcPreProcessPointer && 0 != model_->continuousSolver()) { + OsiSolverInterface* solver = (model_->continuousSolver()->clone()); + // ? model_->continuousSolver()->clone() + // : model_->continuousSolver()->clone(); + int numberColumns = solver->getNumCols(); + for (int i = 0; i < numberColumns; i++) { + if (solver->isInteger(i)) { + solver->setColLower(i, bestSolution[i]); + solver->setColUpper(i, bestSolution[i]); + } + } + solver->resolve(); + cbcPreProcessPointer->postProcess(*solver, false); + delete solver; + origModel = cbcPreProcessPointer->originalModel(); + ui.pCbui->pOutput->x = origModel->getColSolution(); + } else { + origModel = model_->solver(); + ui.pCbui->pOutput->x = bestSolution; + } + if (ui.pCbui->fVerb) + cerr << " % OBJ VAL RAW: " << model_->getObjValue() << " OBJ VAL ORIG(?): " << objVal + << " % BND RAW: " << model_->getBestPossibleObjValue() << " BND ORIG(?): " + << bestBnd + // << " &prepro: " << cbcPreProcessPointer + // << " &model_._solver(): " << model_->solver() + << " orig NCols: " << ui.pCbui->pOutput->nCols + << " prepro NCols: " << model_->getNumCols(); + assert(origModel->getNumCols() == ui.pCbui->pOutput->nCols); + if (ui.pCbui->fVerb) { + if (ui.pCbui->pOutput->nObjVarIndex >= 0) + cerr << " objVAR: " << ui.pCbui->pOutput->x[ui.pCbui->pOutput->nObjVarIndex]; + cerr << endl; + } + ui.pCbui->pOutput->objVal = objVal; + // origModel->getObjValue(); + ui.pCbui->pOutput->status = MIP_wrapper::SAT; + ui.pCbui->pOutput->statusName = "feasible from a callback"; + ui.pCbui->pOutput->bestBound = bestBnd; + ui.pCbui->pOutput->dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - + ui.pCbui->pOutput->dWallTime0) + .count(); + ui.pCbui->pOutput->dCPUTime = model_->getCurrentSeconds(); + ui.pCbui->pOutput->nNodes = model_->getNodeCount(); + ui.pCbui->pOutput->nOpenNodes = -1; // model_->getNodeCount2(); + + /// Call the user function: + if (ui.pCbui->solcbfn) { + (*(ui.pCbui->solcbfn))(*(ui.pCbui->pOutput), ui.pCbui->psi); + ui.pCbui->printed = true; + } + return noAction; // carry on + } else { + return noAction; // carry on + } + } else { + return noAction; + } + } else { + return noAction; // carry on + } +} + +/** This is so user can trap events and do useful stuff. + + ClpSimplex model_ is available as well as anything else you care + to pass in +*/ +class MyEventHandler4 : public ClpEventHandler { +public: + /**@name Overrides */ + //@{ + virtual int event(Event whichEvent); + //@} + + /**@name Constructors, destructor etc*/ + //@{ + /** Default constructor. */ + MyEventHandler4(); + /// Constructor with pointer to model (redundant as setEventHandler does) + MyEventHandler4(ClpSimplex* model); + /** Destructor */ + virtual ~MyEventHandler4(); + /** The copy constructor. */ + MyEventHandler4(const MyEventHandler4& rhs); + /// Assignment + MyEventHandler4& operator=(const MyEventHandler4& rhs); + /// Clone + virtual ClpEventHandler* clone() const; + //@} + +protected: + // data goes here +}; +//------------------------------------------------------------------- +// Default Constructor +//------------------------------------------------------------------- +MyEventHandler4::MyEventHandler4() : ClpEventHandler() {} + +//------------------------------------------------------------------- +// Copy constructor +//------------------------------------------------------------------- +MyEventHandler4::MyEventHandler4(const MyEventHandler4& rhs) : ClpEventHandler(rhs) {} + +// Constructor with pointer to model +MyEventHandler4::MyEventHandler4(ClpSimplex* model) : ClpEventHandler(model) {} + +//------------------------------------------------------------------- +// Destructor +//------------------------------------------------------------------- +MyEventHandler4::~MyEventHandler4() {} + +//---------------------------------------------------------------- +// Assignment operator +//------------------------------------------------------------------- +MyEventHandler4& MyEventHandler4::operator=(const MyEventHandler4& rhs) { + if (this != &rhs) { + ClpEventHandler::operator=(rhs); + } + return *this; +} +//------------------------------------------------------------------- +// Clone +//------------------------------------------------------------------- +ClpEventHandler* MyEventHandler4::clone() const { return new MyEventHandler4(*this); } + +int MyEventHandler4::event(Event whichEvent) { + if ((cancelAsap & 1) != 0) { + // printf("Clp got cancel\n"); + return 5; + } else { + return -1; + } +} +// end SolutionCallback --------------------------------------------------------------------- + +MIP_osicbc_wrapper::Status MIP_osicbc_wrapper::convertStatus(CbcModel* pModel) { + Status s = Status::UNKNOWN; + /* Converting the status. */ + if (pModel->isProvenOptimal()) { + s = Status::OPT; + output.statusName = "Optimal"; + // wrap_assert(osi., "Optimality reported but pool empty?", false); + } else if (pModel->isProvenInfeasible()) { + s = Status::UNSAT; + output.statusName = "Infeasible"; + } else if (pModel->isProvenDualInfeasible()) { + s = Status::UNBND; + output.statusName = "Dual infeasible"; + // s = Status::UNSATorUNBND; + } else if // wrong: (pModel->getColSolution()) + (fabs(pModel->getObjValue()) < 1e50) { + s = Status::SAT; + output.statusName = "Feasible"; + } else if (pModel->isAbandoned()) { // AFTER feas-ty + s = Status::__ERROR; + output.statusName = "Abandoned"; + } else { + s = Status::UNKNOWN; + output.statusName = "Unknown"; + } + return s; +} + +MIP_osicbc_wrapper::Status MIP_osicbc_wrapper::convertStatus() { + Status s = Status::UNKNOWN; + /* Converting the status. */ + if (osi.isProvenOptimal()) { + s = Status::OPT; + output.statusName = "Optimal"; + // wrap_assert(osi., "Optimality reported but pool empty?", false); + } else if (osi.isProvenPrimalInfeasible()) { + s = Status::UNSAT; + output.statusName = "Infeasible"; + } else if (osi.isProvenDualInfeasible()) { + s = Status::UNBND; + output.statusName = "Dual infeasible"; + // s = Status::UNSATorUNBND; + } else if (osi.isAbandoned()) { + s = Status::__ERROR; + output.statusName = "Abandoned"; + } else if // wrong: (pModel->getColSolution()) + (fabs(osi.getObjValue()) < osi.getInfinity()) { + s = Status::SAT; + output.statusName = "Feasible"; + cout << " getSolverObjValue(as minim) == " << osi.getObjValue() << endl; + } else { + s = Status::UNKNOWN; + output.statusName = "Unknown"; + } + return s; +} + +void MIP_osicbc_wrapper::solve() { // Move into ancestor? + if (options->flag_all_solutions && 0 == nProbType) + cerr << "WARNING. --all-solutions for SAT problems not implemented." << endl; + try { + /// Not using CoinPackedMatrix any more, so need to add all constraints at once: + /// But this gives segf: + // osi.addRows(rowStarts.size(), rowStarts.data(), + // columns.data(), element.data(), rowlb.data(), rowub.data()); + /// So: + MIP_wrapper::addPhase1Vars(); // only now + if (fVerbose) cerr << " MIP_osicbc_wrapper: adding constraints physically..." << flush; + vector pRows(rowlb.size()); + for (int i = 0; i < rowlb.size(); ++i) pRows[i] = &rows[i]; + osi.addRows(rowlb.size(), pRows.data(), rowlb.data(), rowub.data()); + // rowStarts.clear(); + // columns.clear(); + // element.clear(); + pRows.clear(); + rows.clear(); + rowlb.clear(); + rowub.clear(); + if (fVerbose) cerr << " done." << endl; + /////////////// Last-minute solver options ////////////////// + // osi->loadProblem(*matrix, + { + std::vector integer_vars; + for (unsigned int i = 0; i < colObj.size(); i++) { + if (REAL != colTypes[i] + // && is_used[i] + ) { + integer_vars.push_back(i); + } + } + osi.setInteger(integer_vars.data(), integer_vars.size()); + } + if (options->sExportModel.size()) { + // Not implemented for OsiClp: + // osi.setColNames(colNames, 0, colObj.size(), 0); + vector colN(colObj.size()); + for (int j = 0; j < colNames.size(); ++j) colN[j] = colNames[j].c_str(); + osi.writeMpsNative(options->sExportModel.c_str(), 0, colN.data()); + } + + // Tell solver to return fast if presolve or initial solve infeasible + osi.getModelPtr()->setMoreSpecialOptions(3); + // allow Clp to handle interrupts + MyEventHandler4 clpEventHandler; + osi.getModelPtr()->passInEventHandler(&clpEventHandler); + + /* switch on/off output to the screen */ + class NullCoinMessageHandler : public CoinMessageHandler { + int print() { return 0; } + void checkSeverity() {} + } nullHandler; + + // CbcSolver control(osi); + // // initialize ??????? + // control.fillValuesInSolver(); + // CbcModel * pModel = control.model(); + if (fVerbose) cerr << " Model creation..." << endl; + + // #define __USE_CbcSolver__ -- not linked rev2274 + /// FOR WARMSTART + for (const auto& vv : warmstart) { + osi.setColName(vv.first, colNames[vv.first]); + } +#ifdef __USE_CbcSolver__ + CbcSolver control(osi); + // initialize + control.fillValuesInSolver(); + CbcModel& model = *control.model(); +#else + CbcModel model(osi); +#endif + // CbcSolver control(osi); + // control.solve(); + if (options->absGap >= 0.0) model.setAllowableGap(options->absGap); + if (options->relGap >= 0.0) model.setAllowableFractionGap(options->relGap); + if (options->intTol >= 0.0) model.setIntegerTolerance(options->intTol); + // model.setCutoffIncrement( objDiff ); + + /// WARMSTART + { + std::vector > mipstart; + for (const auto& vv : warmstart) { + mipstart.push_back(std::make_pair(colNames[vv.first], vv.second)); + } + warmstart.clear(); + model.setMIPStart(mipstart); + } + + CoinMessageHandler msgStderr(stderr); + + class StderrCoinMessageHandler : public CoinMessageHandler { + int print() { + cerr << messageBuffer_ << endl; + return 0; + } + void checkSeverity() {} + } stderrHandler; + + if (fVerbose) { + // osi.messageHandler()->setLogLevel(1); + // osi.getModelPtr()->setLogLevel(1); + // osi.getRealSolverPtr()->messageHandler()->setLogLevel(0); + // DOES NOT WORK: TODO + // model.passInMessageHandler( &stderrHandler ); + msgStderr.setLogLevel(0, 1); + model.passInMessageHandler(&msgStderr); + // model.setLogLevel(1); + // model.solver()->messageHandler()->setLogLevel(0); + } else { + model.passInMessageHandler(&nullHandler); + model.messageHandler()->setLogLevel(0); + model.setLogLevel(0); + model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry); + // osi.passInMessageHandler(&nullHandler); + // osi.messageHandler()->setLogLevel(0); + // osi.setHintParam(OsiDoReducePrint, true, OsiHintTry); + } + + if (options->nTimeout != 0) { + // osi.setMaximumSeconds(nTimeout); + model.setMaximumSeconds(static_cast(options->nTimeout) / 1000.0); + } + + /// TODO + // if(all_solutions && obj.getImpl()) { + // IloNum lastObjVal = (obj.getSense() == IloObjective::Minimize ) ? + // _iloosicbc->use(SolutionCallback(_iloenv, lastObjVal, *this)); + // Turn off OSICBC logging + + /// Solution callback + output.nCols = colObj.size(); + // x.resize(output.nCols); + // output.x = &x[0]; + + if (options->flag_all_solutions && cbui.solcbfn) { + // Event handler. Should be after CbcMain0()? + EventUserInfo ui; + ui.pCbui = &cbui; + // ui.pPP = 0; + MyEventHandler3 eventHandler(&model, ui); + model.passInEventHandler(&eventHandler); + } + + /// Cuts needed + if (cbui.cutcbfn) { + /// This class is passed to CBC to organize cut callbacks + /// We need original solutions here (combinatorial cuts) + class CutCallback : public CglCutGenerator { + MIP_wrapper::CBUserInfo& cbui; + + public: + CutCallback(MIP_wrapper::CBUserInfo& ui) : cbui(ui) {} + CglCutGenerator* clone() const override { return new CutCallback(cbui); } + /// Make sure this overrides but we might need to compile this with old CBC as well + bool needsOriginalModel() const /*override*/ { return true; } + void generateCuts(const OsiSolverInterface& si, OsiCuts& cs, + const CglTreeInfo info = CglTreeInfo()) override { + cbui.pOutput->nCols = si.getNumCols(); + MZN_ASSERT_HARD_MSG( + cbui.pOutput->nCols == ((MIP_wrapper*)(cbui.wrapper))->colNames.size(), + "CBC cut callback: current model is different? Ncols=" + << cbui.pOutput->nCols << ", originally " + << ((MIP_wrapper*)(cbui.wrapper))->colNames.size() + << ". If you have an old version of CBC, to use combinatorial cuts" + " run with --cbcArgs '-preprocess off'"); + cbui.pOutput->x = si.getColSolution(); // change the pointer? + MIP_wrapper::CutInput cuts; + cbui.cutcbfn(*cbui.pOutput, cuts, cbui.psi, + info.options & 128); // options&128: integer candidate + for (const auto& cut : cuts) { // Convert cut sense + OsiRowCut rc; + switch (cut.sense) { + case LQ: + rc.setUb(cut.rhs); + break; + case GQ: + rc.setLb(cut.rhs); + break; + default: + assert(EQ == cut.sense); + rc.setLb(cut.rhs); + rc.setUb(cut.rhs); + } + rc.setRow(cut.rmatind.size(), cut.rmatind.data(), cut.rmatval.data()); + cs.insertIfNotDuplicate(rc); + } + } + }; + CutCallback ccb(cbui); + model.addCutGenerator(&ccb, 10, "MZN_cuts", true, true); // also at solution + } + + if (1 < options->nThreads) { + options->cbc_cmdOptions += " -threads "; + ostringstream oss; + oss << options->nThreads; + options->cbc_cmdOptions += oss.str(); + } + options->cbc_cmdOptions += " -solve"; + options->cbc_cmdOptions += " -quit"; + + cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); + output.dCPUTime = clock(); + + /* OLD: Optimize the problem and obtain solution. */ + // model.branchAndBound(); + // osi.branchAndBound(); + + /// TAKEN FORM DRIVER3.CPP, seems to use most features: +// CbcMain0(model); +// CbcCbcParamUtils::setCbcModelDefaults(model) ; +// const char * argv2[]={"mzn-cbc","-solve","-quit"}; +// CbcMain1(3,argv2,model); +#ifdef __USE_CbcSolver__ + if (fVerbose) + cerr << " Calling control.solve() with options '" << options->cbc_cmdOptions << "'..." + << endl; + control.solve(options->cbc_cmdOptions.c_str(), 1); +#else +#define __USE_callCbc1__ +#ifdef __USE_callCbc1__ + if (fVerbose) + cerr << " Calling callCbc with options '" << options->cbc_cmdOptions << "'..." << endl; + callCbc(options->cbc_cmdOptions, model); +// callCbc1(cbc_cmdOptions, model, callBack); +// What is callBack() for? TODO +#else + CbcMain0(model); + // should be here? + // // Event handler + // EventUserInfo ui; + // MyEventHandler3 eventHandler( &model, ui ); + // model.passInEventHandler(&eventHandler); + /* Now go into code for standalone solver + Could copy arguments and add -quit at end to be safe + but this will do + */ + vector argvS; + MiniZinc::split(cbc_cmdOptions, argvS); + vector argv; + MiniZinc::vecString2vecPChar(argvS, argv); + if (fVerbose) cerr << " Calling CbcMain1 with options '" << cbc_cmdOptions << "'..." << endl; + CbcMain1(argv.size(), argv.data(), model, callBack); +#endif +#endif + + output.dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - output.dWallTime0).count(); + output.dCPUTime = (clock() - output.dCPUTime) / CLOCKS_PER_SEC; + + output.status = convertStatus(&model); + // output.status = convertStatus(); + + /// Continuing to fill the output object: + if (Status::OPT == output.status || Status::SAT == output.status) { + output.objVal = model.getObjValue(); + // output.objVal = osi.getObjValue(); + + /* The size of the problem should be obtained by asking OSICBC what + the actual size is, rather than using what was passed to CBCcopylp. + cur_numrows and cur_numcols store the current number of rows and + columns, respectively. */ // ?????????????? TODO + + int cur_numcols = model.getNumCols(); + // int cur_numcols = osi.getNumCols (); + assert(cur_numcols == colObj.size()); + + wrap_assert(model.getColSolution(), "Failed to get variable values."); + x.assign(model.getColSolution(), model.getColSolution() + cur_numcols); // ColSolution(); + output.x = x.data(); + // output.x = osi.getColSolution(); + if (cbui.solcbfn && (!options->flag_all_solutions || !cbui.printed)) { + cbui.solcbfn(output, cbui.psi); + } + } + output.bestBound = model.getBestPossibleObjValue(); + // output.bestBound = -1; + output.nNodes = model.getNodeCount(); + // output.nNodes = osi.getNodeCount(); + output.nOpenNodes = -1; + + } catch (CoinError& err) { + err.print(true); + } +} + +void MIP_osicbc_wrapper::setObjSense(int s) { osi.setObjSense(-s); } + +/* + +try the following for example: + +CbcMain0(model); +const char * argv2[]={"driver4","-cuts","off" ,"-preprocess","off","-passc","1","-solve","-quit"}; +CbcMain1(9,argv2,model); + +you can add any feature you want to argv2 ... + +if you want to add cuts yourself, or heuristics, do the following: + + OsiSolverInterface *solver2 = osi; + CglPreProcess *process = new CglPreProcess; + solver2 = process->preProcess(*solver,false,2); + + CbcModel model1(*solver2); + + model1.initialSolve(); + + //============================================== + + CglProbing generator1; + generator1.setUsingObjective(true); + generator1.setMaxPass(1); + generator1.setMaxPassRoot(5); + generator1.setMaxProbe(10); + generator1.setMaxProbeRoot(1000); + generator1.setMaxLook(50); + generator1.setMaxLookRoot(500); + generator1.setMaxElements(200); + generator1.setRowCuts(3); + + CglGomory generator2; + generator2.setLimit(300); + + CglKnapsackCover generator3; + + CglRedSplit generator4; + generator4.setLimit(200); + + CglClique generator5; + generator5.setStarCliqueReport(false); + generator5.setRowCliqueReport(false); + + CglMixedIntegerRounding2 mixedGen; + CglFlowCover flowGen; + + CglGMI cut1; + CglMixedIntegerRounding2 cut2; + CglOddHole cut3; + CglSimpleRounding cut4; + CglResidualCapacity cut5; + CglTwomir cut6; + CglZeroHalf cut7; + + model1.addCutGenerator(&generator1,-1,"Probing"); + model1.addCutGenerator(&generator2,-1,"Gomory"); + model1.addCutGenerator(&generator3,-1,"Knapsack"); + model1.addCutGenerator(&generator4,-1,"RedSplit"); + model1.addCutGenerator(&generator5,-1,"Clique"); + model1.addCutGenerator(&flowGen,-1,"FlowCover"); + model1.addCutGenerator(&mixedGen,-1,"MixedIntegerRounding"); + model1.addCutGenerator(&cut1,-1,"GMI"); + model1.addCutGenerator(&cut2,-1,"MixedIntegerRounding2"); + model1.addCutGenerator(&cut3,-1,"OddHole"); + model1.addCutGenerator(&cut4,-1,"SimpleRounding"); + model1.addCutGenerator(&cut5,-1,"ResidualCapacity"); + model1.addCutGenerator(&cut6,-1,"Twomir"); + model1.addCutGenerator(&cut7,-1,"ZeroHalf"); + + + + CbcRounding heuristic1(model1); + CbcHeuristicLocal heuristic2(model1); + + + model1.addHeuristic(&heuristic1); + model1.addHeuristic(&heuristic2); + + + + + model1.setMaximumCutPassesAtRoot(50); + model1.setMaximumCutPasses(1000); + + + + model1.branchAndBound(); + + + OsiSolverInterface * solver3; + + process->postProcess(*model1.solver()); + + solver3 = solver; + + or, use the default strategy: + +CbcStrategyDefault strategy(5); +model1.setStrategy(strategy); + + + + + + +On Sun, Oct 11, 2015 at 8:38 PM, Gleb Belov wrote: + + Hi, + + I am trying to call Cbc 2.9.6 from my program. When using the tutorial-style approach + + OsiClpSolverInterface osi; + osi.add ....... + CbcModel model(osi); + model.branchAndBound(); + + there seem to be no cuts and other stuff applied. When using the method from the examples, + + CbcMain0(model); + const char * argv2[]={"driver4","-solve","-quit"}; + CbcMain1(3,argv2,model); + + there are cuts applied, but obviously different (less aggressive) to the standalone Cbc +executable. I also tried CbcSolver class but its method solve() is not found by the linker. So what +is the 'standard' way of using the 'default' add-ons? + + Moreover. The attached example crashes both in the standalone Cbc and in the CbcCmain0/1 variant +after a few minutes. + + Thanks + + _______________________________________________ + Cbc mailing list + Cbc@list.coin-or.org + http://list.coin-or.org/mailman/listinfo/cbc + + + + +Hi, what is currently good way to have a solution callback in Cbc? the +interrupt example shows 2 ways, don't know which is right. + +Moreover, it says that the solution would be given for the preprocessed +model. Is it possible to produce one for the original? Is it possible to +call other functions from inside, such as number of nodes, dual bound? + +Thanks + +From john.forrest at fastercoin.com Thu Oct 8 10:34:15 2015 +From: john.forrest at fastercoin.com (John Forrest) +Date: Thu, 8 Oct 2015 15:34:15 +0100 +Subject: [Cbc] Solution callbacks +In-Reply-To: <5615F778.9020601@monash.edu> +References: <5615F778.9020601@monash.edu> +Message-ID: <56167EE7.6000607@fastercoin.com> + +Gleb, + +On 08/10/15 05:56, Gleb Belov wrote: +> Hi, what is currently good way to have a solution callback in Cbc? the +> interrupt example shows 2 ways, don't know which is right. +> + +It is the event handling code you would be using. +> Moreover, it says that the solution would be given for the +> preprocessed model. Is it possible to produce one for the original? + +At present no. In principle not difficult. First the callback function +would have to be modified to get passed the CglPreProcess object - +easy. Then in event handler you could make a copy of object and +postsolve (you need a copy as postsolve deletes data). +> Is it possible to call other functions from inside, such as number of +> nodes, dual bound? + +Yes - you have CbcModel * model_ so things like that are available (or +could easily be made available) + +> +> Thanks +> + +John Forrest + + + */ diff --git a/solvers/MIP/MIP_scip_solverfactory.cpp b/solvers/MIP/MIP_scip_solverfactory.cpp new file mode 100644 index 0000000..e27d8b6 --- /dev/null +++ b/solvers/MIP/MIP_scip_solverfactory.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace MiniZinc { +namespace { +void getWrapper() { + static MIP_SolverFactory _scip_solver_factory; + return; +} +} // namespace +SCIP_SolverFactoryInitialiser::SCIP_SolverFactoryInitialiser(void) { getWrapper(); } +} // namespace MiniZinc diff --git a/solvers/MIP/MIP_scip_wrap.cpp b/solvers/MIP/MIP_scip_wrap.cpp new file mode 100644 index 0000000..b9166a0 --- /dev/null +++ b/solvers/MIP/MIP_scip_wrap.cpp @@ -0,0 +1,581 @@ +// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include + +#include "scip/scipshell.h" + +std::string MIP_scip_wrapper::getMznLib() { return "-Glinear_scip"; } + +string MIP_scip_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) { + ostringstream oss; + oss << "MIP wrapper for SCIP " << getVersion(opt) << ". Compiled " __DATE__ " " __TIME__; + return oss.str(); +} +string MIP_scip_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) { + ostringstream oss; + oss << SCIPmajorVersion() << '.' << SCIPminorVersion() << '.' << SCIPtechVersion() << '.' + << SCIPsubversion(); + return oss.str(); +} +string MIP_scip_wrapper::needDllFlag() { return ""; } + +string MIP_scip_wrapper::getId() { return "scip"; } + +string MIP_scip_wrapper::getName() { return "SCIP"; } + +vector MIP_scip_wrapper::getTags() { return {"mip", "float", "api"}; } + +vector MIP_scip_wrapper::getStdFlags() { return {"-a", "-p", "-s"}; } + +void MIP_scip_wrapper::Options::printHelp(ostream& os) { + os << "SCIP MIP wrapper options:" + << std::endl + // -s print statistics + // << " --readParam read SCIP parameters from file + // << "--writeParam write SCIP parameters to file + // << "--tuneParam instruct SCIP to tune parameters instead of solving + << "--writeModel write model to (.lp, .mps, ...?)" << std::endl + << "-a print intermediate solutions (use for optimization problems only TODO)" + << std::endl + << "-p use N threads, default: 1" + << std::endl + // << "--nomippresolve disable MIP presolving NOT IMPL" << std::endl + << "--solver-time-limit stop search after N milliseconds" << std::endl + << "--workmem maximal amount of RAM used, MB" << std::endl + << "--readParam read SCIP parameters from file" << std::endl + << "--writeParam write SCIP parameters to file" + << std::endl + // << "--tuneParam instruct SCIP to tune parameters instead of solving NOT IMPL" + + << "--absGap absolute gap |primal-dual| to stop" << std::endl + << "--relGap relative gap |primal-dual|/ to stop. Default 1e-8, set <0 " + "to use backend's default" + << std::endl + << "--intTol integrality tolerance for a variable. Default 1e-8" + << std::endl + // << "--objDiff objective function discretization. Default 1.0" << std::endl + + << std::endl; +} + +static inline bool beginswith(string s, string t) { return s.compare(0, t.length(), t) == 0; } + +bool MIP_scip_wrapper::Options::processOption(int& i, vector& argv) { + MiniZinc::CLOParser cop(i, argv); + if (string(argv[i]) == "-a" || string(argv[i]) == "--all" || + string(argv[i]) == "--all-solutions") { + flag_all_solutions = true; + } else if (string(argv[i]) == "-f") { + // std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl; + } else if (cop.get("--writeModel", &sExportModel)) { + } else if (cop.get("-p", &nThreads)) { + } else if (cop.get("--solver-time-limit", &nTimeout)) { + } else if (cop.get("--workmem", &nWorkMemLimit)) { + } else if (cop.get("--readParam", &sReadParams)) { + } else if (cop.get("--writeParam", &sWriteParams)) { + } else if (cop.get("--absGap", &absGap)) { + } else if (cop.get("--relGap", &relGap)) { + } else if (cop.get("--intTol", &intTol)) { + // } else if ( cop.get( "--objDiff", &objDiff ) ) { + } else + return false; + return true; +error: + return false; +} + +void MIP_scip_wrapper::wrap_assert(SCIP_RETCODE retcode, string msg, bool fTerm) { + /* evaluate return code of the SCIP process */ + if (retcode != SCIP_OKAY) { + /* write error back trace */ + SCIPprintError(retcode); + string msgAll = (" MIP_scip_wrapper runtime error, see output: " + msg); + cerr << msgAll << endl; + if (fTerm) { + cerr << "TERMINATING." << endl; + throw runtime_error(msgAll); + } + } +} + +SCIP_RETCODE MIP_scip_wrapper::openSCIP() { + SCIP_CALL(SCIPcreate(&scip)); + SCIP_CALL(SCIPincludeDefaultPlugins(scip)); + + /* create empty problem */ + SCIP_CALL(SCIPcreateProbBasic(scip, "mzn_scip")); + return SCIP_OKAY; +} + +SCIP_RETCODE MIP_scip_wrapper::closeSCIP() { + SCIP_CALL(SCIPfree(&scip)); + /// and at last: + // MIP_wrapper::cleanup(); + return SCIP_OKAY; +} + +SCIP_RETCODE MIP_scip_wrapper::doAddVars_SCIP(size_t n, double* obj, double* lb, double* ub, + MIP_wrapper::VarType* vt, string* names) { + /// Convert var types: + // vector ctype(n); + // vector pcNames(n); + for (size_t j = 0; j < n; ++j) { + // pcNames[i] = (char*)names[i].c_str(); + SCIP_VARTYPE ctype; + switch (vt[j]) { + case REAL: + ctype = SCIP_VARTYPE_CONTINUOUS; + break; + case INT: + ctype = SCIP_VARTYPE_INTEGER; + break; + case BINARY: + ctype = SCIP_VARTYPE_BINARY; + break; + default: + throw runtime_error(" MIP_wrapper: unknown variable type"); + } + scipVars.resize(scipVars.size() + 1); + if (fPhase1Over) assert(scipVars.size() == colObj.size()); + SCIP_CALL( + SCIPcreateVarBasic(scip, &scipVars.back(), names[j].c_str(), lb[j], ub[j], obj[j], ctype)); + SCIP_CALL(SCIPaddVar(scip, scipVars.back())); + } + // retcode = SCIP_newcols (env, lp, n, obj, lb, ub, &ctype[0], &pcNames[0]); + // wrap_assert( !retcode, "Failed to declare variables." ); + return SCIP_OKAY; +} + +SCIP_RETCODE MIP_scip_wrapper::delSCIPVars() { + for (size_t j = 0; j < scipVars.size(); ++j) SCIPreleaseVar(scip, &scipVars[j]); + return SCIP_OKAY; +} + +SCIP_RETCODE MIP_scip_wrapper::addRow_SCIP(int nnz, int* rmatind, double* rmatval, + MIP_wrapper::LinConType sense, double rhs, int mask, + string rowName) { + /// Convert var types: + double lh = -SCIPinfinity(scip), rh = SCIPinfinity(scip); + switch (sense) { + case LQ: + rh = rhs; + break; + case EQ: + lh = rh = rhs; + break; + case GQ: + lh = rhs; + break; + default: + throw runtime_error(" MIP_wrapper: unknown constraint type"); + } + const int ccnt = 0; + const int rcnt = 1; + const int rmatbeg[] = {0}; + char* pRName = (char*)rowName.c_str(); + // ignoring mask for now. TODO + SCIP_CONS* cons; + vector ab(nnz); + + for (int j = 0; j < nnz; ++j) ab[j] = scipVars[rmatind[j]]; + + SCIP_CALL(SCIPcreateConsBasicLinear(scip, &cons, rowName.c_str(), nnz, &ab[0], rmatval, lh, rh)); + SCIP_CALL(SCIPaddCons(scip, cons)); + SCIP_CALL(SCIPreleaseCons(scip, &cons)); + return SCIP_OKAY; + // retcode = SCIP_addrows (env, lp, ccnt, rcnt, nnz, &rhs, + // &ssense, rmatbeg, rmatind, rmatval, + // NULL, &pRName); + // wrap_assert( !retcode, "Failed to add constraint." ); +} + +void MIP_scip_wrapper::setVarBounds(int iVar, double lb, double ub) { + wrap_assert(lb <= ub ? SCIP_OKAY : SCIP_ERROR, "scip interface: setVarBounds: lb>ub"); + setVarLB(iVar, lb); + setVarUB(iVar, ub); +} + +void MIP_scip_wrapper::setVarLB(int iVar, double lb) { + auto res = SCIPchgVarLbGlobal(scip, scipVars[iVar], lb); + wrap_assert(res, "scip interface: failed to set var lb."); +} + +void MIP_scip_wrapper::setVarUB(int iVar, double ub) { + auto res = SCIPchgVarUbGlobal(scip, scipVars[iVar], ub); + wrap_assert(res, "scip interface: failed to set var ub."); +} + +void MIP_scip_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, + double* rmatval, MIP_wrapper::LinConType sense, + double rhs, string rowName) { + MZN_ASSERT_HARD_MSG(0 <= bVal && 1 >= bVal, "SCIP: addIndicatorConstraint: bVal not 0/1"); + //// Make sure in order to notice the indices of lazy constr: also here? TODO + // ++ nRows; + + SCIP_CONS* cons; + vector ab(nnz); + SCIP_VAR* + indicator_var; // SCIP 6.0.1 requires that the implication is active for indicator_x == 1 + + for (int j = 0; j < nnz; ++j) ab[j] = scipVars[rmatind[j]]; + + indicator_var = scipVars[iBVar]; + if (0 == bVal) { + wrap_assert(SCIPgetNegatedVar(scip, indicator_var, &indicator_var)); + } + + if (LQ == sense || EQ == sense) { + wrap_assert(SCIPcreateConsBasicIndicator(scip, &cons, rowName.c_str(), indicator_var, nnz, + ab.data(), rmatval, rhs)); + wrap_assert(SCIPaddCons(scip, cons)); + wrap_assert(SCIPreleaseCons(scip, &cons)); + } + if (GQ == sense || EQ == sense) { + std::vector rmatvalNEG(nnz); + for (int i = nnz; i--;) rmatvalNEG[i] = -rmatval[i]; + wrap_assert(SCIPcreateConsBasicIndicator(scip, &cons, rowName.c_str(), indicator_var, nnz, + ab.data(), rmatvalNEG.data(), -rhs)); + wrap_assert(SCIPaddCons(scip, cons)); + wrap_assert(SCIPreleaseCons(scip, &cons)); + } +} + +void MIP_scip_wrapper::addBoundsDisj(int n, double* fUB, double* bnd, int* vars, int nF, + double* fUBF, double* bndF, int* varsF, string rowName) { + SCIP_CONS* cons; + std::vector v(n + nF); + std::vector bt(n + nF); + std::vector bs(n + nF); + + for (int j = 0; j < n; ++j) { + v[j] = scipVars[vars[j]]; + bt[j] = fUB[j] ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER; + bs[j] = bnd[j]; + } + for (int j = 0; j < nF; ++j) { + v[n + j] = scipVars[varsF[j]]; + bt[n + j] = fUBF[j] ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER; + bs[n + j] = bndF[j]; + } + + wrap_assert(SCIPcreateConsBasicBounddisjunction(scip, &cons, rowName.c_str(), v.size(), v.data(), + bt.data(), bs.data())); + wrap_assert(SCIPaddCons(scip, cons)); + wrap_assert(SCIPreleaseCons(scip, &cons)); +} + +void MIP_scip_wrapper::addCumulative(int nnz, int* rmatind, double* d, double* r, double b, + string rowName) { + SCIP_CONS* cons; + vector ab(nnz); + vector nd(nnz), nr(nnz); + + for (int j = 0; j < nnz; ++j) { + ab[j] = scipVars[rmatind[j]]; + nd[j] = (int)round(d[j]); + nr[j] = (int)round(r[j]); + } + + wrap_assert(SCIPcreateConsBasicCumulative(scip, &cons, rowName.c_str(), nnz, ab.data(), nd.data(), + nr.data(), (int)round(b))); + + wrap_assert(SCIPaddCons(scip, cons)); + wrap_assert(SCIPreleaseCons(scip, &cons)); +} + +/// SolutionCallback ------------------------------------------------------------------------ + +/// From event_bestsol.c: +#define EVENTHDLR_NAME "bestsol" +#define EVENTHDLR_DESC "event handler for best solutions found" + +/** includes event handler for best solution found */ +extern SCIP_RETCODE SCIPincludeEventHdlrBestsol(SCIP* scip /**< SCIP data structure */ +); + +/** copy method for event handler plugins (called when SCIP copies plugins) */ +static SCIP_DECL_EVENTCOPY(eventCopyBestsol) { /*lint --e{715}*/ + assert(scip != NULL); + assert(eventhdlr != NULL); + assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); + + /* call inclusion method of event handler */ + SCIP_CALL(SCIPincludeEventHdlrBestsol(scip)); + + return SCIP_OKAY; +} + +/** initialization method of event handler (called after problem was transformed) */ +static SCIP_DECL_EVENTINIT(eventInitBestsol) { /*lint --e{715}*/ + assert(scip != NULL); + assert(eventhdlr != NULL); + assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); + + /* notify SCIP that your event handler wants to react on the event type best solution found */ + SCIP_CALL(SCIPcatchEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, NULL, NULL)); + + return SCIP_OKAY; +} + +/** deinitialization method of event handler (called before transformed problem is freed) */ +static SCIP_DECL_EVENTEXIT(eventExitBestsol) { /*lint --e{715}*/ + assert(scip != NULL); + assert(eventhdlr != NULL); + assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); + + /* notify SCIP that your event handler wants to drop the event type best solution found */ + SCIP_CALL(SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, eventhdlr, NULL, -1)); + + return SCIP_OKAY; +} + +static MIP_wrapper::CBUserInfo* cbuiPtr = 0; +static SCIP_VAR** scipVarsPtr = 0; + +/** execution method of event handler */ +static SCIP_DECL_EVENTEXEC(eventExecBestsol) { /*lint --e{715}*/ + SCIP_SOL* bestsol; + SCIP_Real objVal; + int newincumbent = 0; + + assert(eventhdlr != NULL); + assert(strcmp(SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); + assert(event != NULL); + assert(scip != NULL); + assert(SCIPeventGetType(event) == SCIP_EVENTTYPE_BESTSOLFOUND); + + SCIPdebugMessage("exec method of event handler for best solution found\n"); + + bestsol = SCIPgetBestSol(scip); + assert(bestsol != NULL); + objVal = SCIPgetSolOrigObj(scip, bestsol); + + if (!cbuiPtr) return SCIP_OKAY; + + if (fabs(cbuiPtr->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) { + newincumbent = 1; + cbuiPtr->pOutput->objVal = objVal; + cbuiPtr->pOutput->status = MIP_wrapper::SAT; + cbuiPtr->pOutput->statusName = "feasible from a callback"; + } + + if (newincumbent && scipVarsPtr) { + assert(cbuiPtr->pOutput->x); + SCIP_CALL(SCIPgetSolVals(scip, bestsol, cbuiPtr->pOutput->nCols, scipVarsPtr, + (double*)cbuiPtr->pOutput->x)); + // wrap_assert(!retcode, "Failed to get variable values."); + cbuiPtr->pOutput->nNodes = SCIPgetNNodes(scip); + cbuiPtr->pOutput->nOpenNodes = SCIPgetNNodesLeft(scip); + cbuiPtr->pOutput->bestBound = SCIPgetDualbound(scip); + + cbuiPtr->pOutput->dCPUTime = -1; + + /// Call the user function: + if (cbuiPtr->solcbfn) (*cbuiPtr->solcbfn)(*cbuiPtr->pOutput, cbuiPtr->psi); + } + + return SCIP_OKAY; +} + +/** includes event handler for best solution found */ +SCIP_RETCODE SCIPincludeEventHdlrBestsol(SCIP* scip /**< SCIP data structure */ +) { + SCIP_EVENTHDLRDATA* eventhdlrdata; + SCIP_EVENTHDLR* eventhdlr; + eventhdlrdata = NULL; + + eventhdlr = NULL; + /* create event handler for events on watched variables */ + SCIP_CALL(SCIPincludeEventhdlrBasic(scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, + eventExecBestsol, eventhdlrdata)); + assert(eventhdlr != NULL); + + /// Not for sub-SCIPs + // SCIP_CALL( SCIPsetEventhdlrCopy(scip, eventhdlr, eventCopyBestsol) ); + SCIP_CALL(SCIPsetEventhdlrInit(scip, eventhdlr, eventInitBestsol)); + SCIP_CALL(SCIPsetEventhdlrExit(scip, eventhdlr, eventExitBestsol)); + + return SCIP_OKAY; +} + +MIP_scip_wrapper::Status MIP_scip_wrapper::convertStatus(SCIP_STATUS scipStatus) { + Status s = Status::UNKNOWN; + /* Converting the status. */ + switch (scipStatus) { + case SCIP_STATUS_OPTIMAL: + s = Status::OPT; + output.statusName = "Optimal"; + assert(SCIPgetNSolsFound(scip)); + break; + case SCIP_STATUS_INFEASIBLE: + s = Status::UNSAT; + output.statusName = "Infeasible"; + break; + // case SCIP_MIP_OPTIMAL_INFEAS: + case SCIP_STATUS_INFORUNBD: + s = Status::UNSATorUNBND; + output.statusName = "Infeasible or unbounded"; + break; + // case SCIP_MIP_SOL_LIM: + // s = Status::SAT; + // wrap_assert(SCIP_getsolnpoolnumsolns(env, lp), "Feasibility reported but pool + // empty?", false); break; + case SCIP_STATUS_UNBOUNDED: + s = Status::UNBND; + output.statusName = "Unbounded"; + break; + // case SCIP_STATUSMIP_ABORT_INFEAS: + // case SCIP_MIP_FAIL_INFEAS: + // s = Status::ERROR; + // break; + default: + // case SCIP_MIP_OPTIMAL_TOL: + // case SCIP_MIP_ABORT_RELAXATION_UNBOUNDED: + if (SCIPgetNSols(scip)) { + s = Status::SAT; + output.statusName = "Feasible"; + } else { + s = Status::UNKNOWN; + output.statusName = "Unknown"; + } + } + return s; +} + +SCIP_DECL_MESSAGEWARNING(printMsg) { cerr << msg << flush; } + +SCIP_RETCODE MIP_scip_wrapper::solve_SCIP() { // Move into ancestor? + + /////////////// Last-minute solver options ////////////////// + if (options->flag_all_solutions && 0 == nProbType) + cerr << "WARNING. --all-solutions for SAT problems not implemented." << endl; + + if (options->nThreads > 0) SCIP_CALL(SCIPsetIntParam(scip, "lp/threads", options->nThreads)); + + if (options->nTimeout > 0) + SCIP_CALL( + SCIPsetRealParam(scip, "limits/time", static_cast(options->nTimeout) / 1000.0)); + + if (options->nWorkMemLimit > 0) + SCIP_CALL(SCIPsetRealParam(scip, "limits/memory", options->nWorkMemLimit)); + + if (options->absGap >= 0.0) SCIP_CALL(SCIPsetRealParam(scip, "limits/absgap", options->absGap)); + if (options->relGap >= 0.0) SCIP_CALL(SCIPsetRealParam(scip, "limits/gap", options->relGap)); + if (options->intTol >= 0.0) + SCIP_CALL(SCIPsetRealParam(scip, "numerics/feastol", options->intTol)); + + // retcode = SCIP_setintparam (env, SCIP_PARAM_ClockType, 1); // CPU time + // wrap_assert(!retcode, " SCIP Warning: Failure to measure CPU time.", false); + + if (!options->sExportModel.empty()) { + // std::cerr <<" Exporting LP model to " << sExportModel << " ..." << std::endl; + SCIP_CALL(SCIPwriteOrigProblem(scip, options->sExportModel.c_str(), 0, 0)); + } + + /* Turn on output to the screen - after model export */ + if (!fVerbose) { + // SCIP_CALL(SCIPsetMessagehdlr(scip, NULL)); No LP export then + SCIPsetMessagehdlrQuiet(scip, true); + } else { + SCIP_MESSAGEHDLR* pHndl = 0; + SCIP_CALL(SCIPmessagehdlrCreate(&pHndl, FALSE, NULL, FALSE, printMsg, printMsg, printMsg, NULL, + NULL)); + SCIP_CALL(SCIPsetMessagehdlr(scip, pHndl)); + } + + // assert(scipVars.size() == colObj.size()); + int cur_numcols = scipVars.size(); // No, we create negated indicators: getNCols(); + assert(cur_numcols == colObj.size()); + assert(cur_numcols == scipVars.size()); + + /// Solution callback + output.nCols = colObj.size(); + x.resize(output.nCols); + output.x = &x[0]; + if (options->flag_all_solutions && cbui.solcbfn && !cbuiPtr) { + /* include event handler for best solution found */ + SCIP_CALL(SCIPincludeEventHdlrBestsol(scip)); + cbuiPtr = &cbui; // not thread-safe... TODO + scipVarsPtr = &scipVars[0]; + // retcode = SCIP_setinfocallbackfunc (env, solcallback, &cbui); + // wrap_assert(!retcode, "Failed to set solution callback", false); + } + + if (options->sReadParams.size()) { + SCIP_CALL(SCIPreadParams(scip, options->sReadParams.c_str())); + } + + if (options->sWriteParams.size()) { + SCIP_CALL(SCIPwriteParams(scip, options->sReadParams.c_str(), TRUE, FALSE)); + } + + cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); + output.dCPUTime = clock(); + + /* Optimize the problem and obtain solution. */ + SCIP_CALL(SCIPsolve(scip)); + // wrap_assert( !retcode, "Failed to optimize MIP." ); + + output.dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - output.dWallTime0).count(); + output.dCPUTime = (clock() - output.dCPUTime) / CLOCKS_PER_SEC; + + cbuiPtr = 0; /// cleanup + scipVarsPtr = 0; + + SCIP_STATUS solstat = SCIPgetStatus(scip); + output.status = convertStatus(solstat); + // output.statusName = SCIP_getstatstring (env, solstat, scip_status_buffer); + + /// Continuing to fill the output object: + output.objVal = SCIPgetPrimalbound(scip); + output.bestBound = SCIPgetDualbound(scip); + // wrap_assert(!retcode, "Failed to get the best bound.", false); + if (Status::OPT == output.status || Status::SAT == output.status) { + // wrap_assert( !retcode, "No MIP objective value available." ); + + x.resize(cur_numcols); + output.x = &x[0]; + SCIP_CALL( + SCIPgetSolVals(scip, SCIPgetBestSol(scip), cur_numcols, &scipVars[0], (double*)output.x)); + if (cbui.solcbfn && (!options->flag_all_solutions || !cbui.printed)) { + cbui.solcbfn(output, cbui.psi); + } + } + output.nNodes = SCIPgetNNodes(scip); + output.nOpenNodes = SCIPgetNNodesLeft(scip); // SCIP_getnodeleftcnt (env, lp); + + SCIP_CALL(SCIPfreeTransform(scip)); + + return SCIP_OKAY; +} + +SCIP_RETCODE MIP_scip_wrapper::setObjSense_SCIP(int s) { + SCIP_CALL(SCIPsetObjsense(scip, s > 0 ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE)); + return SCIP_OKAY; +} diff --git a/solvers/MIP/MIP_solverinstance.cpp b/solvers/MIP/MIP_solverinstance.cpp new file mode 100644 index 0000000..5ae2d5a --- /dev/null +++ b/solvers/MIP/MIP_solverinstance.cpp @@ -0,0 +1,197 @@ +// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was ! distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This is the main file for a mzn-cplex solver using a unified + * linearization module && a flexible flattener-to-solver interface + */ + +/// TODO Quadratic terms, even CBC + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include +#include + +std::string MIP_wrapper::getMznLib() { return "-Glinear"; } + +namespace MiniZinc { +namespace SCIPConstraints { + +bool CheckAnnUserCut(const Call* call) { + if (!call->ann().isEmpty()) { + if (call->ann().contains(constants().ann.user_cut)) { + return true; + } + } + return false; +} +bool CheckAnnLazyConstraint(const Call* call) { + if (!call->ann().isEmpty()) { + if (call->ann().contains(constants().ann.lazy_constraint)) { + return true; + } + } + return false; +} +int GetMaskConsType(const Call* call) { + int mask = 0; + const bool fUC = CheckAnnUserCut(call); + const bool fLC = CheckAnnLazyConstraint(call); + if (fUC) { + mask |= MIP_wrapper::MaskConsType_Usercut; + } + if (fLC) { + mask |= MIP_wrapper::MaskConsType_Lazy; + } + if (!fUC && !fLC) mask |= MIP_wrapper::MaskConsType_Normal; + return mask; + // return MIP_wrapper::MaskConsType_Normal; // recognition fails +} +} // namespace SCIPConstraints +} // namespace MiniZinc + +using namespace MiniZinc; + +void XBZCutGen::generate(const MIP_wrapper::Output& slvOut, MIP_wrapper::CutInput& cutsIn) { + assert(pMIP); + const int n = static_cast(varX.size()); + assert(n == varB.size()); + MIP_wrapper::CutDef cut(MIP_wrapper::GQ, MIP_wrapper::MaskConsType_Usercut); + cut.addVar(varZ, -1.0); + for (int i = 0; i < n; ++i) { + const int ix = varX[i]; + const int ib = varB[i]; + assert(ix >= 0 && ix < slvOut.nCols); + assert(ib >= 0 && ib < slvOut.nCols); + const double theXi = slvOut.x[ix]; + const double theBi = slvOut.x[ib]; + const double LBXi = pMIP->colLB[ix]; + const double UBXi = pMIP->colUB[ix]; // tighter bounds from presolve? TODO + bool fi = (theXi + LBXi * (theBi - 1.0) - UBXi * theBi < 0.0); + if (fi) { + cut.addVar(ix, 1.0); + cut.addVar(ib, LBXi); + cut.rhs += LBXi; + } else { + cut.addVar(ib, UBXi); + } + } + double dViol = cut.computeViol(slvOut.x, slvOut.nCols); + if (dViol > 0.01) { // ?? PARAM? TODO + cutsIn.push_back(cut); + cerr << " vi" << dViol << flush; + // cout << cut.rmatind.size() << ' ' + // << cut.rhs << " cutlen, rhs. (Sense fixed to GQ) " << endl; + // for ( int i=0; icolUB[varXij[i * nN + i]] > 0.0) + oss << "SECutGen with " << nN << " cities: diagonal flow " << (i + 1) + << " has UB=" << pMIP->colUB[varXij[i * nN + i]] << "\n"; + return oss.str(); +} + +void SECCutGen::generate(const MIP_wrapper::Output& slvOut, MIP_wrapper::CutInput& cutsIn) { + assert(pMIP); + /// Extract graph, converting to undirected + typedef map, double> TMapFlow; + TMapFlow mapFlow; + for (int i = 0; i < nN; ++i) { + for (int j = 0; j < nN; ++j) { + const double xij = slvOut.x[varXij[nN * i + j]]; + if (i == j) + MZN_ASSERT_HARD_MSG(1e-4 > fabs(xij), + "circuit: X[" << (i + 1) << ", " << (j + 1) << "]==" << xij); + MZN_ASSERT_HARD_MSG( + -1e-4 < xij && 1.0 + 1e-4 > xij, // adjusted from 1e-6 to 1e-4 for CBC. 7.8.19 + "circuit: X[" << (i + 1) << ", " << (j + 1) << "]==" << xij); + if (1e-4 <= xij) { + mapFlow[make_pair(min(i, j), max(i, j))] += xij; + } + } + } + /// Invoking Min Cut + // cerr << " MIN CUT... " << flush; + Algorithms::MinCut mc; + mc.nNodes = nN; + mc.edges.reserve(mapFlow.size()); + mc.weights.reserve(mapFlow.size()); + for (const auto& mf : mapFlow) { + mc.edges.push_back(mf.first); + mc.weights.push_back(mf.second); + } + mc.solve(); + /// Check if violation + if (mc.wMinCut <= 1.999) { + MIP_wrapper::CutDef cut(MIP_wrapper::GQ, + MIP_wrapper::MaskConsType_Lazy | MIP_wrapper::MaskConsType_Usercut); + cut.rhs = 1.0; + int nCutSize = 0; + constexpr int nElemPrint = 20; + // cerr << " CUT: [ "; + for (int i = 0; i < nN; ++i) + if (mc.parities[i]) { + ++nCutSize; + // if ( nCutSize<=nElemPrint ) + // cerr << (i+1) << ", "; + // else if ( nCutSize==nElemPrint+1 ) + // cerr << "..."; + for (int j = 0; j < nN; ++j) + if (!mc.parities[j]) { + cut.addVar(varXij[nN * i + j], 1.0); + } + } + // cerr << "]. " << flush; + double dViol = cut.computeViol(slvOut.x, slvOut.nCols); + if (dViol > 0.0001) { // ?? PARAM? TODO. See also min cut value required + cutsIn.push_back(cut); + /* cerr << " SEC: viol=" << dViol + << " N NODES: " << nN + << " |X|: : " << nCutSize + << flush; */ + } else { + MZN_ASSERT_HARD_MSG(0, " SEC cut: N nodes = " << nN << ": violation = " << dViol + << ": too small compared to the min-cut value " + << (2.0 - mc.wMinCut)); + } + } +} + +void SECCutGen::print(ostream&) {} diff --git a/solvers/MIP/MIP_wrap.cpp b/solvers/MIP/MIP_wrap.cpp new file mode 100644 index 0000000..1731efe --- /dev/null +++ b/solvers/MIP/MIP_wrap.cpp @@ -0,0 +1,25 @@ +// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Gleb Belov + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include diff --git a/solvers/MIP/MIP_xpress_solverfactory.cpp b/solvers/MIP/MIP_xpress_solverfactory.cpp new file mode 100644 index 0000000..ec4faa8 --- /dev/null +++ b/solvers/MIP/MIP_xpress_solverfactory.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +namespace MiniZinc { +namespace { +void getWrapper() { + static MIP_SolverFactory _xpress_solver_factory; + return; +} +} // namespace +Xpress_SolverFactoryInitialiser::Xpress_SolverFactoryInitialiser(void) { getWrapper(); } +} // namespace MiniZinc diff --git a/solvers/MIP/MIP_xpress_wrap.cpp b/solvers/MIP/MIP_xpress_wrap.cpp new file mode 100644 index 0000000..ed933cd --- /dev/null +++ b/solvers/MIP/MIP_xpress_wrap.cpp @@ -0,0 +1,349 @@ +/* + * main authors: + * Karsten Lehmann + */ + +/* this source code form is subject to the terms of the mozilla public + * license, v. 2.0. if a copy of the mpl was not distributed with this + * file, you can obtain one at http://mozilla.org/mpl/2.0/. */ + +#include "minizinc/solvers/MIP/MIP_xpress_wrap.hh" + +#include "minizinc/config.hh" +#include "minizinc/exception.hh" +#include "minizinc/utils.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct UserSolutionCallbackData { + MIP_wrapper::CBUserInfo* info; + XPRBprob* problem; + vector* variables; +}; + +class XpressException : public runtime_error { +public: + XpressException(string msg) : runtime_error(" MIP_xpress_wrapper: " + msg) {} +}; + +string MIP_xpress_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) { + char v[16]; + XPRSgetversion(v); + ostringstream oss; + oss << " MIP wrapper for FICO Xpress Optimiser version " << v; + oss << ". Compiled " __DATE__ " " __TIME__; + return oss.str(); +} + +string MIP_xpress_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) { + char v[16]; + XPRSgetversion(v); + return v; +} + +string MIP_xpress_wrapper::needDllFlag() { return ""; } + +string MIP_xpress_wrapper::getId() { return "xpress"; } + +string MIP_xpress_wrapper::getName() { return "Xpress"; } + +vector MIP_xpress_wrapper::getTags() { return {"mip", "float", "api"}; } + +vector MIP_xpress_wrapper::getStdFlags() { return {"-a", "-n", "-s"}; } + +void MIP_xpress_wrapper::Options::printHelp(ostream& os) { + os << "XPRESS MIP wrapper options:" << std::endl + << "--msgLevel print solver output, default: 0" << std::endl + << "--logFile log file" << std::endl + << "--solver-time-limit stop search after N milliseconds, if negative, it " + "will only stop if at least one solution was found" + << std::endl + << "-n , --numSolutions stop search after N solutions" << std::endl + << "--writeModel write model to " << std::endl + << "--writeModelFormat [lp|mps] the file format of the written model(lp " + "or mps), default: lp" + << std::endl + << "--absGap absolute gap |primal-dual| to stop, default: " << 0 << std::endl + << "--relGap relative gap |primal-dual|/ to stop, " + "default: " + << 0.0001 << std::endl + << "-a, --printAllSolutions print intermediate solution, default: false" << std::endl + << std::endl; +} + +bool MIP_xpress_wrapper::Options::processOption(int& i, std::vector& argv) { + MiniZinc::CLOParser cop(i, argv); + if (cop.get("--msgLevel", &msgLevel)) { + } else if (cop.get("--logFile", &logFile)) { + } else if (cop.get("--solver-time-limit", &timeout)) { + } else if (cop.get("-n --numSolutions", &numSolutions)) { + } else if (cop.get("--writeModel", &writeModelFile)) { + } else if (cop.get("--writeModelFormat", &writeModelFormat)) { + } else if (cop.get("--relGap", &relGap)) { + } else if (cop.get("--absGap", &absGap)) { + } else if (string(argv[i]) == "--printAllSolutions" || string(argv[i]) == "-a") { + printAllSolutions = true; + } else + return false; + return true; +} + +void MIP_xpress_wrapper::setOptions() { + XPRSprob xprsProblem = problem.getXPRSprob(); + + problem.setMsgLevel(options->msgLevel); + + XPRSsetlogfile(xprsProblem, options->logFile.c_str()); + if (options->timeout > 1000 || options->timeout < -1000) { + XPRSsetintcontrol(xprsProblem, XPRS_MAXTIME, static_cast(options->timeout / 1000)); + } + XPRSsetintcontrol(xprsProblem, XPRS_MAXMIPSOL, options->numSolutions); + XPRSsetdblcontrol(xprsProblem, XPRS_MIPABSSTOP, options->absGap); + XPRSsetdblcontrol(xprsProblem, XPRS_MIPRELSTOP, options->relGap); +} + +static MIP_wrapper::Status convertStatus(int xpressStatus) { + switch (xpressStatus) { + case XPRB_MIP_OPTIMAL: + return MIP_wrapper::Status::OPT; + case XPRB_MIP_INFEAS: + return MIP_wrapper::Status::UNSAT; + case XPRB_MIP_UNBOUNDED: + return MIP_wrapper::Status::UNBND; + case XPRB_MIP_NO_SOL_FOUND: + return MIP_wrapper::Status::UNKNOWN; + case XPRB_MIP_NOT_LOADED: + return MIP_wrapper::Status::__ERROR; + default: + return MIP_wrapper::Status::UNKNOWN; + } +} + +static string getStatusName(int xpressStatus) { + string rt = "Xpress stopped with status: "; + switch (xpressStatus) { + case XPRB_MIP_OPTIMAL: + return rt + "Optimal"; + case XPRB_MIP_INFEAS: + return rt + "Infeasible"; + case XPRB_MIP_UNBOUNDED: + return rt + "Unbounded"; + case XPRB_MIP_NO_SOL_FOUND: + return rt + "No solution found"; + case XPRB_MIP_NOT_LOADED: + return rt + "No problem loaded or error"; + default: + return rt + "Unknown status"; + } +} + +static void setOutputVariables(MIP_xpress_wrapper::Output* output, vector* variables) { + size_t nCols = variables->size(); + double* x = (double*)malloc(nCols * sizeof(double)); + for (size_t ii = 0; ii < nCols; ii++) { + x[ii] = (*variables)[ii].getSol(); + } + output->x = x; +} + +static void setOutputAttributes(MIP_xpress_wrapper::Output* output, XPRSprob xprsProblem) { + int xpressStatus = 0; + XPRSgetintattrib(xprsProblem, XPRS_MIPSTATUS, &xpressStatus); + output->status = convertStatus(xpressStatus); + output->statusName = getStatusName(xpressStatus); + + XPRSgetdblattrib(xprsProblem, XPRS_MIPOBJVAL, &output->objVal); + XPRSgetdblattrib(xprsProblem, XPRS_BESTBOUND, &output->bestBound); + + XPRSgetintattrib(xprsProblem, XPRS_NODES, &output->nNodes); + XPRSgetintattrib(xprsProblem, XPRS_ACTIVENODES, &output->nOpenNodes); + + output->dWallTime = + std::chrono::duration(std::chrono::steady_clock::now() - output->dWallTime0).count(); + output->dCPUTime = double(std::clock() - output->cCPUTime0) / CLOCKS_PER_SEC; +} + +static void XPRS_CC userSolNotifyCallback(XPRSprob xprsProblem, void* userData) { + UserSolutionCallbackData* data = (UserSolutionCallbackData*)userData; + MIP_wrapper::CBUserInfo* info = data->info; + + setOutputAttributes(info->pOutput, xprsProblem); + + data->problem->beginCB(xprsProblem); + data->problem->sync(XPRB_XPRS_SOL); + setOutputVariables(info->pOutput, data->variables); + data->problem->endCB(); + + if (info->solcbfn) { + (*info->solcbfn)(*info->pOutput, info->ppp); + } +} + +void MIP_xpress_wrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, + string* names) { + if (obj == nullptr || lb == nullptr || ub == nullptr || vt == nullptr || names == nullptr) { + throw XpressException("invalid input"); + } + for (size_t i = 0; i < n; ++i) { + char* var_name = (char*)names[i].c_str(); + int var_type = convertVariableType(vt[i]); + XPRBvar var = problem.newVar(var_name, var_type, lb[i], ub[i]); + variables.push_back(var); + xpressObj.setTerm(obj[i], var); + } +} + +void MIP_xpress_wrapper::addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, + double rhs, int mask, string rowName) { + addConstraint(nnz, rmatind, rmatval, sense, rhs, mask, rowName); +} + +XPRBctr MIP_xpress_wrapper::addConstraint(int nnz, int* rmatind, double* rmatval, LinConType sense, + double rhs, int mask, string rowName) { + nRows++; + XPRBctr constraint = problem.newCtr(rowName.c_str()); + for (int i = 0; i < nnz; ++i) { + constraint.setTerm(variables[rmatind[i]], rmatval[i]); + } + constraint.setTerm(rhs); + + if (constraint.setType(convertConstraintType(sense)) == 1) { + throw XpressException("error while setting sense of constraint"); + } + + return constraint; +} + +void MIP_xpress_wrapper::writeModelIfRequested() { + int format = XPRB_LP; + if (options->writeModelFormat == "lp") { + format = XPRB_LP; + } else if (options->writeModelFormat == "mps") { + format = XPRB_MPS; + } + if (!options->writeModelFile.empty()) { + problem.exportProb(format, options->writeModelFile.c_str()); + } +} + +void MIP_xpress_wrapper::addDummyConstraint() { + if (getNCols() == 0) { + return; + } + + XPRBctr constraint = problem.newCtr("dummy_constraint"); + constraint.setTerm(variables[0], 1); + constraint.setType(convertConstraintType(LinConType::LQ)); + constraint.setTerm(variables[0].getUB()); +} + +void MIP_xpress_wrapper::solve() { + if (getNRows() == 0) { + addDummyConstraint(); + } + + setOptions(); + writeModelIfRequested(); + setUserSolutionCallback(); + + problem.setObj(xpressObj); + + cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); + cbui.pOutput->cCPUTime0 = output.dCPUTime = std::clock(); + + if (problem.mipOptimize("c") == 1) { + throw XpressException("error while solving"); + } + + setOutputVariables(&output, &variables); + setOutputAttributes(&output, problem.getXPRSprob()); + + if (!options->printAllSolutions && cbui.solcbfn) { + cbui.solcbfn(output, cbui.ppp); + } +} + +void MIP_xpress_wrapper::setUserSolutionCallback() { + if (!options->printAllSolutions) { + return; + } + + UserSolutionCallbackData* data = new UserSolutionCallbackData{&cbui, &problem, &variables}; + + XPRSsetcbintsol(problem.getXPRSprob(), userSolNotifyCallback, data); +} + +void MIP_xpress_wrapper::setObjSense(int s) { problem.setSense(convertObjectiveSense(s)); } + +void MIP_xpress_wrapper::setVarLB(int iVar, double lb) { variables[iVar].setLB(lb); } + +void MIP_xpress_wrapper::setVarUB(int iVar, double ub) { variables[iVar].setUB(ub); } + +void MIP_xpress_wrapper::setVarBounds(int iVar, double lb, double ub) { + setVarLB(iVar, lb); + setVarUB(iVar, ub); +} + +void MIP_xpress_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, + double* rmatval, LinConType sense, double rhs, + string rowName) { + if (bVal != 0 && bVal != 1) { + throw XpressException("indicator bval not in 0/1"); + } + XPRBctr constraint = addConstraint(nnz, rmatind, rmatval, sense, rhs, 0, rowName); + constraint.setIndicator(2 * bVal - 1, variables[iBVar]); +} + +bool MIP_xpress_wrapper::addWarmStart(const std::vector& vars, + const std::vector vals) { + XPRBsol warmstart = problem.newSol(); + for (size_t ii = 0; ii < vars.size(); ii++) { + warmstart.setVar(variables[vars[ii]], vals[ii]); + } + return 1 - problem.addMIPSol(warmstart); +} + +int MIP_xpress_wrapper::convertConstraintType(LinConType sense) { + switch (sense) { + case MIP_wrapper::LQ: + return XPRB_L; + case MIP_wrapper::EQ: + return XPRB_E; + case MIP_wrapper::GQ: + return XPRB_G; + default: + throw XpressException("unkown constraint sense"); + } +} + +int MIP_xpress_wrapper::convertVariableType(VarType varType) { + switch (varType) { + case REAL: + return XPRB_PL; + case INT: + return XPRB_UI; + case BINARY: + return XPRB_BV; + default: + throw XpressException("unknown variable type"); + } +} + +int MIP_xpress_wrapper::convertObjectiveSense(int s) { + switch (s) { + case 1: + return XPRB_MAXIM; + case -1: + return XPRB_MINIM; + default: + throw XpressException("unknown objective sense"); + } +} diff --git a/solvers/fzn/fzn_solverfactory.cpp b/solvers/fzn/fzn_solverfactory.cpp new file mode 100644 index 0000000..339dc28 --- /dev/null +++ b/solvers/fzn/fzn_solverfactory.cpp @@ -0,0 +1,12 @@ +#include +#include + +namespace MiniZinc { +namespace { +void getWrapper() { + static FZN_SolverFactory _fzn_solverfactory; + return; +} +} // namespace +FZN_SolverFactoryInitialiser::FZN_SolverFactoryInitialiser(void) { getWrapper(); } +} // namespace MiniZinc diff --git a/solvers/fzn/fzn_solverinstance.cpp b/solvers/fzn/fzn_solverinstance.cpp new file mode 100644 index 0000000..bcbfa0d --- /dev/null +++ b/solvers/fzn/fzn_solverinstance.cpp @@ -0,0 +1,621 @@ +/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + * Main authors: + * Guido Tack + */ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#ifdef _WIN32 +#define NOMINMAX // Need this before all (implicit) include's of Windows.h +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef _WIN32 +#undef ERROR +#endif + +using namespace std; + +namespace MiniZinc { + +FZN_SolverFactory::FZN_SolverFactory(void) { + SolverConfig sc("org.minizinc.mzn-fzn", + MZN_VERSION_MAJOR "." MZN_VERSION_MINOR "." MZN_VERSION_PATCH); + sc.name("Generic FlatZinc driver"); + sc.mznlibVersion(1); + sc.description("MiniZinc generic FlatZinc solver plugin"); + sc.requiredFlags({"--fzn-cmd"}); + sc.stdFlags({"-a", "-n", "-f", "-p", "-s", "-r", "-v"}); + sc.tags({"__internal__"}); + SolverConfigs::registerBuiltinSolver(sc); +} + +string FZN_SolverFactory::getDescription(SolverInstanceBase::Options*) { + string v = "FZN solver plugin, compiled " __DATE__ " " __TIME__; + return v; +} + +string FZN_SolverFactory::getVersion(SolverInstanceBase::Options*) { return MZN_VERSION_MAJOR; } + +string FZN_SolverFactory::getId() { return "org.minizinc.mzn-fzn"; } + +void FZN_SolverFactory::printHelp(ostream& os) { + os << "MZN-FZN plugin options:" << std::endl + << " --fzn-cmd , --flatzinc-cmd \n the backend solver filename.\n" + << " -b, --backend, --solver-backend \n the backend codename. Currently passed to " + "the solver.\n" + << " --fzn-flags , --flatzinc-flags \n Specify option to be passed to " + "the FlatZinc interpreter.\n" + << " --fzn-flag