mirror of
https://gitlab.kit.edu/uskyk/kv.git
synced 2024-11-22 00:45:03 +00:00
WASM port + misc. changes
This commit is contained in:
parent
24139814cc
commit
4b0541c67a
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/dist
|
||||||
|
166
Cargo.lock
generated
166
Cargo.lock
generated
@ -1,5 +1,43 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_error_panic_hook"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console_log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@ -15,14 +53,142 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc9f84f9b115ce7843d60706df1422a916680bfdfcbdb0447c5614ff9d7e4d78"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kv"
|
name = "kv"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"console_error_panic_hook",
|
||||||
|
"console_log",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"log",
|
||||||
"svg_fmt",
|
"svg_fmt",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "svg_fmt"
|
name = "svg_fmt"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.64"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee1280240b7c461d6a0071313e08f34a60b0365f14260362e5a2b17d1d31aa7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5ac38da8ef716661f0f36c0d8320b89028efe10c7c0afde65baffb496ce0d3b"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc053ec74d454df287b9374ee8abb36ffd5acb95ba87da3ba5b7d3fe20eb401e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d6f8ec44822dd71f5f221a5847fb34acd9060535c1211b70a05844c0f6383b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec600b26223b2948cedfde2a0aa6756dcf1fef616f43d7b3097aaf53a6c4d92b"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
@ -7,5 +7,10 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
console_error_panic_hook = "0.1.6"
|
||||||
|
console_log = "0.2.0"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
|
log = "0.4.14"
|
||||||
svg_fmt = { path = "../rust_debug/svg_fmt" }
|
svg_fmt = { path = "../rust_debug/svg_fmt" }
|
||||||
|
wasm-bindgen = "0.2.71"
|
||||||
|
web-sys = { version = "0.3.48", features = ["Window", "Document", "Location", "HtmlCollection", "HtmlElement", "CssStyleDeclaration", "HtmlTextAreaElement", "HtmlInputElement"] }
|
||||||
|
23
base.js
Normal file
23
base.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
function handleTextChange(event) {
|
||||||
|
const target = event.target;
|
||||||
|
const value = target.value;
|
||||||
|
if (value.length == 0) {
|
||||||
|
target.value = "-";
|
||||||
|
} else {
|
||||||
|
target.value = value.substr(value.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleTextScroll(event) {
|
||||||
|
const target = event.target;
|
||||||
|
const value = target.value;
|
||||||
|
if (event.deltaY < 0) {
|
||||||
|
target.value = "1"; // scroll up
|
||||||
|
} else {
|
||||||
|
target.value = "0"; // scroll down
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
export function attachTypeListener(element) {
|
||||||
|
element.addEventListener("input", handleTextChange);
|
||||||
|
element.addEventListener("wheel", handleTextScroll);
|
||||||
|
}
|
49
index.html
Normal file
49
index.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
|
||||||
|
<title>KV diagram calculator</title>
|
||||||
|
<link data-trunk rel="rust" data-bin="svg-wasm"/>
|
||||||
|
<style>
|
||||||
|
textarea {
|
||||||
|
resize: none;
|
||||||
|
font-size: 32px;
|
||||||
|
line-height: 64px;
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
padding-left: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
#input-container {
|
||||||
|
transform: translate(0px, 0px); /* new absolute context */
|
||||||
|
padding-bottom: 1em;
|
||||||
|
}
|
||||||
|
.solution {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.solution span {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="main">
|
||||||
|
<div id="settings">
|
||||||
|
<label>Number of variables: <input type="number" value="3" id="variables"></label><button id="apply-variables">Apply</button>
|
||||||
|
</div>
|
||||||
|
<p id="hints">
|
||||||
|
Change cells by scrolling (up => 1, down => 0), typing (=> 1, 0) or deleting (=> -).
|
||||||
|
</p>
|
||||||
|
<p id="examples">
|
||||||
|
Load <a href="#--111100">example 1</a>, <a href="#-1111000-1-01---">example 2</a>, <a href="#11101111111111111111101111111101">example 3</a>.
|
||||||
|
</p>
|
||||||
|
<div id="input-container"></div>
|
||||||
|
<button id="calculate">Run</button>
|
||||||
|
<div id="output-help"></div>
|
||||||
|
<div id="output-container"></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
243
src/bin/svg-wasm.rs
Normal file
243
src/bin/svg-wasm.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
use log::{Level, info};
|
||||||
|
|
||||||
|
use kv::*;
|
||||||
|
use web_sys::{EventTarget, HtmlElement, HtmlInputElement, HtmlTextAreaElement};
|
||||||
|
|
||||||
|
macro_rules! web {
|
||||||
|
($($x:ident)*) => {
|
||||||
|
web_sys::window().unwrap()$(.$x().unwrap())*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! web2 {
|
||||||
|
($($x:ident)*) => {
|
||||||
|
web_sys::window().unwrap()$(.$x())*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const HINT_TEXT: &'static str = "Output: core blocks, prime blocks, solution 1, solution 2, ... (solutions only displayed if not equal to core or prime blocks)";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
console_log::init_with_level(Level::Debug).unwrap();
|
||||||
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
|
info!("init");
|
||||||
|
init_settings();
|
||||||
|
if !parse_hash() {
|
||||||
|
let grid = grid(get_var_number());
|
||||||
|
update_input_grid(&grid);
|
||||||
|
}
|
||||||
|
let closure = Closure::wrap(Box::new(|| { parse_hash(); }) as Box<dyn FnMut()>);
|
||||||
|
web!().add_event_listener_with_callback("hashchange", closure.as_ref().unchecked_ref()).unwrap();
|
||||||
|
closure.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_hash() -> bool {
|
||||||
|
let hash = web!(document location hash);
|
||||||
|
if hash.len() >= 2 {
|
||||||
|
let func_spec = &hash[1..];
|
||||||
|
let var_count = (func_spec.len() as f32).log2() as usize;
|
||||||
|
if 2usize.pow(var_count as u32) == func_spec.len() {
|
||||||
|
set_var_number(var_count);
|
||||||
|
let grid = grid(var_count);
|
||||||
|
update_input_grid(&grid);
|
||||||
|
set_input_function(func_spec);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(module = "/base.js")]
|
||||||
|
extern "C" {
|
||||||
|
pub fn attachTypeListener(element: &HtmlElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_output_container() -> HtmlElement {
|
||||||
|
web!(document).get_element_by_id("output-container").unwrap().unchecked_into::<HtmlElement>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_output_help() -> HtmlElement {
|
||||||
|
web!(document).get_element_by_id("output-help").unwrap().unchecked_into::<HtmlElement>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p(text: &str) -> HtmlElement {
|
||||||
|
let p = web!(document).create_element("p").unwrap().unchecked_into::<HtmlElement>();
|
||||||
|
p.set_inner_text(text);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span(text: &str) -> HtmlElement {
|
||||||
|
let p = web!(document).create_element("span").unwrap().unchecked_into::<HtmlElement>();
|
||||||
|
p.set_inner_text(text);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run() {
|
||||||
|
let document = web!(document);
|
||||||
|
|
||||||
|
let var_count = get_var_number();
|
||||||
|
let vars = (0..var_count).map(|x| 2usize.pow(x as u32)).collect::<Vec<_>>();
|
||||||
|
let function: FunctionSpec = (0..2usize.pow(var_count as u32)).map(|idx| get_input(idx)).collect();
|
||||||
|
info!("function: {:?}", function);
|
||||||
|
let hash = Some('#').into_iter().chain(function.iter().map::<char, _>(|&x| x.into())).collect::<String>();
|
||||||
|
web2!(location).set_hash(&hash).unwrap();
|
||||||
|
let groups = find_groups(&function, &vars, One);
|
||||||
|
let (prime, other) = find_core(&function, &vars, One, &groups);
|
||||||
|
let solutions = real_solutions_idxed(function.clone(), &vars, One, &prime, &other);
|
||||||
|
|
||||||
|
let output_container = get_output_container();
|
||||||
|
if output_container.children().length() == 0 {
|
||||||
|
let output_help = get_output_help();
|
||||||
|
output_help.append_child(&p(HINT_TEXT)).unwrap();
|
||||||
|
output_container.append_child(&document.create_element("hr").unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
let output_sub = document.create_element("div").unwrap().unchecked_into::<HtmlElement>();
|
||||||
|
|
||||||
|
let (_grid, w, h) = grid(var_count);
|
||||||
|
|
||||||
|
macro_rules! render_svg {
|
||||||
|
($sol:expr, $mask:expr) => {
|
||||||
|
let svg = svg::get_svg(if var_count < 5 { 64 } else { 80 }, &function, &vars, $mask);
|
||||||
|
let svg_container = document.create_element("div").unwrap();
|
||||||
|
svg_container.set_class_name("solution");
|
||||||
|
svg_container.set_inner_html(&svg);
|
||||||
|
let svg = svg_container.children().item(0).unwrap().unchecked_into::<HtmlElement>();
|
||||||
|
svg.style().set_property("width", &format!("{}px", w * 80)).unwrap();
|
||||||
|
svg.style().set_property("height", &format!("{}px", h * 80)).unwrap();
|
||||||
|
let mut text = String::new();
|
||||||
|
for x in $sol {
|
||||||
|
if !text.is_empty() {
|
||||||
|
text += " ∨ ";
|
||||||
|
}
|
||||||
|
text += &print_implicant(x);
|
||||||
|
}
|
||||||
|
if text.is_empty() {
|
||||||
|
text += ".";
|
||||||
|
}
|
||||||
|
svg_container.append_child(&span(&text)).unwrap();
|
||||||
|
output_sub.append_child(&svg_container).unwrap();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut block_masks: Vec<_> = prime.iter()
|
||||||
|
.map(block_to_mask)
|
||||||
|
.collect();
|
||||||
|
render_svg!(&prime, &block_masks);
|
||||||
|
|
||||||
|
block_masks = prime.iter()
|
||||||
|
.map(block_to_mask)
|
||||||
|
.chain(other.iter().map(block_to_mask)) // also display non-core
|
||||||
|
.collect();
|
||||||
|
render_svg!(prime.iter().chain(other.iter()), &block_masks);
|
||||||
|
|
||||||
|
let mut size = usize::MAX;
|
||||||
|
for sol in &solutions {
|
||||||
|
if sol.len() > size {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size = sol.len();
|
||||||
|
block_masks = prime.iter()
|
||||||
|
.map(block_to_mask)
|
||||||
|
.chain((0..other.len()).map(|idx| {
|
||||||
|
if sol.contains(&idx) {
|
||||||
|
block_to_mask(&other[idx])
|
||||||
|
} else {
|
||||||
|
(!0, 0)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.collect();
|
||||||
|
let blocks = prime.iter()
|
||||||
|
.chain(sol.iter().map(|&i| &other[i]));
|
||||||
|
render_svg!(blocks, &block_masks);
|
||||||
|
}
|
||||||
|
|
||||||
|
output_container.prepend_with_node_1(&output_sub).unwrap();
|
||||||
|
output_container.prepend_with_node_1(&document.create_element("hr").unwrap()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_var_number(x: usize) {
|
||||||
|
get_var_number_el().set_value(&x.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_settings() {
|
||||||
|
let calculate = web!(document).get_element_by_id("calculate").unwrap().unchecked_into::<EventTarget>();
|
||||||
|
let closure = Closure::wrap(Box::new(|| run()) as Box<dyn FnMut()>);
|
||||||
|
calculate.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).unwrap();
|
||||||
|
closure.forget();
|
||||||
|
let apply_count = web!(document).get_element_by_id("apply-variables").unwrap().unchecked_into::<EventTarget>();
|
||||||
|
let closure = Closure::wrap(Box::new(|| update_input_grid(&grid(get_var_number()))) as Box<dyn FnMut()>);
|
||||||
|
apply_count.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()).unwrap();
|
||||||
|
closure.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var_number_el() -> HtmlInputElement {
|
||||||
|
web!(document).get_element_by_id("variables").unwrap().unchecked_into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var_number() -> usize {
|
||||||
|
get_var_number_el().value().parse().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_input_container() -> HtmlElement {
|
||||||
|
web!(document).get_element_by_id("input-container").unwrap().unchecked_into::<HtmlElement>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_input_el(idx: usize) -> Option<HtmlElement> {
|
||||||
|
web!(document).get_element_by_id(&format!("input{}", idx)).map(|x| x.unchecked_into::<HtmlElement>())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_input_el(idx: usize, x: usize, y: usize) -> HtmlElement {
|
||||||
|
let input = web!(document).create_element("textarea").unwrap().unchecked_into::<HtmlTextAreaElement>();
|
||||||
|
input.set_attribute("id", &format!("input{}", idx)).unwrap();
|
||||||
|
input.set_value("-");
|
||||||
|
let style = input.style();
|
||||||
|
style.set_property("position", "absolute").unwrap();
|
||||||
|
style.set_property("left", &format!("{}px", x)).unwrap();
|
||||||
|
style.set_property("top", &format!("{}px", y)).unwrap();
|
||||||
|
get_input_container().append_child(&input).unwrap();
|
||||||
|
attachTypeListener(&input);
|
||||||
|
input.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_input(idx: usize) -> Output {
|
||||||
|
let el = get_input_el(idx).unwrap().unchecked_into::<HtmlTextAreaElement>();
|
||||||
|
match &*el.value() {
|
||||||
|
"0" => Zero,
|
||||||
|
"1" => One,
|
||||||
|
_ => Any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_input(idx: usize, out: Output) {
|
||||||
|
let el = get_input_el(idx).unwrap().unchecked_into::<HtmlTextAreaElement>();
|
||||||
|
el.set_value(&out.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_input_function(func: &str) {
|
||||||
|
for i in 0..func.len() {
|
||||||
|
set_input(i, func[i..i+1].parse().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// also set in style
|
||||||
|
const GRID_SPACING: usize = 64;
|
||||||
|
|
||||||
|
fn update_input_grid((grid, w, h): &(Vec<(usize, usize)>, usize, usize)) {
|
||||||
|
let input_container = get_input_container();
|
||||||
|
input_container.style().set_property("width", &format!("{}px", w * 64)).unwrap();
|
||||||
|
input_container.style().set_property("height", &format!("{}px", h * 64)).unwrap();
|
||||||
|
for i in 0..grid.len() {
|
||||||
|
if get_input_el(i).is_none() {
|
||||||
|
create_input_el(i, grid[i].0 * GRID_SPACING, grid[i].1 * GRID_SPACING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in grid.len().. {
|
||||||
|
if let Some(el) = get_input_el(i) {
|
||||||
|
el.remove();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
src/bin/svg.rs
109
src/bin/svg.rs
@ -1,32 +1,23 @@
|
|||||||
use std::env::args;
|
use std::env::args;
|
||||||
|
|
||||||
use svg_fmt::*;
|
|
||||||
|
|
||||||
use kv::*;
|
use kv::*;
|
||||||
|
|
||||||
const SIZE_FACTOR: usize = 64;
|
const SIZE_FACTOR: usize = 64;
|
||||||
const SIZE_FONT: f32 = 20.0;
|
|
||||||
const SIZE4: usize = SIZE_FACTOR / 4;
|
|
||||||
|
|
||||||
const COLORS: &'static [Color] = &[
|
|
||||||
rgb(0, 0, 255),
|
|
||||||
rgb(255, 0, 0),
|
|
||||||
rgb(0, 255, 0),
|
|
||||||
rgb(255, 255, 0),
|
|
||||||
];
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = args().collect::<Vec<_>>();
|
let args = args().collect::<Vec<_>>();
|
||||||
|
|
||||||
let vars = [A, B, C];
|
let vars = [A, B, C, D];
|
||||||
let function = vec![0, 0, 1, 1, 1, 1, 1, 0].into_iter().map(Into::into).collect();
|
//let function = vec![0, 0, 1, 1, 1, 1, 1, 0].into_iter().map(Into::into).collect();
|
||||||
|
//let function = vec![0, 0, 1, 1, 1, 1, 1, 2].into_iter().map(Into::into).collect();
|
||||||
|
let function = parse_function("-1111000-1-01---");
|
||||||
let groups = find_groups(&function, &vars, One);
|
let groups = find_groups(&function, &vars, One);
|
||||||
eprintln!("all:");
|
eprintln!("all:");
|
||||||
for x in &groups {
|
for x in &groups {
|
||||||
eprintln!("{}", print_implicant(x));
|
eprintln!("{}", print_implicant(x));
|
||||||
}
|
}
|
||||||
eprintln!("prime:");
|
eprintln!("prime:");
|
||||||
let (prime, other) = find_prime(&function, &vars, One, &groups);
|
let (prime, other) = find_core(&function, &vars, One, &groups);
|
||||||
for x in &prime {
|
for x in &prime {
|
||||||
eprintln!("{}", print_implicant(x));
|
eprintln!("{}", print_implicant(x));
|
||||||
}
|
}
|
||||||
@ -54,93 +45,5 @@ fn main() {
|
|||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
let (grid, w, h) = grid(3);
|
println!("{}", svg::get_svg(SIZE_FACTOR, &function, &vars, &block_masks));
|
||||||
println!("{}", BeginSvg { w: w * SIZE_FACTOR + 3, h: h * SIZE_FACTOR + 3 });
|
|
||||||
for (i, &(x, y)) in grid.iter().enumerate() {
|
|
||||||
println!("{}",
|
|
||||||
rectangle(1 + x * SIZE_FACTOR, 1 + y * SIZE_FACTOR, SIZE_FACTOR, SIZE_FACTOR)
|
|
||||||
.fill(white())
|
|
||||||
.stroke(Stroke::Color(black(), 2.0))
|
|
||||||
);
|
|
||||||
let mut rect = rectangle(1 + x * SIZE_FACTOR, 1 + y * SIZE_FACTOR, SIZE_FACTOR, SIZE_FACTOR)
|
|
||||||
.fill(white())
|
|
||||||
.stroke_opacity(0.7)
|
|
||||||
.inflate(-1, -1);
|
|
||||||
let mut ident = 1;
|
|
||||||
for (idx, &(mask, inv_mask)) in block_masks.iter().enumerate() {
|
|
||||||
rect = rect.inflate(-2, -2);
|
|
||||||
ident += 2;
|
|
||||||
if check_mask(i, mask, inv_mask) {
|
|
||||||
// check cells around this one
|
|
||||||
let mut up = false;
|
|
||||||
let mut down = false;
|
|
||||||
let mut left = false;
|
|
||||||
let mut right = false;
|
|
||||||
for &var in &vars {
|
|
||||||
let j = i ^ var;
|
|
||||||
if check_mask(j, mask, inv_mask) {
|
|
||||||
let (x2, y2) = grid[j];
|
|
||||||
if (x + 1) % w == x2 && y == y2 {
|
|
||||||
right = true;
|
|
||||||
}
|
|
||||||
if x == x2 && (y + 1) % h == y2 {
|
|
||||||
down = true;
|
|
||||||
}
|
|
||||||
if x == x2 && y == (y2 + 1) % h {
|
|
||||||
up = true;
|
|
||||||
}
|
|
||||||
if x == (x2 + 1) % w && y == y2 {
|
|
||||||
left = true;
|
|
||||||
}
|
|
||||||
if !up && !down && !left && !right {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut sides = rect.sides();
|
|
||||||
for s in &mut sides {
|
|
||||||
*s = s.opacity(0.9);
|
|
||||||
}
|
|
||||||
if up {
|
|
||||||
println!("{}", sides[0].width(2.0).color(white()));
|
|
||||||
println!("{}", sides[2].width(2.0).color(COLORS[idx]).offset(0.0, -ident - 1));
|
|
||||||
println!("{}", sides[3].width(2.0).color(COLORS[idx]).offset(0.0, -ident - 1));
|
|
||||||
} else {
|
|
||||||
println!("{}", sides[0].width(2.0).color(COLORS[idx]));
|
|
||||||
}
|
|
||||||
if down {
|
|
||||||
println!("{}", sides[1].width(2.0).color(white()));
|
|
||||||
println!("{}", sides[2].width(2.0).color(COLORS[idx]).offset(0.0, ident + 1));
|
|
||||||
println!("{}", sides[3].width(2.0).color(COLORS[idx]).offset(0.0, ident + 1));
|
|
||||||
} else {
|
|
||||||
println!("{}", sides[1].width(2.0).color(COLORS[idx]));
|
|
||||||
}
|
|
||||||
if left {
|
|
||||||
println!("{}", sides[2].width(2.0).color(white()));
|
|
||||||
println!("{}", sides[0].width(2.0).color(COLORS[idx]).offset(-ident - 1, 0));
|
|
||||||
println!("{}", sides[1].width(2.0).color(COLORS[idx]).offset(-ident - 1, 0));
|
|
||||||
} else {
|
|
||||||
println!("{}", sides[2].width(2.0).color(COLORS[idx]));
|
|
||||||
}
|
|
||||||
if right {
|
|
||||||
println!("{}", sides[3].width(1.0).color(white()));
|
|
||||||
println!("{}", sides[0].width(2.0).color(COLORS[idx]).offset(ident + 1, 0));
|
|
||||||
println!("{}", sides[1].width(2.0).color(COLORS[idx]).offset(ident + 1, 0));
|
|
||||||
} else {
|
|
||||||
println!("{}", sides[3].width(2.0).color(COLORS[idx]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("{}",
|
|
||||||
text((x+1) * SIZE_FACTOR - SIZE4, (y+1) * SIZE_FACTOR - SIZE4 / 3, i.to_string())
|
|
||||||
.size(SIZE_FONT)
|
|
||||||
.color(black())
|
|
||||||
);
|
|
||||||
println!("{}",
|
|
||||||
text(x * SIZE_FACTOR + SIZE4, (y+1) * SIZE_FACTOR - SIZE4, function[i].to_string())
|
|
||||||
.size(SIZE_FONT * 2.0)
|
|
||||||
.color(black())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!("{}", EndSvg);
|
|
||||||
}
|
}
|
||||||
|
132
src/lib.rs
132
src/lib.rs
@ -1,8 +1,9 @@
|
|||||||
use std::fmt;
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
use log::{debug, trace, warn};
|
||||||
pub use ui::*;
|
pub use ui::*;
|
||||||
|
|
||||||
pub type Variable = usize;
|
pub type Variable = usize;
|
||||||
@ -12,6 +13,8 @@ pub type BlockRef<'a> = &'a [(Variable, Output)];
|
|||||||
pub const A: Variable = 1 << 0;
|
pub const A: Variable = 1 << 0;
|
||||||
pub const B: Variable = 1 << 1;
|
pub const B: Variable = 1 << 1;
|
||||||
pub const C: Variable = 1 << 2;
|
pub const C: Variable = 1 << 2;
|
||||||
|
pub const D: Variable = 1 << 3;
|
||||||
|
pub const E: Variable = 1 << 4;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum Output {
|
pub enum Output {
|
||||||
@ -45,11 +48,33 @@ impl From<usize> for Output {
|
|||||||
match x {
|
match x {
|
||||||
0 => Zero,
|
0 => Zero,
|
||||||
1 => One,
|
1 => One,
|
||||||
_ => todo!()
|
_ => Any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<char> for Output {
|
||||||
|
fn from(x: char) -> Self {
|
||||||
|
match x {
|
||||||
|
'0' => Zero,
|
||||||
|
'1' => One,
|
||||||
|
_ => Any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Output {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(match s {
|
||||||
|
"0" => Zero,
|
||||||
|
"1" => One,
|
||||||
|
_ => Any
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for Output {
|
impl fmt::Display for Output {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
@ -60,10 +85,29 @@ impl fmt::Display for Output {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Into<char> for Output {
|
||||||
|
fn into(self) -> char {
|
||||||
|
match self {
|
||||||
|
Zero => '0',
|
||||||
|
One => '1',
|
||||||
|
Any => '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_function(func: &str) -> FunctionSpec {
|
||||||
|
func.chars().map(|x| x.into()).collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub type FunctionSpec = Vec<Output>;
|
pub type FunctionSpec = Vec<Output>;
|
||||||
|
|
||||||
pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<Vec<(Variable, Output)>> {
|
pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<Vec<(Variable, Output)>> {
|
||||||
let mut groups: Vec<Vec<(Variable, Output)>> = Vec::new();
|
let mut groups: Vec<Vec<(Variable, Output)>> = Vec::new();
|
||||||
|
// k = 0
|
||||||
|
if func.iter().all(|&x| x != typ.invert()) {
|
||||||
|
groups.push(vec![]);
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
for k in 1..vars.len()+1 {
|
for k in 1..vars.len()+1 {
|
||||||
for vars in vars.iter().combinations(k) {
|
for vars in vars.iter().combinations(k) {
|
||||||
for var_bits in 0..(1 << k) {
|
for var_bits in 0..(1 << k) {
|
||||||
@ -82,15 +126,18 @@ pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<V
|
|||||||
inv_mask ^= if ((var_bits >> i) & 1) == 1 { 0 } else { var };
|
inv_mask ^= if ((var_bits >> i) & 1) == 1 { 0 } else { var };
|
||||||
}
|
}
|
||||||
let mut fits = true;
|
let mut fits = true;
|
||||||
|
let mut found_typ = false;
|
||||||
for input in 0..func.len() {
|
for input in 0..func.len() {
|
||||||
if check_mask(input, mask, inv_mask) {
|
if check_mask(input, mask, inv_mask) {
|
||||||
if func[input] == typ.invert() {
|
if func[input] == typ.invert() {
|
||||||
fits = false;
|
fits = false;
|
||||||
break;
|
break;
|
||||||
|
} else if func[input] == typ {
|
||||||
|
found_typ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fits {
|
if fits && found_typ {
|
||||||
groups.push(var_setting);
|
groups.push(var_setting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +146,7 @@ pub fn find_groups(func: &FunctionSpec, vars: &[Variable], typ: Output) -> Vec<V
|
|||||||
groups
|
groups
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_prime<'a>(func: &FunctionSpec, vars: &[Variable], typ: Output, blocks: &'a [Block]) -> (Vec<&'a [(Variable, Output)]>, Vec<&'a [(Variable, Output)]>) {
|
pub fn find_core<'a>(func: &FunctionSpec, vars: &[Variable], typ: Output, blocks: &'a [Block]) -> (Vec<&'a [(Variable, Output)]>, Vec<&'a [(Variable, Output)]>) {
|
||||||
assert_eq!(func.len(), 2usize.pow(vars.len() as u32));
|
assert_eq!(func.len(), 2usize.pow(vars.len() as u32));
|
||||||
|
|
||||||
let block_masks = blocks.iter()
|
let block_masks = blocks.iter()
|
||||||
@ -130,11 +177,39 @@ pub fn find_prime<'a>(func: &FunctionSpec, vars: &[Variable], typ: Output, block
|
|||||||
(a, b)
|
(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_solutions(mut func: FunctionSpec, vars: &[Variable], typ: Output, prime: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<Block>> {
|
/// May return some incomplete solutions
|
||||||
|
pub fn all_solutions(func: FunctionSpec, vars: &[Variable], typ: Output, core: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<Block>> {
|
||||||
|
all_solutions_idxed(func, vars, typ, core, other)
|
||||||
|
.into_iter()
|
||||||
|
.map(|sol| sol.into_iter().map(|i| other[i].to_vec()).collect())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn real_solutions_idxed(func: FunctionSpec, vars: &[Variable], typ: Output, core: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<usize>> {
|
||||||
|
let core_masks = core.iter()
|
||||||
|
.map(block_to_mask)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let other_masks = other.iter()
|
||||||
|
.map(block_to_mask)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut solutions = all_solutions_idxed(func.clone(), vars, typ, core, other);
|
||||||
|
solutions.sort_unstable_by_key(|x| x.len());
|
||||||
|
solutions.into_iter()
|
||||||
|
.filter(|sol|
|
||||||
|
(0..func.len())
|
||||||
|
.filter(|&i| func[i] == typ)
|
||||||
|
.all(|i|
|
||||||
|
core_masks.iter()
|
||||||
|
.chain(sol.iter().map(|&x| &other_masks[x]))
|
||||||
|
.any(|&(mask, inv_mask)| check_mask(i, mask, inv_mask)))
|
||||||
|
).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_solutions_idxed(mut func: FunctionSpec, vars: &[Variable], typ: Output, core: &[BlockRef], other: &[BlockRef]) -> Vec<Vec<usize>> {
|
||||||
assert_eq!(func.len(), 2usize.pow(vars.len() as u32));
|
assert_eq!(func.len(), 2usize.pow(vars.len() as u32));
|
||||||
|
|
||||||
// first mark all inputs covered by prime blocks as Any
|
// first mark all inputs covered by core blocks as Any
|
||||||
let prime_masks = prime.iter()
|
let prime_masks = core.iter()
|
||||||
.map(block_to_mask)
|
.map(block_to_mask)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
'input: for input in 0..func.len() {
|
'input: for input in 0..func.len() {
|
||||||
@ -152,38 +227,46 @@ pub fn all_solutions(mut func: FunctionSpec, vars: &[Variable], typ: Output, pri
|
|||||||
let other_masks = other.iter()
|
let other_masks = other.iter()
|
||||||
.map(block_to_mask)
|
.map(block_to_mask)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
all_recursive(func, vars, typ, other, &other_masks)
|
all_recursive(func, vars, typ, other, &other_masks, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_recursive(func: FunctionSpec, vars: &[Variable], typ: Output, other: &[BlockRef], other_masks: &[(Variable, Variable)]) -> Vec<Vec<Block>> {
|
fn all_recursive(func: FunctionSpec, vars: &[Variable], typ: Output, other: &[BlockRef], other_masks: &[(Variable, Variable)], start: usize) -> Vec<Vec<usize>> {
|
||||||
let mut all = Vec::new();
|
let mut all = Vec::new();
|
||||||
for i in 0..other.len() {
|
'block: for i in start..other.len() {
|
||||||
let block = &other[i];
|
trace!("trying {}", i);
|
||||||
let (mask, inv_mask) = other_masks[i];
|
let (mask, inv_mask) = other_masks[i];
|
||||||
for input in 0..func.len() {
|
for input in 0..func.len() {
|
||||||
if func[input] != typ {
|
if func[input] != typ {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if check_mask(input, mask, inv_mask) {
|
if check_mask(input, mask, inv_mask) {
|
||||||
|
trace!("success by idx {}", input);
|
||||||
// this block is useful
|
// this block is useful
|
||||||
// mark inputs as done
|
// mark inputs as done
|
||||||
let mut func = func.clone();
|
let mut func = func.clone();
|
||||||
for input in 0..func.len() {
|
for input in 0..func.len() {
|
||||||
if check_mask(input, mask, inv_mask) {
|
if check_mask(input, mask, inv_mask) {
|
||||||
|
trace!("set {} to any", input);
|
||||||
func[input] = Any;
|
func[input] = Any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let extensions = all_recursive(func, vars, typ, other, other_masks);
|
let extensions = all_recursive(func, vars, typ, other, other_masks, i+1);
|
||||||
if extensions.is_empty() {
|
if extensions.is_empty() {
|
||||||
all.push(vec![block.to_vec()]);
|
all.push(vec![i]);
|
||||||
}
|
}
|
||||||
for mut extension in extensions {
|
for mut extension in extensions {
|
||||||
extension.push(block.to_vec());
|
if !extension.is_empty() {
|
||||||
|
extension.push(i);
|
||||||
all.push(extension);
|
all.push(extension);
|
||||||
|
} else {
|
||||||
|
warn!("empty extension?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue 'block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
trace!("returning");
|
||||||
all
|
all
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,10 +294,10 @@ pub fn print_implicate(vars: &[(Variable, Output)]) -> String {
|
|||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for i in 0..vars.len() {
|
for i in 0..vars.len() {
|
||||||
let (var, out) = vars[i];
|
let (var, out) = vars[i];
|
||||||
|
s.push(print_var(var));
|
||||||
if out == One {
|
if out == One {
|
||||||
s += "~";
|
s += "̅";
|
||||||
}
|
}
|
||||||
s += print_var(var);
|
|
||||||
if i != vars.len() - 1 {
|
if i != vars.len() - 1 {
|
||||||
s += " v ";
|
s += " v ";
|
||||||
}
|
}
|
||||||
@ -226,10 +309,10 @@ pub fn print_implicant(vars: &[(Variable, Output)]) -> String {
|
|||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for i in 0..vars.len() {
|
for i in 0..vars.len() {
|
||||||
let (var, out) = vars[i];
|
let (var, out) = vars[i];
|
||||||
|
s.push(print_var(var));
|
||||||
if out == Zero {
|
if out == Zero {
|
||||||
s += "~";
|
s += "̅";
|
||||||
}
|
}
|
||||||
s += print_var(var);
|
|
||||||
if i != vars.len() - 1 {
|
if i != vars.len() - 1 {
|
||||||
s += " ";
|
s += " ";
|
||||||
}
|
}
|
||||||
@ -237,13 +320,8 @@ pub fn print_implicant(vars: &[(Variable, Output)]) -> String {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_var(var: Variable) -> &'static str {
|
fn print_var(var: Variable) -> char {
|
||||||
match var {
|
('a' as u8 + (0usize.leading_zeros() - var.leading_zeros() - 1) as u8) as char
|
||||||
A => "a",
|
|
||||||
B => "b",
|
|
||||||
C => "c",
|
|
||||||
_ => "?"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -255,7 +333,7 @@ fn test_3var() {
|
|||||||
vec![(A, One), (C, Zero)],
|
vec![(A, One), (C, Zero)],
|
||||||
vec![(B, Zero), (C, Zero)],
|
vec![(B, Zero), (C, Zero)],
|
||||||
]);
|
]);
|
||||||
let prime = find_prime(&fun, &mut [A, B, C], Zero, &groups).0;
|
let prime = find_core(&fun, &mut [A, B, C], Zero, &groups).0;
|
||||||
assert_eq!(prime, vec![
|
assert_eq!(prime, vec![
|
||||||
vec![(A, Zero), (B, Zero)],
|
vec![(A, Zero), (B, Zero)],
|
||||||
vec![(A, One), (C, Zero)],
|
vec![(A, One), (C, Zero)],
|
||||||
|
@ -8,7 +8,7 @@ fn main() {
|
|||||||
println!("{}", print_implicant(x));
|
println!("{}", print_implicant(x));
|
||||||
}
|
}
|
||||||
println!("prime:");
|
println!("prime:");
|
||||||
let (prime, other) = find_prime(&function, &[A, B, C], One, &groups);
|
let (prime, other) = find_core(&function, &[A, B, C], One, &groups);
|
||||||
for x in &prime {
|
for x in &prime {
|
||||||
println!("{}", print_implicant(x));
|
println!("{}", print_implicant(x));
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
pub mod svg;
|
||||||
|
|
||||||
/// Returns grid coordinates, width, height
|
/// Returns grid coordinates, width, height
|
||||||
pub fn grid(var_count: usize) -> (Vec<(usize, usize)>, usize, usize) {
|
pub fn grid(var_count: usize) -> (Vec<(usize, usize)>, usize, usize) {
|
||||||
let mut grid = vec![(0, 0), (1, 0)];
|
let mut grid = vec![(0, 0), (1, 0)];
|
142
src/ui/svg.rs
Normal file
142
src/ui/svg.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
use svg_fmt::*;
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
const SIZE_FONT: f32 = 20.0;
|
||||||
|
|
||||||
|
const COLORS: &'static [Color] = &[
|
||||||
|
rgb(0, 170, 255),
|
||||||
|
rgb(255, 0, 0),
|
||||||
|
rgb(0, 255, 0),
|
||||||
|
rgb(255, 170, 0),
|
||||||
|
rgb(255, 0, 255), // pink
|
||||||
|
rgb(0, 128, 0), // dark green
|
||||||
|
rgb(0, 0, 128), // dark blue
|
||||||
|
rgb(128, 0, 128), // dark red
|
||||||
|
];
|
||||||
|
|
||||||
|
pub fn get_svg(size_factor: usize, function: &FunctionSpec, vars: &[Variable], block_masks: &[(usize, usize)]) -> String {
|
||||||
|
let size4 = size_factor / 4;
|
||||||
|
let size8 = size_factor / 8;
|
||||||
|
let delta = size_factor as f32 / 32.0;
|
||||||
|
let mut svg = String::new();
|
||||||
|
|
||||||
|
let (grid, w, h) = grid(vars.len());
|
||||||
|
|
||||||
|
let mut blocks = vec![vec![vec![]; w]; h];
|
||||||
|
for (i, &(x, y)) in grid.iter().enumerate() {
|
||||||
|
for (idx, &(mask, inv_mask)) in block_masks.iter().enumerate() {
|
||||||
|
if check_mask(i, mask, inv_mask) {
|
||||||
|
blocks[y][x].push(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg += &BeginSvg { w: w * size_factor + 3, h: h * size_factor + 3 }.to_string();
|
||||||
|
for (i, &(x, y)) in grid.iter().enumerate() {
|
||||||
|
svg += &rectangle(1 + x * size_factor, 1 + y * size_factor, size_factor, size_factor)
|
||||||
|
.fill(white())
|
||||||
|
.stroke(Stroke::Color(black(), 2.0)).to_string();
|
||||||
|
let x_start = 1 + x * size_factor;
|
||||||
|
let x_end = 1 + (x+1) * size_factor;
|
||||||
|
let y_start = 1 + y * size_factor;
|
||||||
|
let y_end = 1 + (y+1) * size_factor;
|
||||||
|
let mut rect = rectangle(1 + x * size_factor, 1 + y * size_factor, size_factor, size_factor)
|
||||||
|
.fill(white())
|
||||||
|
.stroke_opacity(0.7)
|
||||||
|
.inflate(-1, -1);
|
||||||
|
for (idx, &(mask, inv_mask)) in block_masks.iter().enumerate() {
|
||||||
|
rect = rect.inflate(-delta, -delta);
|
||||||
|
if check_mask(i, mask, inv_mask) {
|
||||||
|
// check cells around this one
|
||||||
|
let mut up = false;
|
||||||
|
let mut down = false;
|
||||||
|
let mut left = false;
|
||||||
|
let mut right = false;
|
||||||
|
for &var in vars {
|
||||||
|
let j = i ^ var;
|
||||||
|
if check_mask(j, mask, inv_mask) {
|
||||||
|
let (x2, y2) = grid[j];
|
||||||
|
if (x + 1) % w == x2 && y == y2 {
|
||||||
|
right = true;
|
||||||
|
}
|
||||||
|
if x == x2 && (y + 1) % h == y2 {
|
||||||
|
down = true;
|
||||||
|
}
|
||||||
|
if x == x2 && y == (y2 + 1) % h {
|
||||||
|
up = true;
|
||||||
|
}
|
||||||
|
if x == (x2 + 1) % w && y == y2 {
|
||||||
|
left = true;
|
||||||
|
}
|
||||||
|
if !up && !down && !left && !right {
|
||||||
|
// happens when vars >= 5 (obviously)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut sides = rect.sides();
|
||||||
|
for s in &mut sides {
|
||||||
|
*s = s.opacity(0.9);
|
||||||
|
}
|
||||||
|
let up = up && !(y == 0 && (0..h).all(|y| blocks[y][x].contains(&idx)));
|
||||||
|
let down = down && !(y == h-1 && (0..h).all(|y| blocks[y][x].contains(&idx)));
|
||||||
|
let left = left && !(x == 0 && (0..w).all(|x| blocks[y][x].contains(&idx)));
|
||||||
|
let right = right && !(x == w-1 && (0..w).all(|x| blocks[y][x].contains(&idx)));
|
||||||
|
if up {
|
||||||
|
svg += &sides[0].width(2.0).color(white()).to_string();
|
||||||
|
if !left {
|
||||||
|
svg += &sides[2].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0.0, y_start).to_string();
|
||||||
|
}
|
||||||
|
if !right {
|
||||||
|
svg += &sides[3].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0.0, y_start).to_string();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
svg += &sides[0].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||||
|
}
|
||||||
|
if down {
|
||||||
|
svg += &sides[1].width(2.0).color(white()).to_string();
|
||||||
|
if !left {
|
||||||
|
svg += &sides[2].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0, y_end).to_string();
|
||||||
|
}
|
||||||
|
if !right {
|
||||||
|
svg += &sides[3].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(0, y_end).to_string();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
svg += &sides[1].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||||
|
}
|
||||||
|
if left {
|
||||||
|
svg += &sides[2].width(2.0).color(white()).to_string();
|
||||||
|
if !up {
|
||||||
|
svg += &sides[0].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_start, 0).to_string();
|
||||||
|
}
|
||||||
|
if !down {
|
||||||
|
svg += &sides[1].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_start, 0).to_string();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
svg += &sides[2].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||||
|
}
|
||||||
|
if right {
|
||||||
|
svg += &sides[3].width(1.0).color(white()).to_string();
|
||||||
|
if !up {
|
||||||
|
svg += &sides[0].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_end, 0).to_string();
|
||||||
|
}
|
||||||
|
if !down {
|
||||||
|
svg += &sides[1].width(2.0).color(COLORS[idx % COLORS.len()]).flip_to(x_end, 0).to_string();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
svg += &sides[3].width(2.0).color(COLORS[idx % COLORS.len()]).to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
svg += &text((x+1) * size_factor - size8, (y+1) * size_factor - size4 / 3, i.to_string())
|
||||||
|
.align(Align::Right)
|
||||||
|
.size(SIZE_FONT)
|
||||||
|
.color(black()).to_string();
|
||||||
|
svg += &text(x * size_factor + size4, (y+1) * size_factor - size4, function[i].to_string())
|
||||||
|
.size(SIZE_FONT * 2.0)
|
||||||
|
.color(black()).to_string();
|
||||||
|
}
|
||||||
|
svg += &EndSvg.to_string();
|
||||||
|
|
||||||
|
svg
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user