{"id":20660984,"url":"https://github.com/liatemplates/logicemu","last_synced_at":"2026-02-03T12:37:19.232Z","repository":{"id":107256834,"uuid":"165814729","full_name":"LiaTemplates/LogicEmu","owner":"LiaTemplates","description":"Integrate the graphical logicemu-project into LiaScript","archived":false,"fork":false,"pushed_at":"2019-11-24T22:42:37.000Z","size":176,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-17T06:15:08.476Z","etag":null,"topics":["emulator","liascript","logic","template"],"latest_commit_sha":null,"homepage":"https://liascript.github.io/course/?https://raw.githubusercontent.com/liaTemplates/LogicEmu/master/README.md","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LiaTemplates.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-15T08:36:20.000Z","updated_at":"2023-02-01T04:17:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"8b5c9f06-653b-4dc9-b7ab-f1e0f5f35182","html_url":"https://github.com/LiaTemplates/LogicEmu","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LiaTemplates/LogicEmu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FLogicEmu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FLogicEmu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FLogicEmu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FLogicEmu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiaTemplates","download_url":"https://codeload.github.com/LiaTemplates/LogicEmu/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaTemplates%2FLogicEmu/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264027589,"owners_count":23546103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emulator","liascript","logic","template"],"created_at":"2024-11-16T19:06:43.600Z","updated_at":"2026-02-03T12:37:14.204Z","avatar_url":"https://github.com/LiaTemplates.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nauthor:   André Dietrich\n\nemail:    andre.dietrich@ovgu.de\n\nversion:  0.0.4\n\nlanguage: en\n\nnarrator: US English Female\n\nlogo:     https://raw.githubusercontent.com/lvandeve/logicemu/master/screenshot_add.png\n\ncomment:  Use LogicEmu to create any kind of logic emulations based on ASCII-diagrams.\n\n@LogicEmu.eval: @LogicEmu._eval_(@uid)\n\n@LogicEmu._eval_\n\u003cscript\u003e\nlet code = window.encodeBoard(`@input`);\n\nlet iframe = document.getElementById(\"logic_emu@0\");\n\niframe.contentWindow.location.reload(true);\n\niframe.contentWindow.location.replace(\"https://liatemplates.github.io/LogicEmu/docs/index.html#code=\"+code);\n\n\"LIA: stop\";\n\u003c/script\u003e\n\n\u003ciframe id=\"logic_emu@0\" width=\"100%\" height=\"400px\" src=\"\"\u003e\u003c/iframe\u003e\n\n@end\n\n@LogicEmu.run: @LogicEmu._run_(@uid,```@0```)\n\n@LogicEmu._run_\n\u003cscript\u003e\nlet code = window.encodeBoard(`@1`);\n\nlet iframe = document.getElementById(\"logic_emu@0\");\n\niframe.contentWindow.location.reload(true);\n\niframe.contentWindow.location.replace(\"https://liatemplates.github.io/LogicEmu/docs/index.html#code=\"+code);\n\u003c/script\u003e\n\n\u003ciframe id=\"logic_emu@0\" width=\"100%\" height=\"400px\" src=\"\"\u003e\u003c/iframe\u003e\n\n@end\n\n@onload\nfunction LZ77Coder() {\n  this.lz77MatchLen = function(text, i0, i1) {\n    var l = 0;\n    while(i1 + l \u003c text.length \u0026\u0026 text[i1 + l] == text[i0 + l] \u0026\u0026 l \u003c 255) {\n      l++;\n    }\n    return l;\n  };\n\n  this.encodeString = function(text) {\n    return arrayToString(this.encode(stringToArray(text)));\n  };\n\n  this.decodeString = function(text) {\n    return arrayToString(this.decode(stringToArray(text)));\n  };\n\n  // Designed mainly for 7-bit ASCII text. Although the text array may contain values\n  // above 127 (e.g. unicode codepoints), only values 0-127 are encoded efficiently.\n  this.encode = function(text) {\n    var result = [];\n    var map = {};\n\n    var encodeVarint = function(i, arr) {\n      if(i \u003c 128) {\n        arr.push(i);\n      } else if(i \u003c 16384) {\n        arr.push(128 | (i \u0026 127));\n        arr.push(i \u003e\u003e 7);\n      } else {\n        arr.push(128 | (i \u0026 127));\n        arr.push(128 | ((i \u003e\u003e 7) \u0026 127));\n        arr.push((i \u003e\u003e 14) \u0026 127);\n      }\n    };\n\n    for(var i = 0; i \u003c text.length; i++) {\n      var len = 0;\n      var dist = 0;\n\n      var sub = arrayToStringPart(text, i, 4);\n      var s = map[sub];\n      if(s) {\n        for(var j = s.length - 1; j \u003e= 0; j--) {\n          var i2 = s[j];\n          var d = i - i2;\n          if(d \u003e 2097151) break;\n          var l = this.lz77MatchLen(text, i2, i);\n          if(l \u003e len) {\n            len = l;\n            dist = d;\n            if(l \u003e 255) break; // good enough, stop search\n          }\n        }\n      }\n\n      if(len \u003e 2097151) len = 2097151;\n\n      if(!(len \u003e 5 || (len \u003e 4 \u0026\u0026 dist \u003c 16383) || (len \u003e 3 \u0026\u0026 dist \u003c 127))) {\n        len = 1;\n      }\n\n      for(var j = 0; j \u003c len; j++) {\n        var sub = arrayToStringPart(text, i + j, 4);\n        if(!map[sub]) map[sub] = [];\n        if(map[sub].length \u003e 1000) map[sub] = []; // prune\n        map[sub].push(i + j);\n      }\n      i += len - 1;\n\n      if(len \u003e= 3) {\n        if(len \u003c 130) {\n          result.push(128 + len - 3);\n        } else {\n          var len2 = len - 128;\n          result.push(255);\n          encodeVarint(len2, result);\n        }\n        encodeVarint(dist, result);\n      } else {\n        var c = text[i];\n        if(c \u003c 128) {\n          result.push(c);\n        } else {\n          // Above-ascii character, encoded as unicode codepoint (not UTF-16).\n          // Normally such character does not appear in circuits, but it could in comments.\n          result.push(255);\n          encodeVarint(c - 128, result);\n          result.push(0);\n        }\n      }\n    }\n    return result;\n  };\n\n  this.decode = function(encoded) {\n    var result = [];\n    var temp;\n    for(var i = 0; i \u003c encoded.length;) {\n      var c = encoded[i++];\n      if(c \u003e 127) {\n        var len = c + 3 - 128;\n        if(c == 255) {\n          len = encoded[i++];\n          if(len \u003e 127) len += (encoded[i++] \u003c\u003c 7) - 128;\n          if(len \u003e 16383) len += (encoded[i++] \u003c\u003c 14) - 16384;\n          len += 128;\n        }\n        dist = encoded[i++];\n        if(dist \u003e 127) dist += (encoded[i++] \u003c\u003c 7) - 128;\n        if(dist \u003e 16383) dist += (encoded[i++] \u003c\u003c 14) - 16384;\n\n        if(dist == 0) {\n          result.push(len);\n        } else {\n          for(var j = 0; j \u003c len; j++) {\n            result.push(result[result.length - dist]);\n          }\n        }\n      } else {\n        result.push(c);\n      }\n    }\n    return result;\n  };\n}\nfunction arrayToString(a) {\n  var s = '';\n  for(var i = 0; i \u003c a.length; i++) {\n    //s += String.fromCharCode(a[i]);\n    var c = a[i];\n    if (c \u003c 0x10000) {\n       s += String.fromCharCode(c);\n    } else if (c \u003c= 0x10FFFF) {\n      s += String.fromCharCode((c \u003e\u003e 10) + 0xD7C0);\n      s += String.fromCharCode((c \u0026 0x3FF) + 0xDC00);\n    } else {\n      s += ' ';\n    }\n  }\n  return s;\n}\nfunction stringToArray(s) {\n  var a = [];\n  for(var i = 0; i \u003c s.length; i++) {\n    //a.push(s.charCodeAt(i));\n    var c = s.charCodeAt(i);\n    if (c \u003e= 0xD800 \u0026\u0026 c \u003c= 0xDBFF \u0026\u0026 i + 1 \u003c s.length) {\n      var c2 = s.charCodeAt(i + 1);\n      if (c2 \u003e= 0xDC00 \u0026\u0026 c2 \u003c= 0xDFFF) {\n        c = (c \u003c\u003c 10) + c2 - 0x35FDC00;\n        i++;\n      }\n    }\n    a.push(c);\n  }\n  return a;\n}\n// ignores the utf-32 unlike arrayToString but that's ok for now\nfunction arrayToStringPart(a, pos, len) {\n  var s = '';\n  for(var i = pos; i \u003c pos + len; i++) {\n    s += String.fromCharCode(a[i]);\n  }\n  return s;\n}\nfunction RangeCoder() {\n  this.base = 256;\n  this.high = 1 \u003c\u003c 24;\n  this.low = 1 \u003c\u003c 16;\n  this.num = 256;\n  this.values = [];\n  this.inc = 8;\n\n  this.reset = function() {\n    this.values = [];\n    for(var i = 0; i \u003c= this.num; i++) {\n      this.values.push(i);\n    }\n  };\n\n  this.floordiv = function(a, b) {\n    return Math.floor(a / b);\n  };\n\n  // Javascript numbers are doubles with 53 bits of integer precision so can\n  // represent unsigned 32-bit ints, but logic operators like \u0026 and \u003e\u003e behave as\n  // if on 32-bit signed integers (31-bit unsigned). Mask32 makes the result\n  // positive again. Use e.g. after multiply to simulate unsigned 32-bit overflow.\n  this.mask32 = function(a) {\n    return ((a \u003e\u003e 1) \u0026 0x7fffffff) * 2 + (a \u0026 1);\n  };\n\n  this.update = function(symbol) {\n    // too large denominator\n    if(this.getTotal() + this.inc \u003e= this.low) {\n      var last = this.values[0];\n      for(var i = 0; i \u003c this.num; i++) {\n        var d = this.values[i + 1] - last;\n        d = (d \u003e 1) ? this.floordiv(d, 2) : d;\n        last = this.values[i + 1];\n        this.values[i + 1] = this.values[i] + d;\n      }\n    }\n    for(var i = symbol + 1; i \u003c this.values.length; i++) {\n      this.values[i] += this.inc;\n    }\n  };\n\n  this.getProbability = function(symbol) {\n    return [this.values[symbol], this.values[symbol + 1]];\n  };\n\n  this.getSymbol = function(scaled_value) {\n    var symbol = this.binSearch(this.values, scaled_value);\n    var p = this.getProbability(symbol);\n    p.push(symbol);\n    return p;\n  };\n\n  this.getTotal = function() {\n    return this.values[this.values.length - 1];\n  };\n\n  // returns last index in values that contains entry that is \u003c= value\n  this.binSearch = function(values, value) {\n    var high = values.length - 1, low = 0, result = 0;\n    if(value \u003e values[high]) return high;\n    while(low \u003c= high) {\n      var mid = this.floordiv(low + high, 2);\n      if(values[mid] \u003e= value) {\n        result = mid;\n        high = mid - 1;\n      } else {\n        low = mid + 1;\n      }\n    }\n    if(result \u003e 0 \u0026\u0026 values[result] \u003e value) result--;\n    return result;\n  };\n\n  this.encodeString = function(text) {\n    return arrayToString(this.encode(stringToArray(text)));\n  };\n\n  this.decodeString = function(text) {\n    return arrayToString(this.decode(stringToArray(text)));\n  };\n\n  this.encode = function(data) {\n    this.reset();\n\n    var result = [1];\n    var low = 0;\n    var range = 0xffffffff;\n\n    result.push(data.length \u0026 255);\n    result.push((data.length \u003e\u003e 8) \u0026 255);\n    result.push((data.length \u003e\u003e 16) \u0026 255);\n    result.push((data.length \u003e\u003e 24) \u0026 255);\n\n    for(var i = 0; i \u003c data.length; i++) {\n      var c = data[i];\n      var p = this.getProbability(c);\n      var total = this.getTotal();\n      var start = p[0];\n      var size = p[1] - p[0];\n      this.update(c);\n      range = this.floordiv(range, total);\n      low = this.mask32(start * range + low);\n      range = this.mask32(range * size);\n\n      for(;;) {\n        if(low == 0 \u0026\u0026 range == 0) {\n          return null; // something went wrong, avoid hanging\n        }\n        if(this.mask32(low ^ (low + range)) \u003e= this.high) {\n          if(range \u003e= this.low) break;\n          range = this.mask32((-low) \u0026 (this.low - 1));\n        }\n        result.push((this.floordiv(low, this.high)) \u0026 (this.base - 1));\n        range = this.mask32(range * this.base);\n        low = this.mask32(low * this.base);\n      }\n    }\n\n    for(var i = this.high; i \u003e 0; i = this.floordiv(i, this.base)) {\n      result.push(this.floordiv(low, this.high) \u0026 (this.base - 1));\n      low = this.mask32(low * this.base);\n    }\n\n    if(result.length \u003e data.length) {\n      result = [0];\n      for(var i = 0; i \u003c data.length; i++) result[i + 1] = data[i];\n    }\n\n    return result;\n  };\n\n  this.decode = function(data) {\n    if(data.length \u003c 1) return null;\n    var result = [];\n    if(data[0] == 0) {\n      for(var i = 1; i \u003c data.length; i++) result[i - 1] = data[i];\n      return result;\n    }\n    if(data[0] != 1) return null;\n    if(data.length \u003c 5) return null;\n\n    this.reset();\n\n    var code = 0;\n    var low = 0;\n    var range = 0xffffffff;\n    var pos = 1;\n    var symbolsize = data[pos++];\n    symbolsize |= (data[pos++] \u003c\u003c 8);\n    symbolsize |= (data[pos++] \u003c\u003c 16);\n    symbolsize |= (data[pos++] \u003c\u003c 24);\n    symbolsize = this.mask32(symbolsize);\n\n    for(var i = this.high; i \u003e 0; i = this.floordiv(i, this.base)) {\n      var d = pos \u003e= data.length ? 0 : data[pos++];\n      code = this.mask32(code * this.base + d);\n    }\n    for(var i = 0; i \u003c symbolsize; i++) {\n      var total = this.getTotal();\n      var scaled_value = this.floordiv(code - low, (this.floordiv(range, total)));\n      var p = this.getSymbol(scaled_value);\n      var c = p[2];\n      result.push(c);\n      var start = p[0];\n      var size = p[1] - p[0];\n      this.update(c);\n\n      range = this.floordiv(range, total);\n      low = this.mask32(start * range + low);\n      range = this.mask32(range * size);\n      for(;;) {\n        if(low == 0 \u0026\u0026 range == 0) {\n          return null; // something went wrong, avoid hanging\n        }\n        if(this.mask32(low ^ (low + range)) \u003e= this.high) {\n          if(range \u003e= this.low) break;\n          range = this.mask32((-low) \u0026 (this.low - 1));\n        }\n        var d = pos \u003e= data.length ? 0 : data[pos++];\n        code = this.mask32(code * this.base + d);\n        range = this.mask32(range * this.base);\n        low = this.mask32(low * this.base);\n      }\n    }\n\n    return result;\n  };\n}\n\nwindow.encodeBoard = function(text) {\n  var lz77 = (new LZ77Coder()).encodeString(text);\n  var range = (new RangeCoder()).encodeString(lz77);\n  return '0' + toBase64(range); // '0' = format version\n}\n\nfunction toBase64(text) {\n  var result = btoa(text);\n  result = result.split('=')[0];\n  result = result.replace(new RegExp('\\\\+', 'g'), '-');\n  result = result.replace(new RegExp('/', 'g'), '_');\n  return result;\n}\n@end\n\n--\u003e\n\n# LogicEmu - Template\n\n                                   --{{0}}--\nThis document makes the logic emulator [logicemu](https://lodev.org/logicemu/)\navailable in [LiaScript](https://LiaScript.github.io). You can either run\nsimulations directly or associate them with an code-block, to make them editable\nand executable.\n\n__Try it on LiaScript:__\n\nhttps://liascript.github.io/course/?https://raw.githubusercontent.com/liaTemplates/logicemu/master/README.md\n\n__See the project on Github:__\n\nhttps://github.com/liaTemplates/logicemu\n\n                                   --{{1}}--\nThere are three ways to use this template. The easiest way is to use the\n`import` statement and the url of the raw text-file of the master branch or any\nother branch or version. But you can also copy the required functionionality\ndirectly into the header of your Markdown document, see therefor the [last\nslide](#4 \"Implementation\"). And of course, you could also clone this project\nand change it, as you wish.\n\n                                     {{1}}\n1. Load the macros via\n\n   `import: https://raw.githubusercontent.com/liaTemplates/logicemu/master/README.md`\n\n2. Copy the definitions into your Project\n\n3. Clone this repository on GitHub\n\n## `@LogicEmu.run`\n\n                                     {{0}}\nUse the following block-macro, if you only want to execute a certain simulation.\nThat means, use the common Markdown code-block notation, add a syntax\nhighlighting and the macro `@LogicEmu.run` to the head of this block. That's it\nthe simulation will be automatically executed in LiaScript.\n\n``` text @LogicEmu.run\ns******\u003ea****\u003el\n       ^\ns******\n\n\ns**\u003ea**\u003eo**\u003el\n   \u003e    ^\ns**\u003ee**\u003ea\n       \u003e\ns******\u003ee**\u003el\n```\n\n\n## `@LogicEmu.eval`\n\n                                     {{0}}\nIf you want to allow editing, then simply attach the macro `@LogicEmu.eval` to\nthe end of your code-block.\n\n```\n\"128\"  \"64\"  \"32\"  \"16\"  \"8\"   \"4\"   \"2\"   \"1\"\n  l     l     l     l     l     l     l     l\n  ^     ^     ^     ^     ^     ^     ^     ^\n\"carry\"l\u003co\u003ca e o\u003ca e o\u003ca e o\u003ca e o\u003ca e o\u003ca e o\u003ca e o\u003ca e s\"carry\"\n^ ^^^/^ ^^^/^ ^^^/^ ^^^/^ ^^^/^ ^^^/^ ^^^/^ ^^^/\na e * a e * a e * a e * a e * a e * a e * a e *\n^^^   ^^^   ^^^   ^^^   ^^^   ^^^   ^^^   ^^^\n| |   | |   | |   | |   | |   | |   | |   | |\n| |   | |   | |   | |   | |   | |   | |   | |\n| ,---+-+---+-+---+-+---+-+---+-+---+-+---+-+-*\n| ,---* *---+-+---+-+---+-+---+-+---+-+---+-+-+-*\n| | *-------* *---+-+---+-+---+-+---+-+---+-+-+-+-*\n| | | *-----------* *---+-+---+-+---+-+---+-+-+-+-+-*\n| | | | *---------------* *---+-+---+-+---+-+-+-+-+-+-*\n| | | | | *-------------------* *---+-+---+-+-+-+-+-+-+-*\n| | | | | | *-----------------------* *---+-+-+-+-+-+-+-+-*\n| | | | | | | *---------------------------* *-+-+-+-+-+-+-+-*\n| | | | | | | |                               | | | | | | | |\n| | | | | | | |                               | | | | | | | |\ns s s s s s s s                               s s s s s s s s\n\"a\" \"1 6 3 1 8 4 2 1\"                         \"b\" \"1 6 3 1 8 4 2 1\"\n\"2 4 2 6        \"                             \"2 4 2 6        \"\n\"8              \"                             \"8              \"\n```\n@LogicEmu.eval\n\n\n## Implementation\n\n                         --{{0}}--\nThe code shows how the two main macros were implemented by calling the macro\n`@LogicEmu._eval_` and `@LogicEmu._run_`. All ASCII diagrams are passed to an\niframe, which contains a reduced version of the original logicemu. `@onload` is\nrequired at the beginning to load all required functionionality that is needed\nto encode the input.\n\n````` js\n@LogicEmu.eval: @LogicEmu._eval_(@uid)\n\n@LogicEmu._eval_\n\u003cscript\u003e\nlet code = window.encodeBoard(`@input`);\n\nlet iframe = document.getElementById(\"logic_emu@0\");\n\niframe.contentWindow.location.reload(true);\n\niframe.contentWindow.location.replace(\"https://liatemplates.github.io/LogicEmu/docs/index.html#code=\"+code);\n\n\"LIA: stop\";\n\u003c/script\u003e\n\n\u003ciframe id=\"logic_emu@0\" width=\"100%\" height=\"400px\" src=\"\"\u003e\u003c/iframe\u003e\n\n@end\n\n@LogicEmu.run: @LogicEmu._run_(@uid,```@0```)\n\n@LogicEmu._run_\n\u003cscript\u003e\nlet code = window.encodeBoard(`@1`);\n\nlet iframe = document.getElementById(\"logic_emu@0\");\n\niframe.contentWindow.location.reload(true);\n\niframe.contentWindow.location.replace(\"https://liatemplates.github.io/LogicEmu/docs/index.html#code=\"+code);\n\u003c/script\u003e\n\n\u003ciframe id=\"logic_emu@0\" width=\"100%\" height=\"400px\" src=\"\"\u003e\u003c/iframe\u003e\n\n@end\n\n@onload\nfunction LZ77Coder() {\n  this.lz77MatchLen = function(text, i0, i1) {\n    var l = 0;\n    while(i1 + l \u003c text.length \u0026\u0026 text[i1 + l] == text[i0 + l] \u0026\u0026 l \u003c 255) {\n      l++;\n    }\n    return l;\n  };\n\n  this.encodeString = function(text) {\n    return arrayToString(this.encode(stringToArray(text)));\n  };\n\n  this.decodeString = function(text) {\n    return arrayToString(this.decode(stringToArray(text)));\n  };\n\n  // Designed mainly for 7-bit ASCII text. Although the text array may contain values\n  // above 127 (e.g. unicode codepoints), only values 0-127 are encoded efficiently.\n  this.encode = function(text) {\n    var result = [];\n    var map = {};\n\n    var encodeVarint = function(i, arr) {\n      if(i \u003c 128) {\n        arr.push(i);\n      } else if(i \u003c 16384) {\n        arr.push(128 | (i \u0026 127));\n        arr.push(i \u003e\u003e 7);\n      } else {\n        arr.push(128 | (i \u0026 127));\n        arr.push(128 | ((i \u003e\u003e 7) \u0026 127));\n        arr.push((i \u003e\u003e 14) \u0026 127);\n      }\n    };\n\n    for(var i = 0; i \u003c text.length; i++) {\n      var len = 0;\n      var dist = 0;\n\n      var sub = arrayToStringPart(text, i, 4);\n      var s = map[sub];\n      if(s) {\n        for(var j = s.length - 1; j \u003e= 0; j--) {\n          var i2 = s[j];\n          var d = i - i2;\n          if(d \u003e 2097151) break;\n          var l = this.lz77MatchLen(text, i2, i);\n          if(l \u003e len) {\n            len = l;\n            dist = d;\n            if(l \u003e 255) break; // good enough, stop search\n          }\n        }\n      }\n\n      if(len \u003e 2097151) len = 2097151;\n\n      if(!(len \u003e 5 || (len \u003e 4 \u0026\u0026 dist \u003c 16383) || (len \u003e 3 \u0026\u0026 dist \u003c 127))) {\n        len = 1;\n      }\n\n      for(var j = 0; j \u003c len; j++) {\n        var sub = arrayToStringPart(text, i + j, 4);\n        if(!map[sub]) map[sub] = [];\n        if(map[sub].length \u003e 1000) map[sub] = []; // prune\n        map[sub].push(i + j);\n      }\n      i += len - 1;\n\n      if(len \u003e= 3) {\n        if(len \u003c 130) {\n          result.push(128 + len - 3);\n        } else {\n          var len2 = len - 128;\n          result.push(255);\n          encodeVarint(len2, result);\n        }\n        encodeVarint(dist, result);\n      } else {\n        var c = text[i];\n        if(c \u003c 128) {\n          result.push(c);\n        } else {\n          // Above-ascii character, encoded as unicode codepoint (not UTF-16).\n          // Normally such character does not appear in circuits, but it could in comments.\n          result.push(255);\n          encodeVarint(c - 128, result);\n          result.push(0);\n        }\n      }\n    }\n    return result;\n  };\n\n  this.decode = function(encoded) {\n    var result = [];\n    var temp;\n    for(var i = 0; i \u003c encoded.length;) {\n      var c = encoded[i++];\n      if(c \u003e 127) {\n        var len = c + 3 - 128;\n        if(c == 255) {\n          len = encoded[i++];\n          if(len \u003e 127) len += (encoded[i++] \u003c\u003c 7) - 128;\n          if(len \u003e 16383) len += (encoded[i++] \u003c\u003c 14) - 16384;\n          len += 128;\n        }\n        dist = encoded[i++];\n        if(dist \u003e 127) dist += (encoded[i++] \u003c\u003c 7) - 128;\n        if(dist \u003e 16383) dist += (encoded[i++] \u003c\u003c 14) - 16384;\n\n        if(dist == 0) {\n          result.push(len);\n        } else {\n          for(var j = 0; j \u003c len; j++) {\n            result.push(result[result.length - dist]);\n          }\n        }\n      } else {\n        result.push(c);\n      }\n    }\n    return result;\n  };\n}\nfunction arrayToString(a) {\n  var s = '';\n  for(var i = 0; i \u003c a.length; i++) {\n    //s += String.fromCharCode(a[i]);\n    var c = a[i];\n    if (c \u003c 0x10000) {\n       s += String.fromCharCode(c);\n    } else if (c \u003c= 0x10FFFF) {\n      s += String.fromCharCode((c \u003e\u003e 10) + 0xD7C0);\n      s += String.fromCharCode((c \u0026 0x3FF) + 0xDC00);\n    } else {\n      s += ' ';\n    }\n  }\n  return s;\n}\nfunction stringToArray(s) {\n  var a = [];\n  for(var i = 0; i \u003c s.length; i++) {\n    //a.push(s.charCodeAt(i));\n    var c = s.charCodeAt(i);\n    if (c \u003e= 0xD800 \u0026\u0026 c \u003c= 0xDBFF \u0026\u0026 i + 1 \u003c s.length) {\n      var c2 = s.charCodeAt(i + 1);\n      if (c2 \u003e= 0xDC00 \u0026\u0026 c2 \u003c= 0xDFFF) {\n        c = (c \u003c\u003c 10) + c2 - 0x35FDC00;\n        i++;\n      }\n    }\n    a.push(c);\n  }\n  return a;\n}\n// ignores the utf-32 unlike arrayToString but that's ok for now\nfunction arrayToStringPart(a, pos, len) {\n  var s = '';\n  for(var i = pos; i \u003c pos + len; i++) {\n    s += String.fromCharCode(a[i]);\n  }\n  return s;\n}\nfunction RangeCoder() {\n  this.base = 256;\n  this.high = 1 \u003c\u003c 24;\n  this.low = 1 \u003c\u003c 16;\n  this.num = 256;\n  this.values = [];\n  this.inc = 8;\n\n  this.reset = function() {\n    this.values = [];\n    for(var i = 0; i \u003c= this.num; i++) {\n      this.values.push(i);\n    }\n  };\n\n  this.floordiv = function(a, b) {\n    return Math.floor(a / b);\n  };\n\n  // Javascript numbers are doubles with 53 bits of integer precision so can\n  // represent unsigned 32-bit ints, but logic operators like \u0026 and \u003e\u003e behave as\n  // if on 32-bit signed integers (31-bit unsigned). Mask32 makes the result\n  // positive again. Use e.g. after multiply to simulate unsigned 32-bit overflow.\n  this.mask32 = function(a) {\n    return ((a \u003e\u003e 1) \u0026 0x7fffffff) * 2 + (a \u0026 1);\n  };\n\n  this.update = function(symbol) {\n    // too large denominator\n    if(this.getTotal() + this.inc \u003e= this.low) {\n      var last = this.values[0];\n      for(var i = 0; i \u003c this.num; i++) {\n        var d = this.values[i + 1] - last;\n        d = (d \u003e 1) ? this.floordiv(d, 2) : d;\n        last = this.values[i + 1];\n        this.values[i + 1] = this.values[i] + d;\n      }\n    }\n    for(var i = symbol + 1; i \u003c this.values.length; i++) {\n      this.values[i] += this.inc;\n    }\n  };\n\n  this.getProbability = function(symbol) {\n    return [this.values[symbol], this.values[symbol + 1]];\n  };\n\n  this.getSymbol = function(scaled_value) {\n    var symbol = this.binSearch(this.values, scaled_value);\n    var p = this.getProbability(symbol);\n    p.push(symbol);\n    return p;\n  };\n\n  this.getTotal = function() {\n    return this.values[this.values.length - 1];\n  };\n\n  // returns last index in values that contains entry that is \u003c= value\n  this.binSearch = function(values, value) {\n    var high = values.length - 1, low = 0, result = 0;\n    if(value \u003e values[high]) return high;\n    while(low \u003c= high) {\n      var mid = this.floordiv(low + high, 2);\n      if(values[mid] \u003e= value) {\n        result = mid;\n        high = mid - 1;\n      } else {\n        low = mid + 1;\n      }\n    }\n    if(result \u003e 0 \u0026\u0026 values[result] \u003e value) result--;\n    return result;\n  };\n\n  this.encodeString = function(text) {\n    return arrayToString(this.encode(stringToArray(text)));\n  };\n\n  this.decodeString = function(text) {\n    return arrayToString(this.decode(stringToArray(text)));\n  };\n\n  this.encode = function(data) {\n    this.reset();\n\n    var result = [1];\n    var low = 0;\n    var range = 0xffffffff;\n\n    result.push(data.length \u0026 255);\n    result.push((data.length \u003e\u003e 8) \u0026 255);\n    result.push((data.length \u003e\u003e 16) \u0026 255);\n    result.push((data.length \u003e\u003e 24) \u0026 255);\n\n    for(var i = 0; i \u003c data.length; i++) {\n      var c = data[i];\n      var p = this.getProbability(c);\n      var total = this.getTotal();\n      var start = p[0];\n      var size = p[1] - p[0];\n      this.update(c);\n      range = this.floordiv(range, total);\n      low = this.mask32(start * range + low);\n      range = this.mask32(range * size);\n\n      for(;;) {\n        if(low == 0 \u0026\u0026 range == 0) {\n          return null; // something went wrong, avoid hanging\n        }\n        if(this.mask32(low ^ (low + range)) \u003e= this.high) {\n          if(range \u003e= this.low) break;\n          range = this.mask32((-low) \u0026 (this.low - 1));\n        }\n        result.push((this.floordiv(low, this.high)) \u0026 (this.base - 1));\n        range = this.mask32(range * this.base);\n        low = this.mask32(low * this.base);\n      }\n    }\n\n    for(var i = this.high; i \u003e 0; i = this.floordiv(i, this.base)) {\n      result.push(this.floordiv(low, this.high) \u0026 (this.base - 1));\n      low = this.mask32(low * this.base);\n    }\n\n    if(result.length \u003e data.length) {\n      result = [0];\n      for(var i = 0; i \u003c data.length; i++) result[i + 1] = data[i];\n    }\n\n    return result;\n  };\n\n  this.decode = function(data) {\n    if(data.length \u003c 1) return null;\n    var result = [];\n    if(data[0] == 0) {\n      for(var i = 1; i \u003c data.length; i++) result[i - 1] = data[i];\n      return result;\n    }\n    if(data[0] != 1) return null;\n    if(data.length \u003c 5) return null;\n\n    this.reset();\n\n    var code = 0;\n    var low = 0;\n    var range = 0xffffffff;\n    var pos = 1;\n    var symbolsize = data[pos++];\n    symbolsize |= (data[pos++] \u003c\u003c 8);\n    symbolsize |= (data[pos++] \u003c\u003c 16);\n    symbolsize |= (data[pos++] \u003c\u003c 24);\n    symbolsize = this.mask32(symbolsize);\n\n    for(var i = this.high; i \u003e 0; i = this.floordiv(i, this.base)) {\n      var d = pos \u003e= data.length ? 0 : data[pos++];\n      code = this.mask32(code * this.base + d);\n    }\n    for(var i = 0; i \u003c symbolsize; i++) {\n      var total = this.getTotal();\n      var scaled_value = this.floordiv(code - low, (this.floordiv(range, total)));\n      var p = this.getSymbol(scaled_value);\n      var c = p[2];\n      result.push(c);\n      var start = p[0];\n      var size = p[1] - p[0];\n      this.update(c);\n\n      range = this.floordiv(range, total);\n      low = this.mask32(start * range + low);\n      range = this.mask32(range * size);\n      for(;;) {\n        if(low == 0 \u0026\u0026 range == 0) {\n          return null; // something went wrong, avoid hanging\n        }\n        if(this.mask32(low ^ (low + range)) \u003e= this.high) {\n          if(range \u003e= this.low) break;\n          range = this.mask32((-low) \u0026 (this.low - 1));\n        }\n        var d = pos \u003e= data.length ? 0 : data[pos++];\n        code = this.mask32(code * this.base + d);\n        range = this.mask32(range * this.base);\n        low = this.mask32(low * this.base);\n      }\n    }\n\n    return result;\n  };\n}\n\nwindow.encodeBoard = function(text) {\n  var lz77 = (new LZ77Coder()).encodeString(text);\n  var range = (new RangeCoder()).encodeString(lz77);\n  return '0' + toBase64(range); // '0' = format version\n}\n\nfunction toBase64(text) {\n  var result = btoa(text);\n  result = result.split('=')[0];\n  result = result.replace(new RegExp('\\\\+', 'g'), '-');\n  result = result.replace(new RegExp('/', 'g'), '_');\n  return result;\n}\n@end\n`````\n\n                         --{{1}}--\nIf you want to minimize loading effort in your LiaScript project, you can also\ncopy this code and paste it into your main comment header, see the code in the\nraw file of this document.\n\n{{1}} https://raw.githubusercontent.com/liaTemplates/logicemu/master/README.md\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliatemplates%2Flogicemu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliatemplates%2Flogicemu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliatemplates%2Flogicemu/lists"}