[
  {
    "path": ".ctags",
    "content": "--exclude=*.svg\n--exclude=*.yml\n--exclude=.cljs_rhino_repl\n--exclude=target\n--exclude=vendor"
  },
  {
    "path": ".dir-locals.el",
    "content": "((nil . ((cider-preferred-build-tool . \"clojure-cli\")\n         (cider-default-cljs-repl . node))))\n"
  },
  {
    "path": ".gitignore",
    "content": "*.class\n*.jar\n.m2\n/.cljs_rhino_repl\n/.cpcache\n/.nightlight.edn\n/.nrepl-*\n/.nyc_output\n/TAGS\n/node_modules\n/out\n/tags\n/target\n"
  },
  {
    "path": ".s3cfg",
    "content": "[default]\naccess_key =\nacl_public = False\nbucket_location = US\ncloudfront_host = cloudfront.amazonaws.com\ncloudfront_resource = /2008-06-30/distribution\ndefault_mime_type = binary/octet-stream\ndelete_removed = False\ndry_run = False\nencoding = UTF-8\nencrypt = False\nforce = False\nget_continue = False\ngpg_command = /usr/bin/gpg\ngpg_decrypt = %(gpg_command)s -d --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s\ngpg_encrypt = %(gpg_command)s -c --verbose --no-use-agent --batch --yes --passphrase-fd %(passphrase_fd)s -o %(output_file)s %(input_file)s\ngpg_passphrase =\nguess_mime_type = True\nhost_base = s3.amazonaws.com\nhost_bucket = %(bucket)s.s3.amazonaws.com\nhuman_readable_sizes = False\nlist_md5 = False\npreserve_attrs = True\nprogress_meter = True\nproxy_host =\nproxy_port = 0\nrecursive = False\nrecv_chunk = 4096\nsecret_key =\nsend_chunk = 4096\nsimpledb_host = sdb.amazonaws.com\nskip_existing = False\nurlencoding_mode = normal\nuse_https = False\nverbosity = WARNING\n"
  },
  {
    "path": "BACKERS.md",
    "content": "Listed below are the [Zetawar Kickstarter](https://www.kickstarter.com/projects/311016908/zetawar/)\nbackers who chose to have their names listed and backed the project at or above the\n\"Virtual Immortality\" level.\n\n### Sponsors\n\n[![Awesome Forms](https://awesomeforms.com/assets/logotype-0e39fee9805478f7769f0e25cb16b6e2.png)](https://awesomeforms.com)\n\n[Rowdy Labs](http://www.rowdylabs.com)\n\n### Backers\n\n- -Toni-\n- Alex Eberts\n- Austin Haas\n- Ben and Anna Donovan\n- Benni Stauss\n- Benno Kittelmann\n- Bill Kratzer\n- Boris Kourtoukov\n- Brandon Mathis\n- Brent Hagany\n- Cameron Callahan\n- Carl T Kleihege\n- Chris Wasser\n- Coop\n- Dan Boykis\n- Dmitri Lobach\n- Dmitri Sotnikov\n- Greg Polander\n- Herb Bruss\n- Igor \"McBell\" Makaruks\n- J Johansen\n- JD Bell\n- James Reeves\n- Jarrod Doherty\n- Jeroen Lamain\n- Joel Martin\n- Jon M Dugan\n- Jonas Winje\n- Jonas lowery\n- Jonathan Boston\n- Joshua Miller\n- Juraj Martinka\n- Kephren\n- Kephren Newton\n- Kevin D. Saunders\n- Kevin Hutson (@mrjabba)\n- Koji Yusa\n- Lambda Island\n- Lloyd Goss\n- Loosetoes\n- Luke Ehresman\n- Markku Rontu\n- Martin Clausen\n- Max Juchheim\n- Mewmoomoo\n- Michael Dowden\n- Mikailos\n- Mikkel Gravgaard\n- Nate Jones\n- Nathan Ehresman\n- Nathan Powell\n- Nikita Prokopov\n- Nor Azman\n- Pasi Heikkinen\n- Pete\n- Phillip DiPuccio\n- Rob Balder\n- Ross Williams\n- RubyGeek LLC\n- Stonecrusher\n- The Griesbach Family\n- Tianxiang Xiong\n- Tim Folkers\n- Toby Nance\n- Tomi Kasurinen\n- Troy Bonneau\n- Ty Goss\n- Vovusique\n- Walton Hoops\n- Wasserlasser\n- Zach Erbaugh\n- gtoast\n- jclaggett\n- kika\n- lfn3\n- peonicles\n- scientific-coder\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Unreleased\n* Use clickable cursor for clickable things on the map\n* Add new map\n"
  },
  {
    "path": "Jenkinsfile",
    "content": "// -*- mode: groovy; -*-\n\nnode {\n  currentBuild.result = 'SUCCESS'\n\n  def commitHash\n  def permaBuildPrefix\n\n  try {\n    stage('Checkout') {\n      checkout scm\n      sh 'git rev-parse HEAD > commit'\n      commitHash = readFile('commit').trim()\n      permaBuildPrefix = \"/builds/${commitHash}\"\n    }\n\n    stage('Prepare') {\n      sh 'nix-shell . --run \"npm install\"'\n    }\n\n    stage('Test') {\n      sh \"nix-shell . --run 'boot --no-colors run-tests'\"\n    }\n\n    stage('Build') {\n      sh \"ZETAWAR_BUILD='${commitHash}' nix-shell . --run 'boot --no-colors build-site -e ${ZETAWAR_ENV}'\"\n      sh \"ZETAWAR_BUILD='${commitHash}' ZETAWAR_PREFIX=${permaBuildPrefix} nix-shell . --run 'boot --no-colors build-site -e ${ZETAWAR_ENV} -t target.permabuild'\"\n    }\n\n    stage('Deploy') {\n      sh \"nix-shell . --run './bin/deploy -b ${S3_BUCKET} ${PUBLIC_FLAG}'\"\n      sh \"nix-shell . --run './bin/deploy -s target.permabuild -b ${S3_BUCKET} -p ${permaBuildPrefix} ${PUBLIC_FLAG}'\"\n    }\n  } catch (err) {\n    currentBuild.result = 'FAILURE'\n    throw err\n  } finally {\n    notifyBuild(currentBuild.result)\n  }\n}\n\ndef notifyBuild(String buildStatus = 'STARTED') {\n  // build status of null means successful\n  buildStatus =  buildStatus ?: 'SUCCESS'\n\n  // Default values\n  def colorName = 'RED'\n  def colorCode = '#FF0000'\n  def recipients = UNSUCCESSFUL_RECIPIENTS\n  def subject = \"${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}!\"\n  def summary = \"${subject} (${env.BUILD_URL})\"\n  def details = \"\"\"\\\n${env.JOB_NAME} - Build # ${env.BUILD_NUMBER} - ${buildStatus}\n\nCheck console output at ${env.BUILD_URL} to view the results.\n\"\"\"\n\n  // Override default values based on build status\n  if (buildStatus == 'STARTED') {\n    color = 'YELLOW'\n    colorCode = '#FFFF00'\n  } else if (buildStatus == 'SUCCESS') {\n    color = 'GREEN'\n    colorCode = '#00FF00'\n    recipients = SUCCESS_RECIPIENTS\n\n    if (BACKER_BUILD == 'true') {\n      subject = \"A new Zetawar build is available!\"\n      summary = \"${subject} (http://dev.zetawar.com/)\"\n      urlDetails = \"A new Zetawar build is available at http://dev.zetawar.com/.\"\n      changeDetails = sh(script: 'git log --pretty=\"- %s\" --since=\"7 days ago\"', returnStdout: true)\n      footerDetails = \"\"\"\\\nYou're getting this email because you indicated you would like to receive build\nnotifications when you filled out the Zetawar Kickstarter survey. If you no\nlonger want to receive build notifications, please email builds@zetawar.com.\n\"\"\".split(\"\\n\").join(\" \")\n      details = \"\"\"\\\n${urlDetails}\n\nRecent changes:\n${changeDetails}\n\n${footerDetails}\n\"\"\"\n    }\n  } else {\n    color = 'RED'\n    colorCode = '#FF0000'\n  }\n\n  // Send notifications\n  if (SEND_NOTIFICATIONS == 'true' && (buildStatus != 'SUCCESS' || BACKER_BUILD == 'true')) {\n    emailext (\n      to: recipients,\n      replyTo: REPLY_TO,\n      subject: subject,\n      body: details,\n    )\n  }\n}\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "The MIT License\n\nCopyright (c) 2016 Arugaba LLC\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": ".PHONY: build-staging build-prod deploy-staging deploy-prod\n\nbuild-staging:\n\tboot build-site -e staging\n\nbuild-prod:\n\tboot build-site -e prod\n\ndeploy-staging: build-staging\n\t./bin/deploy -b staging.zetawar.com -P\n\ndeploy-prod: build-prod\n\t./bin/deploy -b www.zetawar.com -P\n"
  },
  {
    "path": "README.md",
    "content": "# Zetawar [![CircleCI](https://circleci.com/gh/Zetawar/zetawar.svg?style=svg&circle-token=b630f4db37e3538b7c390856687648246468e98b)](https://circleci.com/gh/Zetawar/zetawar)\n\nSource code for [Zetawar](http://www.zetawar.com/). Zetawar is funded by the\ngenerous support of the [Zetawar\nKickstarter](https://www.kickstarter.com/projects/djwhitt/zetawar) backers.\n\n## Licenses\n\nZetawar is Copyright 2016 Arugaba LLC licensed under the terms of the [MIT\nlicense](LICENSE.txt).\n\nTiles and unit sprites are from Elite Command Copyright 2015 Chris Vincent\nunder the [Creative Commons Attribution 4.0 International\nLicense](https://creativecommons.org/licenses/by/4.0/).\n\nMusic is Copyright 2017 Chris Haislet licensed under the [Creative Commons\nAttribution-NonCommercial 4.0\nInternational](https://creativecommons.org/licenses/by-nc/4.0/).\n\nAwesomeForms sponsor logo is Copyright 2015 Awesome Forms LLC 2015 all rights\nreserved.\n"
  },
  {
    "path": "assets/benchmarks.html",
    "content": "<!doctype html>\n<html>\n    <head>\n        <title>Zetawar Benchmarks</title>\n        <link rel=\"stylesheet\" type=\"text/css\" href=\"css/main.css\">\n    </head>\n    <body id=\"benchmarks\">\n        <script type=\"text/javascript\" src=\"js/vendor/lodash.js\"></script>\n        <script type=\"text/javascript\" src=\"js/vendor/platform.js\"></script>\n        <script type=\"text/javascript\" src=\"js/vendor/benchmark.js\"></script>\n        <script type=\"text/javascript\" src=\"js/main.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "assets/css/highlight/default.css",
    "content": "/*\n\nOriginal highlight.js style (c) Ivan Sagalaev <maniac@softwaremaniacs.org>\n\n*/\n\n.hljs {\n  display: block;\n  overflow-x: auto;\n  padding: 0.5em;\n  background: #F0F0F0;\n}\n\n\n/* Base color: saturation 0; */\n\n.hljs,\n.hljs-subst {\n  color: #444;\n}\n\n.hljs-comment {\n  color: #888888;\n}\n\n.hljs-keyword,\n.hljs-attribute,\n.hljs-selector-tag,\n.hljs-meta-keyword,\n.hljs-doctag,\n.hljs-name {\n  font-weight: bold;\n}\n\n\n/* User color: hue: 0 */\n\n.hljs-type,\n.hljs-string,\n.hljs-number,\n.hljs-selector-id,\n.hljs-selector-class,\n.hljs-quote,\n.hljs-template-tag,\n.hljs-deletion {\n  color: #880000;\n}\n\n.hljs-title,\n.hljs-section {\n  color: #880000;\n  font-weight: bold;\n}\n\n.hljs-regexp,\n.hljs-symbol,\n.hljs-variable,\n.hljs-template-variable,\n.hljs-link,\n.hljs-selector-attr,\n.hljs-selector-pseudo {\n  color: #BC6060;\n}\n\n\n/* Language color: hue: 90; */\n\n.hljs-literal {\n  color: #78A960;\n}\n\n.hljs-built_in,\n.hljs-bullet,\n.hljs-code,\n.hljs-addition {\n  color: #397300;\n}\n\n\n/* Meta color: hue: 200 */\n\n.hljs-meta {\n  color: #1f7199;\n}\n\n.hljs-meta-string {\n  color: #4d99bf;\n}\n\n\n/* Misc effects */\n\n.hljs-emphasis {\n  font-style: italic;\n}\n\n.hljs-strong {\n  font-weight: bold;\n}\n"
  },
  {
    "path": "assets/js/cli.cljs.edn",
    "content": "{:require [zetawar.cli]\n :compiler-options {:target :nodejs}}\n"
  },
  {
    "path": "assets/js/externs.js",
    "content": "/** @return {string} */\nvar lzwEncode = function(s) {};\n\n/** @return {string} */\nvar lzwDecode = function(s) {};\n\nvar ZetawarAI = {\n    \"makeActorContext\": function() {},\n    \"scoreActor\": function() {},\n    \"makeBaseActionContext\": function() {},\n    \"scoreBaseAction\": function() {},\n    \"makeUnitActionContext\": function() {},\n    \"scoreUnitAction\": function() {},\n};\n"
  },
  {
    "path": "assets/js/highlight.pack.js",
    "content": "/*! highlight.js v9.9.0 | BSD3 License | git.io/hljslicense */\n!function(e){var n=\"object\"==typeof window&&window||\"object\"==typeof self&&self;\"undefined\"!=typeof exports?e(exports):n&&(n.hljs=e({}),\"function\"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/[&<>]/gm,function(e){return I[e]})}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function i(e){return k.test(e)}function a(e){var n,t,r,a,o=e.className+\" \";if(o+=e.parentNode?e.parentNode.className:\"\",t=B.exec(o))return R(t[1])?t[1]:\"no-highlight\";for(o=o.split(/\\s+/),n=0,r=o.length;r>n;n++)if(a=o[n],i(a)||R(a))return a}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,i){for(var a=e.firstChild;a;a=a.nextSibling)3===a.nodeType?i+=a.nodeValue.length:1===a.nodeType&&(n.push({event:\"start\",offset:i,node:a}),i=r(a,i),t(a).match(/br|hr|img|input/)||n.push({event:\"stop\",offset:i,node:a}));return i}(e,0),n}function c(e,r,i){function a(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset<r[0].offset?e:r:\"start\"===r[0].event?e:r:e.length?e:r}function o(e){function r(e){return\" \"+e.nodeName+'=\"'+n(e.value)+'\"'}l+=\"<\"+t(e)+w.map.call(e.attributes,r).join(\"\")+\">\"}function u(e){l+=\"</\"+t(e)+\">\"}function c(e){(\"start\"===e.event?o:u)(e.node)}for(var s=0,l=\"\",f=[];e.length||r.length;){var g=a();if(l+=n(i.substring(s,g[0].offset)),s=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=a();while(g===e&&g.length&&g[0].offset===s);f.reverse().forEach(o)}else\"start\"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(i.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),\"m\"+(e.cI?\"i\":\"\")+(r?\"g\":\"\"))}function r(i,a){if(!i.compiled){if(i.compiled=!0,i.k=i.k||i.bK,i.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(\" \").forEach(function(e){var t=e.split(\"|\");u[t[0]]=[n,t[1]?Number(t[1]):1]})};\"string\"==typeof i.k?c(\"keyword\",i.k):E(i.k).forEach(function(e){c(e,i.k[e])}),i.k=u}i.lR=t(i.l||/\\w+/,!0),a&&(i.bK&&(i.b=\"\\\\b(\"+i.bK.split(\" \").join(\"|\")+\")\\\\b\"),i.b||(i.b=/\\B|\\b/),i.bR=t(i.b),i.e||i.eW||(i.e=/\\B|\\b/),i.e&&(i.eR=t(i.e)),i.tE=n(i.e)||\"\",i.eW&&a.tE&&(i.tE+=(i.e?\"|\":\"\")+a.tE)),i.i&&(i.iR=t(i.i)),null==i.r&&(i.r=1),i.c||(i.c=[]);var s=[];i.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push(\"self\"===e?i:e)}),i.c=s,i.c.forEach(function(e){r(e,i)}),i.starts&&r(i.starts,a);var l=i.c.map(function(e){return e.bK?\"\\\\.?(\"+e.b+\")\\\\.?\":e.b}).concat([i.tE,i.i]).map(n).filter(Boolean);i.t=l.length?t(l.join(\"|\"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,i,a){function o(e,n){var t,i;for(t=0,i=n.c.length;i>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!i&&r(n.iR,e)}function g(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function h(e,n,t,r){var i=r?\"\":y.classPrefix,a='<span class=\"'+i,o=t?\"\":C;return a+=e+'\">',a+n+o}function p(){var e,t,r,i;if(!E.k)return n(B);for(i=\"\",t=0,E.lR.lastIndex=0,r=E.lR.exec(B);r;)i+=n(B.substring(t,r.index)),e=g(E,r),e?(M+=e[1],i+=h(e[0],n(r[0]))):i+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(B);return i+n(B.substr(t))}function d(){var e=\"string\"==typeof E.sL;if(e&&!x[E.sL])return n(B);var t=e?l(E.sL,B,!0,L[E.sL]):f(B,E.sL.length?E.sL:void 0);return E.r>0&&(M+=t.r),e&&(L[E.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){k+=null!=E.sL?d():p(),B=\"\"}function v(e){k+=e.cN?h(e.cN,\"\",!0):\"\",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(B+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?B+=n:(t.eB&&(B+=n),b(),t.rB||t.eB||(B=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var i=E;i.skip?B+=n:(i.rE||i.eE||(B+=n),b(),i.eE&&(B=n));do E.cN&&(k+=C),E.skip||(M+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,\"\"),i.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme \"'+n+'\" for mode \"'+(E.cN||\"<unnamed>\")+'\"');return B+=n,n.length||1}var N=R(e);if(!N)throw new Error('Unknown language: \"'+e+'\"');s(N);var w,E=a||N,L={},k=\"\";for(w=E;w!==N;w=w.parent)w.cN&&(k=h(w.cN,\"\",!0)+k);var B=\"\",M=0;try{for(var I,j,O=0;;){if(E.t.lastIndex=O,I=E.t.exec(t),!I)break;j=m(t.substring(O,I.index),I[0]),O=I.index+j}for(m(t.substr(O)),w=E;w.parent;w=w.parent)w.cN&&(k+=C);return{r:M,value:k,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf(\"Illegal\"))return{r:0,value:n(t)};throw T}}function f(e,t){t=t||y.languages||E(x);var r={r:0,value:n(e)},i=r;return t.filter(R).forEach(function(n){var t=l(n,e,!1);t.language=n,t.r>i.r&&(i=t),t.r>r.r&&(i=r,r=t)}),i.language&&(r.second_best=i),r}function g(e){return y.tabReplace||y.useBR?e.replace(M,function(e,n){return y.useBR&&\"\\n\"===e?\"<br>\":y.tabReplace?n.replace(/\\t/g,y.tabReplace):void 0}):e}function h(e,n,t){var r=n?L[n]:t,i=[e.trim()];return e.match(/\\bhljs\\b/)||i.push(\"hljs\"),-1===e.indexOf(r)&&i.push(r),i.join(\" \").trim()}function p(e){var n,t,r,o,s,p=a(e);i(p)||(y.useBR?(n=document.createElementNS(\"http://www.w3.org/1999/xhtml\",\"div\"),n.innerHTML=e.innerHTML.replace(/\\n/g,\"\").replace(/<br[ \\/]*>/g,\"\\n\")):n=e,s=n.textContent,r=p?l(p,s,!0):f(s),t=u(n),t.length&&(o=document.createElementNS(\"http://www.w3.org/1999/xhtml\",\"div\"),o.innerHTML=r.value,r.value=c(t,u(o),s)),r.value=g(r.value),e.innerHTML=r.value,e.className=h(e.className,p,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function d(e){y=o(y,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll(\"pre code\");w.forEach.call(e,p)}}function v(){addEventListener(\"DOMContentLoaded\",b,!1),addEventListener(\"load\",b,!1)}function m(n,t){var r=x[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function N(){return E(x)}function R(e){return e=(e||\"\").toLowerCase(),x[e]||x[L[e]]}var w=[],E=Object.keys,x={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\\blang(?:uage)?-([\\w-]+)\\b/i,M=/((^(<[^>]+>|\\t|)+|(?:\\n)))/gm,C=\"</span>\",y={classPrefix:\"hljs-\",tabReplace:null,useBR:!1,languages:void 0},I={\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\"};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=R,e.inherit=o,e.IR=\"[a-zA-Z]\\\\w*\",e.UIR=\"[a-zA-Z_]\\\\w*\",e.NR=\"\\\\b\\\\d+(\\\\.\\\\d+)?\",e.CNR=\"(-?)(\\\\b0[xX][a-fA-F0-9]+|(\\\\b\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)([eE][-+]?\\\\d+)?)\",e.BNR=\"\\\\b(0b[01]+)\",e.RSR=\"!|!=|!==|%|%=|&|&&|&=|\\\\*|\\\\*=|\\\\+|\\\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\\\?|\\\\[|\\\\{|\\\\(|\\\\^|\\\\^=|\\\\||\\\\|=|\\\\|\\\\||~\",e.BE={b:\"\\\\\\\\[\\\\s\\\\S]\",r:0},e.ASM={cN:\"string\",b:\"'\",e:\"'\",i:\"\\\\n\",c:[e.BE]},e.QSM={cN:\"string\",b:'\"',e:'\"',i:\"\\\\n\",c:[e.BE]},e.PWM={b:/\\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\\b/},e.C=function(n,t,r){var i=e.inherit({cN:\"comment\",b:n,e:t,c:[]},r||{});return i.c.push(e.PWM),i.c.push({cN:\"doctag\",b:\"(?:TODO|FIXME|NOTE|BUG|XXX):\",r:0}),i},e.CLCM=e.C(\"//\",\"$\"),e.CBCM=e.C(\"/\\\\*\",\"\\\\*/\"),e.HCM=e.C(\"#\",\"$\"),e.NM={cN:\"number\",b:e.NR,r:0},e.CNM={cN:\"number\",b:e.CNR,r:0},e.BNM={cN:\"number\",b:e.BNR,r:0},e.CSSNM={cN:\"number\",b:e.NR+\"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?\",r:0},e.RM={cN:\"regexp\",b:/\\//,e:/\\/[gimuy]*/,i:/\\n/,c:[e.BE,{b:/\\[/,e:/\\]/,r:0,c:[e.BE]}]},e.TM={cN:\"title\",b:e.IR,r:0},e.UTM={cN:\"title\",b:e.UIR,r:0},e.METHOD_GUARD={b:\"\\\\.\\\\s*\"+e.UIR,r:0},e});hljs.registerLanguage(\"clojure\",function(e){var t={\"builtin-name\":\"def defonce cond apply if-not if-let if not not= = < > <= >= == + / * - rem quot neg? pos? delay? symbol? keyword? true? false? integer? empty? coll? list? set? ifn? fn? associative? sequential? sorted? counted? reversible? number? decimal? class? distinct? isa? float? rational? reduced? ratio? odd? even? char? seq? vector? string? map? nil? contains? zero? instance? not-every? not-any? libspec? -> ->> .. . inc compare do dotimes mapcat take remove take-while drop letfn drop-last take-last drop-while while intern condp case reduced cycle split-at split-with repeat replicate iterate range merge zipmap declare line-seq sort comparator sort-by dorun doall nthnext nthrest partition eval doseq await await-for let agent atom send send-off release-pending-sends add-watch mapv filterv remove-watch agent-error restart-agent set-error-handler error-handler set-error-mode! error-mode shutdown-agents quote var fn loop recur throw try monitor-enter monitor-exit defmacro defn defn- macroexpand macroexpand-1 for dosync and or when when-not when-let comp juxt partial sequence memoize constantly complement identity assert peek pop doto proxy defstruct first rest cons defprotocol cast coll deftype defrecord last butlast sigs reify second ffirst fnext nfirst nnext defmulti defmethod meta with-meta ns in-ns create-ns import refer keys select-keys vals key val rseq name namespace promise into transient persistent! conj! assoc! dissoc! pop! disj! use class type num float double short byte boolean bigint biginteger bigdec print-method print-dup throw-if printf format load compile get-in update-in pr pr-on newline flush read slurp read-line subvec with-open memfn time re-find re-groups rand-int rand mod locking assert-valid-fdecl alias resolve ref deref refset swap! reset! set-validator! compare-and-set! alter-meta! reset-meta! commute get-validator alter ref-set ref-history-count ref-min-history ref-max-history ensure sync io! new next conj set! to-array future future-call into-array aset gen-class reduce map filter find empty hash-map hash-set sorted-map sorted-map-by sorted-set sorted-set-by vec vector seq flatten reverse assoc dissoc list disj get union difference intersection extend extend-type extend-protocol int nth delay count concat chunk chunk-buffer chunk-append chunk-first chunk-rest max min dec unchecked-inc-int unchecked-inc unchecked-dec-inc unchecked-dec unchecked-negate unchecked-add-int unchecked-add unchecked-subtract-int unchecked-subtract chunk-next chunk-cons chunked-seq? prn vary-meta lazy-seq spread list* str find-keyword keyword symbol gensym force rationalize\"},r=\"a-zA-Z_\\\\-!.?+*=<>&#'\",n=\"[\"+r+\"][\"+r+\"0-9/;:]*\",a=\"[-+]?\\\\d+(\\\\.\\\\d+)?\",o={b:n,r:0},s={cN:\"number\",b:a,r:0},i=e.inherit(e.QSM,{i:null}),c=e.C(\";\",\"$\",{r:0}),d={cN:\"literal\",b:/\\b(true|false|nil)\\b/},l={b:\"[\\\\[\\\\{]\",e:\"[\\\\]\\\\}]\"},m={cN:\"comment\",b:\"\\\\^\"+n},p=e.C(\"\\\\^\\\\{\",\"\\\\}\"),u={cN:\"symbol\",b:\"[:]{1,2}\"+n},f={b:\"\\\\(\",e:\"\\\\)\"},h={eW:!0,r:0},y={k:t,l:n,cN:\"name\",b:n,starts:h},b=[f,i,m,p,c,u,l,s,d,o];return f.c=[e.C(\"comment\",\"\"),y,h],h.c=b,l.c=b,{aliases:[\"clj\"],i:/\\S/,c:[f,i,m,p,c,u,l,s,d]}});"
  },
  {
    "path": "assets/js/main.cljs.edn",
    "content": "{:require [zetawar.core]\n :init-fns [zetawar.core/init]\n :modules {:devcards {:entries #{\"zetawar.devcards\"}}}\n :compiler-options {:devcards true\n                    :asset-path \"/js/main.out\"\n                    :externs [\"js/externs.js\"]\n                    :foreign-libs [{:file \"lzw.js\"\n                                    :provides [\"lzw\"]}\n                                   {:file \"custom_ai.js\"\n                                    :provides [\"zetawar-js-ai\"]}]}}\n"
  },
  {
    "path": "assets/js/vendor/benchmark.js",
    "content": "/*!\n * Benchmark.js v2.1.0 <https://benchmarkjs.com/>\n * Copyright 2010-2016 Mathias Bynens <https://mths.be/>\n * Based on JSLitmus.js, copyright Robert Kieffer <http://broofa.com/>\n * Modified by John-David Dalton <http://allyoucanleet.com/>\n * Available under MIT license <https://mths.be/mit>\n */\n;(function() {\n  'use strict';\n\n  /** Used as a safe reference for `undefined` in pre ES5 environments. */\n  var undefined;\n\n  /** Used to determine if values are of the language type Object. */\n  var objectTypes = {\n    'function': true,\n    'object': true\n  };\n\n  /** Used as a reference to the global object. */\n  var root = (objectTypes[typeof window] && window) || this;\n\n  /** Detect free variable `define`. */\n  var freeDefine = typeof define == 'function' && typeof define.amd == 'object' && define.amd && define;\n\n  /** Detect free variable `exports`. */\n  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;\n\n  /** Detect free variable `module`. */\n  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;\n\n  /** Detect free variable `global` from Node.js or Browserified code and use it as `root`. */\n  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;\n  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {\n    root = freeGlobal;\n  }\n\n  /** Detect free variable `require`. */\n  var freeRequire = typeof require == 'function' && require;\n\n  /** Used to assign each benchmark an incremented id. */\n  var counter = 0;\n\n  /** Detect the popular CommonJS extension `module.exports`. */\n  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;\n\n  /** Used to detect primitive types. */\n  var rePrimitive = /^(?:boolean|number|string|undefined)$/;\n\n  /** Used to make every compiled test unique. */\n  var uidCounter = 0;\n\n  /** Used to assign default `context` object properties. */\n  var contextProps = [\n    'Array', 'Date', 'Function', 'Math', 'Object', 'RegExp', 'String', '_',\n    'clearTimeout', 'chrome', 'chromium', 'document', 'navigator', 'phantom',\n    'platform', 'process', 'runtime', 'setTimeout'\n  ];\n\n  /** Used to avoid hz of Infinity. */\n  var divisors = {\n    '1': 4096,\n    '2': 512,\n    '3': 64,\n    '4': 8,\n    '5': 0\n  };\n\n  /**\n   * T-Distribution two-tailed critical values for 95% confidence.\n   * For more info see http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm.\n   */\n  var tTable = {\n    '1':  12.706, '2':  4.303, '3':  3.182, '4':  2.776, '5':  2.571, '6':  2.447,\n    '7':  2.365,  '8':  2.306, '9':  2.262, '10': 2.228, '11': 2.201, '12': 2.179,\n    '13': 2.16,   '14': 2.145, '15': 2.131, '16': 2.12,  '17': 2.11,  '18': 2.101,\n    '19': 2.093,  '20': 2.086, '21': 2.08,  '22': 2.074, '23': 2.069, '24': 2.064,\n    '25': 2.06,   '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042,\n    'infinity': 1.96\n  };\n\n  /**\n   * Critical Mann-Whitney U-values for 95% confidence.\n   * For more info see http://www.saburchill.com/IBbiology/stats/003.html.\n   */\n  var uTable = {\n    '5':  [0, 1, 2],\n    '6':  [1, 2, 3, 5],\n    '7':  [1, 3, 5, 6, 8],\n    '8':  [2, 4, 6, 8, 10, 13],\n    '9':  [2, 4, 7, 10, 12, 15, 17],\n    '10': [3, 5, 8, 11, 14, 17, 20, 23],\n    '11': [3, 6, 9, 13, 16, 19, 23, 26, 30],\n    '12': [4, 7, 11, 14, 18, 22, 26, 29, 33, 37],\n    '13': [4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45],\n    '14': [5, 9, 13, 17, 22, 26, 31, 36, 40, 45, 50, 55],\n    '15': [5, 10, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64],\n    '16': [6, 11, 15, 21, 26, 31, 37, 42, 47, 53, 59, 64, 70, 75],\n    '17': [6, 11, 17, 22, 28, 34, 39, 45, 51, 57, 63, 67, 75, 81, 87],\n    '18': [7, 12, 18, 24, 30, 36, 42, 48, 55, 61, 67, 74, 80, 86, 93, 99],\n    '19': [7, 13, 19, 25, 32, 38, 45, 52, 58, 65, 72, 78, 85, 92, 99, 106, 113],\n    '20': [8, 14, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 98, 105, 112, 119, 127],\n    '21': [8, 15, 22, 29, 36, 43, 50, 58, 65, 73, 80, 88, 96, 103, 111, 119, 126, 134, 142],\n    '22': [9, 16, 23, 30, 38, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 133, 141, 150, 158],\n    '23': [9, 17, 24, 32, 40, 48, 56, 64, 73, 81, 89, 98, 106, 115, 123, 132, 140, 149, 157, 166, 175],\n    '24': [10, 17, 25, 33, 42, 50, 59, 67, 76, 85, 94, 102, 111, 120, 129, 138, 147, 156, 165, 174, 183, 192],\n    '25': [10, 18, 27, 35, 44, 53, 62, 71, 80, 89, 98, 107, 117, 126, 135, 145, 154, 163, 173, 182, 192, 201, 211],\n    '26': [11, 19, 28, 37, 46, 55, 64, 74, 83, 93, 102, 112, 122, 132, 141, 151, 161, 171, 181, 191, 200, 210, 220, 230],\n    '27': [11, 20, 29, 38, 48, 57, 67, 77, 87, 97, 107, 118, 125, 138, 147, 158, 168, 178, 188, 199, 209, 219, 230, 240, 250],\n    '28': [12, 21, 30, 40, 50, 60, 70, 80, 90, 101, 111, 122, 132, 143, 154, 164, 175, 186, 196, 207, 218, 228, 239, 250, 261, 272],\n    '29': [13, 22, 32, 42, 52, 62, 73, 83, 94, 105, 116, 127, 138, 149, 160, 171, 182, 193, 204, 215, 226, 238, 249, 260, 271, 282, 294],\n    '30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317]\n  };\n\n  /*--------------------------------------------------------------------------*/\n\n  /**\n   * Create a new `Benchmark` function using the given `context` object.\n   *\n   * @static\n   * @memberOf Benchmark\n   * @param {Object} [context=root] The context object.\n   * @returns {Function} Returns a new `Benchmark` function.\n   */\n  function runInContext(context) {\n    // Exit early if unable to acquire lodash.\n    var _ = context && context._ || req('lodash') || root._;\n    if (!_) {\n      Benchmark.runInContext = runInContext;\n      return Benchmark;\n    }\n    // Avoid issues with some ES3 environments that attempt to use values, named\n    // after built-in constructors like `Object`, for the creation of literals.\n    // ES5 clears this up by stating that literals must use built-in constructors.\n    // See http://es5.github.io/#x11.1.5.\n    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;\n\n    /** Native constructor references. */\n    var Array = context.Array,\n        Date = context.Date,\n        Function = context.Function,\n        Math = context.Math,\n        Object = context.Object,\n        RegExp = context.RegExp,\n        String = context.String;\n\n    /** Used for `Array` and `Object` method references. */\n    var arrayRef = [],\n        objectProto = Object.prototype;\n\n    /** Native method shortcuts. */\n    var abs = Math.abs,\n        clearTimeout = context.clearTimeout,\n        floor = Math.floor,\n        log = Math.log,\n        max = Math.max,\n        min = Math.min,\n        pow = Math.pow,\n        push = arrayRef.push,\n        setTimeout = context.setTimeout,\n        shift = arrayRef.shift,\n        slice = arrayRef.slice,\n        sqrt = Math.sqrt,\n        toString = objectProto.toString,\n        unshift = arrayRef.unshift;\n\n    /** Detect DOM document object. */\n    var doc = isHostType(context, 'document') && context.document;\n\n    /** Used to access Wade Simmons' Node.js `microtime` module. */\n    var microtimeObject = req('microtime');\n\n    /** Used to access Node.js's high resolution timer. */\n    var processObject = isHostType(context, 'process') && context.process;\n\n    /** Used to prevent a `removeChild` memory leak in IE < 9. */\n    var trash = doc && doc.createElement('div');\n\n    /** Used to integrity check compiled tests. */\n    var uid = 'uid' + _.now();\n\n    /** Used to avoid infinite recursion when methods call each other. */\n    var calledBy = {};\n\n    /**\n     * An object used to flag environments/features.\n     *\n     * @static\n     * @memberOf Benchmark\n     * @type Object\n     */\n    var support = {};\n\n    (function() {\n\n      /**\n       * Detect if running in a browser environment.\n       *\n       * @memberOf Benchmark.support\n       * @type boolean\n       */\n      support.browser = doc && isHostType(context, 'navigator') && !isHostType(context, 'phantom');\n\n      /**\n       * Detect if the Timers API exists.\n       *\n       * @memberOf Benchmark.support\n       * @type boolean\n       */\n      support.timeout = isHostType(context, 'setTimeout') && isHostType(context, 'clearTimeout');\n\n      /**\n       * Detect if function decompilation is support.\n       *\n       * @name decompilation\n       * @memberOf Benchmark.support\n       * @type boolean\n       */\n      try {\n        // Safari 2.x removes commas in object literals from `Function#toString` results.\n        // See http://webk.it/11609 for more details.\n        // Firefox 3.6 and Opera 9.25 strip grouping parentheses from `Function#toString` results.\n        // See http://bugzil.la/559438 for more details.\n        support.decompilation = Function(\n          ('return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')')\n          // Avoid issues with code added by Istanbul.\n          .replace(/__cov__[^;]+;/g, '')\n        )()(0).x === '1';\n      } catch(e) {\n        support.decompilation = false;\n      }\n    }());\n\n    /**\n     * Timer object used by `clock()` and `Deferred#resolve`.\n     *\n     * @private\n     * @type Object\n     */\n    var timer = {\n\n      /**\n       * The timer namespace object or constructor.\n       *\n       * @private\n       * @memberOf timer\n       * @type {Function|Object}\n       */\n      'ns': Date,\n\n      /**\n       * Starts the deferred timer.\n       *\n       * @private\n       * @memberOf timer\n       * @param {Object} deferred The deferred instance.\n       */\n      'start': null, // Lazy defined in `clock()`.\n\n      /**\n       * Stops the deferred timer.\n       *\n       * @private\n       * @memberOf timer\n       * @param {Object} deferred The deferred instance.\n       */\n      'stop': null // Lazy defined in `clock()`.\n    };\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * The Benchmark constructor.\n     *\n     * Note: The Benchmark constructor exposes a handful of lodash methods to\n     * make working with arrays, collections, and objects easier. The lodash\n     * methods are:\n     * [`each/forEach`](https://lodash.com/docs#forEach), [`forOwn`](https://lodash.com/docs#forOwn),\n     * [`has`](https://lodash.com/docs#has), [`indexOf`](https://lodash.com/docs#indexOf),\n     * [`map`](https://lodash.com/docs#map), and [`reduce`](https://lodash.com/docs#reduce)\n     *\n     * @constructor\n     * @param {string} name A name to identify the benchmark.\n     * @param {Function|string} fn The test to benchmark.\n     * @param {Object} [options={}] Options object.\n     * @example\n     *\n     * // basic usage (the `new` operator is optional)\n     * var bench = new Benchmark(fn);\n     *\n     * // or using a name first\n     * var bench = new Benchmark('foo', fn);\n     *\n     * // or with options\n     * var bench = new Benchmark('foo', fn, {\n     *\n     *   // displayed by `Benchmark#toString` if `name` is not available\n     *   'id': 'xyz',\n     *\n     *   // called when the benchmark starts running\n     *   'onStart': onStart,\n     *\n     *   // called after each run cycle\n     *   'onCycle': onCycle,\n     *\n     *   // called when aborted\n     *   'onAbort': onAbort,\n     *\n     *   // called when a test errors\n     *   'onError': onError,\n     *\n     *   // called when reset\n     *   'onReset': onReset,\n     *\n     *   // called when the benchmark completes running\n     *   'onComplete': onComplete,\n     *\n     *   // compiled/called before the test loop\n     *   'setup': setup,\n     *\n     *   // compiled/called after the test loop\n     *   'teardown': teardown\n     * });\n     *\n     * // or name and options\n     * var bench = new Benchmark('foo', {\n     *\n     *   // a flag to indicate the benchmark is deferred\n     *   'defer': true,\n     *\n     *   // benchmark test function\n     *   'fn': function(deferred) {\n     *     // call `Deferred#resolve` when the deferred test is finished\n     *     deferred.resolve();\n     *   }\n     * });\n     *\n     * // or options only\n     * var bench = new Benchmark({\n     *\n     *   // benchmark name\n     *   'name': 'foo',\n     *\n     *   // benchmark test as a string\n     *   'fn': '[1,2,3,4].sort()'\n     * });\n     *\n     * // a test's `this` binding is set to the benchmark instance\n     * var bench = new Benchmark('foo', function() {\n     *   'My name is '.concat(this.name); // \"My name is foo\"\n     * });\n     */\n    function Benchmark(name, fn, options) {\n      var bench = this;\n\n      // Allow instance creation without the `new` operator.\n      if (!(bench instanceof Benchmark)) {\n        return new Benchmark(name, fn, options);\n      }\n      // Juggle arguments.\n      if (_.isPlainObject(name)) {\n        // 1 argument (options).\n        options = name;\n      }\n      else if (_.isFunction(name)) {\n        // 2 arguments (fn, options).\n        options = fn;\n        fn = name;\n      }\n      else if (_.isPlainObject(fn)) {\n        // 2 arguments (name, options).\n        options = fn;\n        fn = null;\n        bench.name = name;\n      }\n      else {\n        // 3 arguments (name, fn [, options]).\n        bench.name = name;\n      }\n      setOptions(bench, options);\n\n      bench.id || (bench.id = ++counter);\n      bench.fn == null && (bench.fn = fn);\n\n      bench.stats = cloneDeep(bench.stats);\n      bench.times = cloneDeep(bench.times);\n    }\n\n    /**\n     * The Deferred constructor.\n     *\n     * @constructor\n     * @memberOf Benchmark\n     * @param {Object} clone The cloned benchmark instance.\n     */\n    function Deferred(clone) {\n      var deferred = this;\n      if (!(deferred instanceof Deferred)) {\n        return new Deferred(clone);\n      }\n      deferred.benchmark = clone;\n      clock(deferred);\n    }\n\n    /**\n     * The Event constructor.\n     *\n     * @constructor\n     * @memberOf Benchmark\n     * @param {Object|string} type The event type.\n     */\n    function Event(type) {\n      var event = this;\n      if (type instanceof Event) {\n        return type;\n      }\n      return (event instanceof Event)\n        ? _.assign(event, { 'timeStamp': _.now() }, typeof type == 'string' ? { 'type': type } : type)\n        : new Event(type);\n    }\n\n    /**\n     * The Suite constructor.\n     *\n     * Note: Each Suite instance has a handful of wrapped lodash methods to\n     * make working with Suites easier. The wrapped lodash methods are:\n     * [`each/forEach`](https://lodash.com/docs#forEach), [`indexOf`](https://lodash.com/docs#indexOf),\n     * [`map`](https://lodash.com/docs#map), and [`reduce`](https://lodash.com/docs#reduce)\n     *\n     * @constructor\n     * @memberOf Benchmark\n     * @param {string} name A name to identify the suite.\n     * @param {Object} [options={}] Options object.\n     * @example\n     *\n     * // basic usage (the `new` operator is optional)\n     * var suite = new Benchmark.Suite;\n     *\n     * // or using a name first\n     * var suite = new Benchmark.Suite('foo');\n     *\n     * // or with options\n     * var suite = new Benchmark.Suite('foo', {\n     *\n     *   // called when the suite starts running\n     *   'onStart': onStart,\n     *\n     *   // called between running benchmarks\n     *   'onCycle': onCycle,\n     *\n     *   // called when aborted\n     *   'onAbort': onAbort,\n     *\n     *   // called when a test errors\n     *   'onError': onError,\n     *\n     *   // called when reset\n     *   'onReset': onReset,\n     *\n     *   // called when the suite completes running\n     *   'onComplete': onComplete\n     * });\n     */\n    function Suite(name, options) {\n      var suite = this;\n\n      // Allow instance creation without the `new` operator.\n      if (!(suite instanceof Suite)) {\n        return new Suite(name, options);\n      }\n      // Juggle arguments.\n      if (_.isPlainObject(name)) {\n        // 1 argument (options).\n        options = name;\n      } else {\n        // 2 arguments (name [, options]).\n        suite.name = name;\n      }\n      setOptions(suite, options);\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * A specialized version of `_.cloneDeep` which only clones arrays and plain\n     * objects assigning all other values by reference.\n     *\n     * @private\n     * @param {*} value The value to clone.\n     * @returns {*} The cloned value.\n     */\n    var cloneDeep = _.partial(_.cloneDeepWith, _, function(value) {\n      // Only clone primitives, arrays, and plain objects.\n      return (_.isObject(value) && !_.isArray(value) && !_.isPlainObject(value))\n        ? value\n        : undefined;\n    });\n\n    /**\n     * Creates a function from the given arguments string and body.\n     *\n     * @private\n     * @param {string} args The comma separated function arguments.\n     * @param {string} body The function body.\n     * @returns {Function} The new function.\n     */\n    function createFunction() {\n      // Lazy define.\n      createFunction = function(args, body) {\n        var result,\n            anchor = freeDefine ? freeDefine.amd : Benchmark,\n            prop = uid + 'createFunction';\n\n        runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}');\n        result = anchor[prop];\n        delete anchor[prop];\n        return result;\n      };\n      // Fix JaegerMonkey bug.\n      // For more information see http://bugzil.la/639720.\n      createFunction = support.browser && (createFunction('', 'return\"' + uid + '\"') || _.noop)() == uid ? createFunction : Function;\n      return createFunction.apply(null, arguments);\n    }\n\n    /**\n     * Delay the execution of a function based on the benchmark's `delay` property.\n     *\n     * @private\n     * @param {Object} bench The benchmark instance.\n     * @param {Object} fn The function to execute.\n     */\n    function delay(bench, fn) {\n      bench._timerId = _.delay(fn, bench.delay * 1e3);\n    }\n\n    /**\n     * Destroys the given element.\n     *\n     * @private\n     * @param {Element} element The element to destroy.\n     */\n    function destroyElement(element) {\n      trash.appendChild(element);\n      trash.innerHTML = '';\n    }\n\n    /**\n     * Gets the name of the first argument from a function's source.\n     *\n     * @private\n     * @param {Function} fn The function.\n     * @returns {string} The argument name.\n     */\n    function getFirstArgument(fn) {\n      return (!_.has(fn, 'toString') &&\n        (/^[\\s(]*function[^(]*\\(([^\\s,)]+)/.exec(fn) || 0)[1]) || '';\n    }\n\n    /**\n     * Computes the arithmetic mean of a sample.\n     *\n     * @private\n     * @param {Array} sample The sample.\n     * @returns {number} The mean.\n     */\n    function getMean(sample) {\n      return (_.reduce(sample, function(sum, x) {\n        return sum + x;\n      }) / sample.length) || 0;\n    }\n\n    /**\n     * Gets the source code of a function.\n     *\n     * @private\n     * @param {Function} fn The function.\n     * @returns {string} The function's source code.\n     */\n    function getSource(fn) {\n      var result = '';\n      if (isStringable(fn)) {\n        result = String(fn);\n      } else if (support.decompilation) {\n        // Escape the `{` for Firefox 1.\n        result = _.result(/^[^{]+\\{([\\s\\S]*)\\}\\s*$/.exec(fn), 1);\n      }\n      // Trim string.\n      result = (result || '').replace(/^\\s+|\\s+$/g, '');\n\n      // Detect strings containing only the \"use strict\" directive.\n      return /^(?:\\/\\*+[\\w\\W]*?\\*\\/|\\/\\/.*?[\\n\\r\\u2028\\u2029]|\\s)*([\"'])use strict\\1;?$/.test(result)\n        ? ''\n        : result;\n    }\n\n    /**\n     * Checks if an object is of the specified class.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @param {string} name The name of the class.\n     * @returns {boolean} Returns `true` if the value is of the specified class, else `false`.\n     */\n    function isClassOf(value, name) {\n      return value != null && toString.call(value) == '[object ' + name + ']';\n    }\n\n    /**\n     * Host objects can return type values that are different from their actual\n     * data type. The objects we are concerned with usually return non-primitive\n     * types of \"object\", \"function\", or \"unknown\".\n     *\n     * @private\n     * @param {*} object The owner of the property.\n     * @param {string} property The property to check.\n     * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`.\n     */\n    function isHostType(object, property) {\n      if (object == null) {\n        return false;\n      }\n      var type = typeof object[property];\n      return !rePrimitive.test(type) && (type != 'object' || !!object[property]);\n    }\n\n    /**\n     * Checks if a value can be safely coerced to a string.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if the value can be coerced, else `false`.\n     */\n    function isStringable(value) {\n      return _.isString(value) || (_.has(value, 'toString') && _.isFunction(value.toString));\n    }\n\n    /**\n     * A wrapper around `require` to suppress `module missing` errors.\n     *\n     * @private\n     * @param {string} id The module id.\n     * @returns {*} The exported module or `null`.\n     */\n    function req(id) {\n      try {\n        var result = freeExports && freeRequire(id);\n      } catch(e) {}\n      return result || null;\n    }\n\n    /**\n     * Runs a snippet of JavaScript via script injection.\n     *\n     * @private\n     * @param {string} code The code to run.\n     */\n    function runScript(code) {\n      var anchor = freeDefine ? define.amd : Benchmark,\n          script = doc.createElement('script'),\n          sibling = doc.getElementsByTagName('script')[0],\n          parent = sibling.parentNode,\n          prop = uid + 'runScript',\n          prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();';\n\n      // Firefox 2.0.0.2 cannot use script injection as intended because it executes\n      // asynchronously, but that's OK because script injection is only used to avoid\n      // the previously commented JaegerMonkey bug.\n      try {\n        // Remove the inserted script *before* running the code to avoid differences\n        // in the expected script element count/order of the document.\n        script.appendChild(doc.createTextNode(prefix + code));\n        anchor[prop] = function() { destroyElement(script); };\n      } catch(e) {\n        parent = parent.cloneNode(false);\n        sibling = null;\n        script.text = code;\n      }\n      parent.insertBefore(script, sibling);\n      delete anchor[prop];\n    }\n\n    /**\n     * A helper function for setting options/event handlers.\n     *\n     * @private\n     * @param {Object} object The benchmark or suite instance.\n     * @param {Object} [options={}] Options object.\n     */\n    function setOptions(object, options) {\n      options = object.options = _.assign({}, cloneDeep(object.constructor.options), cloneDeep(options));\n\n      _.forOwn(options, function(value, key) {\n        if (value != null) {\n          // Add event listeners.\n          if (/^on[A-Z]/.test(key)) {\n            _.each(key.split(' '), function(key) {\n              object.on(key.slice(2).toLowerCase(), value);\n            });\n          } else if (!_.has(object, key)) {\n            object[key] = cloneDeep(value);\n          }\n        }\n      });\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Handles cycling/completing the deferred benchmark.\n     *\n     * @memberOf Benchmark.Deferred\n     */\n    function resolve() {\n      var deferred = this,\n          clone = deferred.benchmark,\n          bench = clone._original;\n\n      if (bench.aborted) {\n        // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete.\n        deferred.teardown();\n        clone.running = false;\n        cycle(deferred);\n      }\n      else if (++deferred.cycles < clone.count) {\n        clone.compiled.call(deferred, context, timer);\n      }\n      else {\n        timer.stop(deferred);\n        deferred.teardown();\n        delay(clone, function() { cycle(deferred); });\n      }\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * A generic `Array#filter` like method.\n     *\n     * @static\n     * @memberOf Benchmark\n     * @param {Array} array The array to iterate over.\n     * @param {Function|string} callback The function/alias called per iteration.\n     * @returns {Array} A new array of values that passed callback filter.\n     * @example\n     *\n     * // get odd numbers\n     * Benchmark.filter([1, 2, 3, 4, 5], function(n) {\n     *   return n % 2;\n     * }); // -> [1, 3, 5];\n     *\n     * // get fastest benchmarks\n     * Benchmark.filter(benches, 'fastest');\n     *\n     * // get slowest benchmarks\n     * Benchmark.filter(benches, 'slowest');\n     *\n     * // get benchmarks that completed without erroring\n     * Benchmark.filter(benches, 'successful');\n     */\n    function filter(array, callback) {\n      if (callback === 'successful') {\n        // Callback to exclude those that are errored, unrun, or have hz of Infinity.\n        callback = function(bench) {\n          return bench.cycles && _.isFinite(bench.hz) && !bench.error;\n        };\n      }\n      else if (callback === 'fastest' || callback === 'slowest') {\n        // Get successful, sort by period + margin of error, and filter fastest/slowest.\n        var result = filter(array, 'successful').sort(function(a, b) {\n          a = a.stats; b = b.stats;\n          return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback === 'fastest' ? 1 : -1);\n        });\n\n        return _.filter(result, function(bench) {\n          return result[0].compare(bench) == 0;\n        });\n      }\n      return _.filter(array, callback);\n    }\n\n    /**\n     * Converts a number to a more readable comma-separated string representation.\n     *\n     * @static\n     * @memberOf Benchmark\n     * @param {number} number The number to convert.\n     * @returns {string} The more readable string representation.\n     */\n    function formatNumber(number) {\n      number = String(number).split('.');\n      return number[0].replace(/(?=(?:\\d{3})+$)(?!\\b)/g, ',') +\n        (number[1] ? '.' + number[1] : '');\n    }\n\n    /**\n     * Invokes a method on all items in an array.\n     *\n     * @static\n     * @memberOf Benchmark\n     * @param {Array} benches Array of benchmarks to iterate over.\n     * @param {Object|string} name The name of the method to invoke OR options object.\n     * @param {...*} [args] Arguments to invoke the method with.\n     * @returns {Array} A new array of values returned from each method invoked.\n     * @example\n     *\n     * // invoke `reset` on all benchmarks\n     * Benchmark.invoke(benches, 'reset');\n     *\n     * // invoke `emit` with arguments\n     * Benchmark.invoke(benches, 'emit', 'complete', listener);\n     *\n     * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks\n     * Benchmark.invoke(benches, {\n     *\n     *   // invoke the `run` method\n     *   'name': 'run',\n     *\n     *   // pass a single argument\n     *   'args': true,\n     *\n     *   // treat as queue, removing benchmarks from front of `benches` until empty\n     *   'queued': true,\n     *\n     *   // called before any benchmarks have been invoked.\n     *   'onStart': onStart,\n     *\n     *   // called between invoking benchmarks\n     *   'onCycle': onCycle,\n     *\n     *   // called after all benchmarks have been invoked.\n     *   'onComplete': onComplete\n     * });\n     */\n    function invoke(benches, name) {\n      var args,\n          bench,\n          queued,\n          index = -1,\n          eventProps = { 'currentTarget': benches },\n          options = { 'onStart': _.noop, 'onCycle': _.noop, 'onComplete': _.noop },\n          result = _.toArray(benches);\n\n      /**\n       * Invokes the method of the current object and if synchronous, fetches the next.\n       */\n      function execute() {\n        var listeners,\n            async = isAsync(bench);\n\n        if (async) {\n          // Use `getNext` as the first listener.\n          bench.on('complete', getNext);\n          listeners = bench.events.complete;\n          listeners.splice(0, 0, listeners.pop());\n        }\n        // Execute method.\n        result[index] = _.isFunction(bench && bench[name]) ? bench[name].apply(bench, args) : undefined;\n        // If synchronous return `true` until finished.\n        return !async && getNext();\n      }\n\n      /**\n       * Fetches the next bench or executes `onComplete` callback.\n       */\n      function getNext(event) {\n        var cycleEvent,\n            last = bench,\n            async = isAsync(last);\n\n        if (async) {\n          last.off('complete', getNext);\n          last.emit('complete');\n        }\n        // Emit \"cycle\" event.\n        eventProps.type = 'cycle';\n        eventProps.target = last;\n        cycleEvent = Event(eventProps);\n        options.onCycle.call(benches, cycleEvent);\n\n        // Choose next benchmark if not exiting early.\n        if (!cycleEvent.aborted && raiseIndex() !== false) {\n          bench = queued ? benches[0] : result[index];\n          if (isAsync(bench)) {\n            delay(bench, execute);\n          }\n          else if (async) {\n            // Resume execution if previously asynchronous but now synchronous.\n            while (execute()) {}\n          }\n          else {\n            // Continue synchronous execution.\n            return true;\n          }\n        } else {\n          // Emit \"complete\" event.\n          eventProps.type = 'complete';\n          options.onComplete.call(benches, Event(eventProps));\n        }\n        // When used as a listener `event.aborted = true` will cancel the rest of\n        // the \"complete\" listeners because they were already called above and when\n        // used as part of `getNext` the `return false` will exit the execution while-loop.\n        if (event) {\n          event.aborted = true;\n        } else {\n          return false;\n        }\n      }\n\n      /**\n       * Checks if invoking `Benchmark#run` with asynchronous cycles.\n       */\n      function isAsync(object) {\n        // Avoid using `instanceof` here because of IE memory leak issues with host objects.\n        var async = args[0] && args[0].async;\n        return name == 'run' && (object instanceof Benchmark) &&\n          ((async == null ? object.options.async : async) && support.timeout || object.defer);\n      }\n\n      /**\n       * Raises `index` to the next defined index or returns `false`.\n       */\n      function raiseIndex() {\n        index++;\n\n        // If queued remove the previous bench.\n        if (queued && index > 0) {\n          shift.call(benches);\n        }\n        // If we reached the last index then return `false`.\n        return (queued ? benches.length : index < result.length)\n          ? index\n          : (index = false);\n      }\n      // Juggle arguments.\n      if (_.isString(name)) {\n        // 2 arguments (array, name).\n        args = slice.call(arguments, 2);\n      } else {\n        // 2 arguments (array, options).\n        options = _.assign(options, name);\n        name = options.name;\n        args = _.isArray(args = 'args' in options ? options.args : []) ? args : [args];\n        queued = options.queued;\n      }\n      // Start iterating over the array.\n      if (raiseIndex() !== false) {\n        // Emit \"start\" event.\n        bench = result[index];\n        eventProps.type = 'start';\n        eventProps.target = bench;\n        options.onStart.call(benches, Event(eventProps));\n\n        // End early if the suite was aborted in an \"onStart\" listener.\n        if (name == 'run' && (benches instanceof Suite) && benches.aborted) {\n          // Emit \"cycle\" event.\n          eventProps.type = 'cycle';\n          options.onCycle.call(benches, Event(eventProps));\n          // Emit \"complete\" event.\n          eventProps.type = 'complete';\n          options.onComplete.call(benches, Event(eventProps));\n        }\n        // Start method execution.\n        else {\n          if (isAsync(bench)) {\n            delay(bench, execute);\n          } else {\n            while (execute()) {}\n          }\n        }\n      }\n      return result;\n    }\n\n    /**\n     * Creates a string of joined array values or object key-value pairs.\n     *\n     * @static\n     * @memberOf Benchmark\n     * @param {Array|Object} object The object to operate on.\n     * @param {string} [separator1=','] The separator used between key-value pairs.\n     * @param {string} [separator2=': '] The separator used between keys and values.\n     * @returns {string} The joined result.\n     */\n    function join(object, separator1, separator2) {\n      var result = [],\n          length = (object = Object(object)).length,\n          arrayLike = length === length >>> 0;\n\n      separator2 || (separator2 = ': ');\n      _.each(object, function(value, key) {\n        result.push(arrayLike ? value : key + separator2 + value);\n      });\n      return result.join(separator1 || ',');\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Aborts all benchmarks in the suite.\n     *\n     * @name abort\n     * @memberOf Benchmark.Suite\n     * @returns {Object} The suite instance.\n     */\n    function abortSuite() {\n      var event,\n          suite = this,\n          resetting = calledBy.resetSuite;\n\n      if (suite.running) {\n        event = Event('abort');\n        suite.emit(event);\n        if (!event.cancelled || resetting) {\n          // Avoid infinite recursion.\n          calledBy.abortSuite = true;\n          suite.reset();\n          delete calledBy.abortSuite;\n\n          if (!resetting) {\n            suite.aborted = true;\n            invoke(suite, 'abort');\n          }\n        }\n      }\n      return suite;\n    }\n\n    /**\n     * Adds a test to the benchmark suite.\n     *\n     * @memberOf Benchmark.Suite\n     * @param {string} name A name to identify the benchmark.\n     * @param {Function|string} fn The test to benchmark.\n     * @param {Object} [options={}] Options object.\n     * @returns {Object} The benchmark instance.\n     * @example\n     *\n     * // basic usage\n     * suite.add(fn);\n     *\n     * // or using a name first\n     * suite.add('foo', fn);\n     *\n     * // or with options\n     * suite.add('foo', fn, {\n     *   'onCycle': onCycle,\n     *   'onComplete': onComplete\n     * });\n     *\n     * // or name and options\n     * suite.add('foo', {\n     *   'fn': fn,\n     *   'onCycle': onCycle,\n     *   'onComplete': onComplete\n     * });\n     *\n     * // or options only\n     * suite.add({\n     *   'name': 'foo',\n     *   'fn': fn,\n     *   'onCycle': onCycle,\n     *   'onComplete': onComplete\n     * });\n     */\n    function add(name, fn, options) {\n      var suite = this,\n          bench = new Benchmark(name, fn, options),\n          event = Event({ 'type': 'add', 'target': bench });\n\n      if (suite.emit(event), !event.cancelled) {\n        suite.push(bench);\n      }\n      return suite;\n    }\n\n    /**\n     * Creates a new suite with cloned benchmarks.\n     *\n     * @name clone\n     * @memberOf Benchmark.Suite\n     * @param {Object} options Options object to overwrite cloned options.\n     * @returns {Object} The new suite instance.\n     */\n    function cloneSuite(options) {\n      var suite = this,\n          result = new suite.constructor(_.assign({}, suite.options, options));\n\n      // Copy own properties.\n      _.forOwn(suite, function(value, key) {\n        if (!_.has(result, key)) {\n          result[key] = value && _.isFunction(value.clone)\n            ? value.clone()\n            : cloneDeep(value);\n        }\n      });\n      return result;\n    }\n\n    /**\n     * An `Array#filter` like method.\n     *\n     * @name filter\n     * @memberOf Benchmark.Suite\n     * @param {Function|string} callback The function/alias called per iteration.\n     * @returns {Object} A new suite of benchmarks that passed callback filter.\n     */\n    function filterSuite(callback) {\n      var suite = this,\n          result = new suite.constructor(suite.options);\n\n      result.push.apply(result, filter(suite, callback));\n      return result;\n    }\n\n    /**\n     * Resets all benchmarks in the suite.\n     *\n     * @name reset\n     * @memberOf Benchmark.Suite\n     * @returns {Object} The suite instance.\n     */\n    function resetSuite() {\n      var event,\n          suite = this,\n          aborting = calledBy.abortSuite;\n\n      if (suite.running && !aborting) {\n        // No worries, `resetSuite()` is called within `abortSuite()`.\n        calledBy.resetSuite = true;\n        suite.abort();\n        delete calledBy.resetSuite;\n      }\n      // Reset if the state has changed.\n      else if ((suite.aborted || suite.running) &&\n          (suite.emit(event = Event('reset')), !event.cancelled)) {\n        suite.aborted = suite.running = false;\n        if (!aborting) {\n          invoke(suite, 'reset');\n        }\n      }\n      return suite;\n    }\n\n    /**\n     * Runs the suite.\n     *\n     * @name run\n     * @memberOf Benchmark.Suite\n     * @param {Object} [options={}] Options object.\n     * @returns {Object} The suite instance.\n     * @example\n     *\n     * // basic usage\n     * suite.run();\n     *\n     * // or with options\n     * suite.run({ 'async': true, 'queued': true });\n     */\n    function runSuite(options) {\n      var suite = this;\n\n      suite.reset();\n      suite.running = true;\n      options || (options = {});\n\n      invoke(suite, {\n        'name': 'run',\n        'args': options,\n        'queued': options.queued,\n        'onStart': function(event) {\n          suite.emit(event);\n        },\n        'onCycle': function(event) {\n          var bench = event.target;\n          if (bench.error) {\n            suite.emit({ 'type': 'error', 'target': bench });\n          }\n          suite.emit(event);\n          event.aborted = suite.aborted;\n        },\n        'onComplete': function(event) {\n          suite.running = false;\n          suite.emit(event);\n        }\n      });\n      return suite;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Executes all registered listeners of the specified event type.\n     *\n     * @memberOf Benchmark, Benchmark.Suite\n     * @param {Object|string} type The event type or object.\n     * @param {...*} [args] Arguments to invoke the listener with.\n     * @returns {*} Returns the return value of the last listener executed.\n     */\n    function emit(type) {\n      var listeners,\n          object = this,\n          event = Event(type),\n          events = object.events,\n          args = (arguments[0] = event, arguments);\n\n      event.currentTarget || (event.currentTarget = object);\n      event.target || (event.target = object);\n      delete event.result;\n\n      if (events && (listeners = _.has(events, event.type) && events[event.type])) {\n        _.each(listeners.slice(), function(listener) {\n          if ((event.result = listener.apply(object, args)) === false) {\n            event.cancelled = true;\n          }\n          return !event.aborted;\n        });\n      }\n      return event.result;\n    }\n\n    /**\n     * Returns an array of event listeners for a given type that can be manipulated\n     * to add or remove listeners.\n     *\n     * @memberOf Benchmark, Benchmark.Suite\n     * @param {string} type The event type.\n     * @returns {Array} The listeners array.\n     */\n    function listeners(type) {\n      var object = this,\n          events = object.events || (object.events = {});\n\n      return _.has(events, type) ? events[type] : (events[type] = []);\n    }\n\n    /**\n     * Unregisters a listener for the specified event type(s),\n     * or unregisters all listeners for the specified event type(s),\n     * or unregisters all listeners for all event types.\n     *\n     * @memberOf Benchmark, Benchmark.Suite\n     * @param {string} [type] The event type.\n     * @param {Function} [listener] The function to unregister.\n     * @returns {Object} The benchmark instance.\n     * @example\n     *\n     * // unregister a listener for an event type\n     * bench.off('cycle', listener);\n     *\n     * // unregister a listener for multiple event types\n     * bench.off('start cycle', listener);\n     *\n     * // unregister all listeners for an event type\n     * bench.off('cycle');\n     *\n     * // unregister all listeners for multiple event types\n     * bench.off('start cycle complete');\n     *\n     * // unregister all listeners for all event types\n     * bench.off();\n     */\n    function off(type, listener) {\n      var object = this,\n          events = object.events;\n\n      if (!events) {\n        return object;\n      }\n      _.each(type ? type.split(' ') : events, function(listeners, type) {\n        var index;\n        if (typeof listeners == 'string') {\n          type = listeners;\n          listeners = _.has(events, type) && events[type];\n        }\n        if (listeners) {\n          if (listener) {\n            index = _.indexOf(listeners, listener);\n            if (index > -1) {\n              listeners.splice(index, 1);\n            }\n          } else {\n            listeners.length = 0;\n          }\n        }\n      });\n      return object;\n    }\n\n    /**\n     * Registers a listener for the specified event type(s).\n     *\n     * @memberOf Benchmark, Benchmark.Suite\n     * @param {string} type The event type.\n     * @param {Function} listener The function to register.\n     * @returns {Object} The benchmark instance.\n     * @example\n     *\n     * // register a listener for an event type\n     * bench.on('cycle', listener);\n     *\n     * // register a listener for multiple event types\n     * bench.on('start cycle', listener);\n     */\n    function on(type, listener) {\n      var object = this,\n          events = object.events || (object.events = {});\n\n      _.each(type.split(' '), function(type) {\n        (_.has(events, type)\n          ? events[type]\n          : (events[type] = [])\n        ).push(listener);\n      });\n      return object;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Aborts the benchmark without recording times.\n     *\n     * @memberOf Benchmark\n     * @returns {Object} The benchmark instance.\n     */\n    function abort() {\n      var event,\n          bench = this,\n          resetting = calledBy.reset;\n\n      if (bench.running) {\n        event = Event('abort');\n        bench.emit(event);\n        if (!event.cancelled || resetting) {\n          // Avoid infinite recursion.\n          calledBy.abort = true;\n          bench.reset();\n          delete calledBy.abort;\n\n          if (support.timeout) {\n            clearTimeout(bench._timerId);\n            delete bench._timerId;\n          }\n          if (!resetting) {\n            bench.aborted = true;\n            bench.running = false;\n          }\n        }\n      }\n      return bench;\n    }\n\n    /**\n     * Creates a new benchmark using the same test and options.\n     *\n     * @memberOf Benchmark\n     * @param {Object} options Options object to overwrite cloned options.\n     * @returns {Object} The new benchmark instance.\n     * @example\n     *\n     * var bizarro = bench.clone({\n     *   'name': 'doppelganger'\n     * });\n     */\n    function clone(options) {\n      var bench = this,\n          result = new bench.constructor(_.assign({}, bench, options));\n\n      // Correct the `options` object.\n      result.options = _.assign({}, cloneDeep(bench.options), cloneDeep(options));\n\n      // Copy own custom properties.\n      _.forOwn(bench, function(value, key) {\n        if (!_.has(result, key)) {\n          result[key] = cloneDeep(value);\n        }\n      });\n\n      return result;\n    }\n\n    /**\n     * Determines if a benchmark is faster than another.\n     *\n     * @memberOf Benchmark\n     * @param {Object} other The benchmark to compare.\n     * @returns {number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate.\n     */\n    function compare(other) {\n      var bench = this;\n\n      // Exit early if comparing the same benchmark.\n      if (bench == other) {\n        return 0;\n      }\n      var critical,\n          zStat,\n          sample1 = bench.stats.sample,\n          sample2 = other.stats.sample,\n          size1 = sample1.length,\n          size2 = sample2.length,\n          maxSize = max(size1, size2),\n          minSize = min(size1, size2),\n          u1 = getU(sample1, sample2),\n          u2 = getU(sample2, sample1),\n          u = min(u1, u2);\n\n      function getScore(xA, sampleB) {\n        return _.reduce(sampleB, function(total, xB) {\n          return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5);\n        }, 0);\n      }\n\n      function getU(sampleA, sampleB) {\n        return _.reduce(sampleA, function(total, xA) {\n          return total + getScore(xA, sampleB);\n        }, 0);\n      }\n\n      function getZ(u) {\n        return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12);\n      }\n      // Reject the null hypothesis the two samples come from the\n      // same population (i.e. have the same median) if...\n      if (size1 + size2 > 30) {\n        // ...the z-stat is greater than 1.96 or less than -1.96\n        // http://www.statisticslectures.com/topics/mannwhitneyu/\n        zStat = getZ(u);\n        return abs(zStat) > 1.96 ? (u == u1 ? 1 : -1) : 0;\n      }\n      // ...the U value is less than or equal the critical U value.\n      critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3];\n      return u <= critical ? (u == u1 ? 1 : -1) : 0;\n    }\n\n    /**\n     * Reset properties and abort if running.\n     *\n     * @memberOf Benchmark\n     * @returns {Object} The benchmark instance.\n     */\n    function reset() {\n      var bench = this;\n      if (bench.running && !calledBy.abort) {\n        // No worries, `reset()` is called within `abort()`.\n        calledBy.reset = true;\n        bench.abort();\n        delete calledBy.reset;\n        return bench;\n      }\n      var event,\n          index = 0,\n          changes = [],\n          queue = [];\n\n      // A non-recursive solution to check if properties have changed.\n      // For more information see http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4.\n      var data = {\n        'destination': bench,\n        'source': _.assign({}, cloneDeep(bench.constructor.prototype), cloneDeep(bench.options))\n      };\n\n      do {\n        _.forOwn(data.source, function(value, key) {\n          var changed,\n              destination = data.destination,\n              currValue = destination[key];\n\n          // Skip pseudo private properties like `_timerId` which could be a\n          // Java object in environments like RingoJS.\n          if (key.charAt(0) == '_') {\n            return;\n          }\n          if (value && typeof value == 'object') {\n            if (_.isArray(value)) {\n              // Check if an array value has changed to a non-array value.\n              if (!_.isArray(currValue)) {\n                changed = currValue = [];\n              }\n              // Check if an array has changed its length.\n              if (currValue.length != value.length) {\n                changed = currValue = currValue.slice(0, value.length);\n                currValue.length = value.length;\n              }\n            }\n            // Check if an object has changed to a non-object value.\n            else if (!currValue || typeof currValue != 'object') {\n              changed = currValue = {};\n            }\n            // Register a changed object.\n            if (changed) {\n              changes.push({ 'destination': destination, 'key': key, 'value': currValue });\n            }\n            queue.push({ 'destination': currValue, 'source': value });\n          }\n          // Register a changed primitive.\n          else if (value !== currValue && !(value == null || _.isFunction(value))) {\n            changes.push({ 'destination': destination, 'key': key, 'value': value });\n          }\n        });\n      }\n      while ((data = queue[index++]));\n\n      // If changed emit the `reset` event and if it isn't cancelled reset the benchmark.\n      if (changes.length && (bench.emit(event = Event('reset')), !event.cancelled)) {\n        _.each(changes, function(data) {\n          data.destination[data.key] = data.value;\n        });\n      }\n      return bench;\n    }\n\n    /**\n     * Displays relevant benchmark information when coerced to a string.\n     *\n     * @name toString\n     * @memberOf Benchmark\n     * @returns {string} A string representation of the benchmark instance.\n     */\n    function toStringBench() {\n      var bench = this,\n          error = bench.error,\n          hz = bench.hz,\n          id = bench.id,\n          stats = bench.stats,\n          size = stats.sample.length,\n          pm = '\\xb1',\n          result = bench.name || (_.isNaN(id) ? id : '<Test #' + id + '>');\n\n      if (error) {\n        result += ': ' + join(error);\n      } else {\n        result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +\n          stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';\n      }\n      return result;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Clocks the time taken to execute a test per cycle (secs).\n     *\n     * @private\n     * @param {Object} bench The benchmark instance.\n     * @returns {number} The time taken.\n     */\n    function clock() {\n      var options = Benchmark.options,\n          templateData = {},\n          timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }];\n\n      // Lazy define for hi-res timers.\n      clock = function(clone) {\n        var deferred;\n\n        if (clone instanceof Deferred) {\n          deferred = clone;\n          clone = deferred.benchmark;\n        }\n        var bench = clone._original,\n            stringable = isStringable(bench.fn),\n            count = bench.count = clone.count,\n            decompilable = stringable || (support.decompilation && (clone.setup !== _.noop || clone.teardown !== _.noop)),\n            id = bench.id,\n            name = bench.name || (typeof id == 'number' ? '<Test #' + id + '>' : id),\n            result = 0;\n\n        // Init `minTime` if needed.\n        clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime);\n\n        // Compile in setup/teardown functions and the test loop.\n        // Create a new compiled test, instead of using the cached `bench.compiled`,\n        // to avoid potential engine optimizations enabled over the life of the test.\n        var funcBody = deferred\n          ? 'var d#=this,${fnArg}=d#,m#=d#.benchmark._original,f#=m#.fn,su#=m#.setup,td#=m#.teardown;' +\n            // When `deferred.cycles` is `0` then...\n            'if(!d#.cycles){' +\n            // set `deferred.fn`,\n            'd#.fn=function(){var ${fnArg}=d#;if(typeof f#==\"function\"){try{${fn}\\n}catch(e#){f#(d#)}}else{${fn}\\n}};' +\n            // set `deferred.teardown`,\n            'd#.teardown=function(){d#.cycles=0;if(typeof td#==\"function\"){try{${teardown}\\n}catch(e#){td#()}}else{${teardown}\\n}};' +\n            // execute the benchmark's `setup`,\n            'if(typeof su#==\"function\"){try{${setup}\\n}catch(e#){su#()}}else{${setup}\\n};' +\n            // start timer,\n            't#.start(d#);' +\n            // and then execute `deferred.fn` and return a dummy object.\n            '}d#.fn();return{uid:\"${uid}\"}'\n\n          : 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count,n#=t#.ns;${setup}\\n${begin};' +\n            'while(i#--){${fn}\\n}${end};${teardown}\\nreturn{elapsed:r#,uid:\"${uid}\"}';\n\n        var compiled = bench.compiled = clone.compiled = createCompiled(bench, decompilable, deferred, funcBody),\n            isEmpty = !(templateData.fn || stringable);\n\n        try {\n          if (isEmpty) {\n            // Firefox may remove dead code from `Function#toString` results.\n            // For more information see http://bugzil.la/536085.\n            throw new Error('The test \"' + name + '\" is empty. This may be the result of dead code removal.');\n          }\n          else if (!deferred) {\n            // Pretest to determine if compiled code exits early, usually by a\n            // rogue `return` statement, by checking for a return object with the uid.\n            bench.count = 1;\n            compiled = decompilable && (compiled.call(bench, context, timer) || {}).uid == templateData.uid && compiled;\n            bench.count = count;\n          }\n        } catch(e) {\n          compiled = null;\n          clone.error = e || new Error(String(e));\n          bench.count = count;\n        }\n        // Fallback when a test exits early or errors during pretest.\n        if (!compiled && !deferred && !isEmpty) {\n          funcBody = (\n            stringable || (decompilable && !clone.error)\n              ? 'function f#(){${fn}\\n}var r#,s#,m#=this,i#=m#.count'\n              : 'var r#,s#,m#=this,f#=m#.fn,i#=m#.count'\n            ) +\n            ',n#=t#.ns;${setup}\\n${begin};m#.f#=f#;while(i#--){m#.f#()}${end};' +\n            'delete m#.f#;${teardown}\\nreturn{elapsed:r#}';\n\n          compiled = createCompiled(bench, decompilable, deferred, funcBody);\n\n          try {\n            // Pretest one more time to check for errors.\n            bench.count = 1;\n            compiled.call(bench, context, timer);\n            bench.count = count;\n            delete clone.error;\n          }\n          catch(e) {\n            bench.count = count;\n            if (!clone.error) {\n              clone.error = e || new Error(String(e));\n            }\n          }\n        }\n        // If no errors run the full test loop.\n        if (!clone.error) {\n          compiled = bench.compiled = clone.compiled = createCompiled(bench, decompilable, deferred, funcBody);\n          result = compiled.call(deferred || bench, context, timer).elapsed;\n        }\n        return result;\n      };\n\n      /*----------------------------------------------------------------------*/\n\n      /**\n       * Creates a compiled function from the given function `body`.\n       */\n      function createCompiled(bench, decompilable, deferred, body) {\n        var fn = bench.fn,\n            fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '';\n\n        templateData.uid = uid + uidCounter++;\n\n        _.assign(templateData, {\n          'setup': decompilable ? getSource(bench.setup) : interpolate('m#.setup()'),\n          'fn': decompilable ? getSource(fn) : interpolate('m#.fn(' + fnArg + ')'),\n          'fnArg': fnArg,\n          'teardown': decompilable ? getSource(bench.teardown) : interpolate('m#.teardown()')\n        });\n\n        // Use API of chosen timer.\n        if (timer.unit == 'ns') {\n          _.assign(templateData, {\n            'begin': interpolate('s#=n#()'),\n            'end': interpolate('r#=n#(s#);r#=r#[0]+(r#[1]/1e9)')\n          });\n        }\n        else if (timer.unit == 'us') {\n          if (timer.ns.stop) {\n            _.assign(templateData, {\n              'begin': interpolate('s#=n#.start()'),\n              'end': interpolate('r#=n#.microseconds()/1e6')\n            });\n          } else {\n            _.assign(templateData, {\n              'begin': interpolate('s#=n#()'),\n              'end': interpolate('r#=(n#()-s#)/1e6')\n            });\n          }\n        }\n        else if (timer.ns.now) {\n          _.assign(templateData, {\n            'begin': interpolate('s#=n#.now()'),\n            'end': interpolate('r#=(n#.now()-s#)/1e3')\n          });\n        }\n        else {\n          _.assign(templateData, {\n            'begin': interpolate('s#=new n#().getTime()'),\n            'end': interpolate('r#=(new n#().getTime()-s#)/1e3')\n          });\n        }\n        // Define `timer` methods.\n        timer.start = createFunction(\n          interpolate('o#'),\n          interpolate('var n#=this.ns,${begin};o#.elapsed=0;o#.timeStamp=s#')\n        );\n\n        timer.stop = createFunction(\n          interpolate('o#'),\n          interpolate('var n#=this.ns,s#=o#.timeStamp,${end};o#.elapsed=r#')\n        );\n\n        // Create compiled test.\n        return createFunction(\n          interpolate('window,t#'),\n          'var global = window, clearTimeout = global.clearTimeout, setTimeout = global.setTimeout;\\n' +\n          interpolate(body)\n        );\n      }\n\n      /**\n       * Gets the current timer's minimum resolution (secs).\n       */\n      function getRes(unit) {\n        var measured,\n            begin,\n            count = 30,\n            divisor = 1e3,\n            ns = timer.ns,\n            sample = [];\n\n        // Get average smallest measurable time.\n        while (count--) {\n          if (unit == 'us') {\n            divisor = 1e6;\n            if (ns.stop) {\n              ns.start();\n              while (!(measured = ns.microseconds())) {}\n            } else {\n              begin = ns();\n              while (!(measured = ns() - begin)) {}\n            }\n          }\n          else if (unit == 'ns') {\n            divisor = 1e9;\n            begin = (begin = ns())[0] + (begin[1] / divisor);\n            while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) {}\n            divisor = 1;\n          }\n          else if (ns.now) {\n            begin = ns.now();\n            while (!(measured = ns.now() - begin)) {}\n          }\n          else {\n            begin = new ns().getTime();\n            while (!(measured = new ns().getTime() - begin)) {}\n          }\n          // Check for broken timers.\n          if (measured > 0) {\n            sample.push(measured);\n          } else {\n            sample.push(Infinity);\n            break;\n          }\n        }\n        // Convert to seconds.\n        return getMean(sample) / divisor;\n      }\n\n      /**\n       * Interpolates a given template string.\n       */\n      function interpolate(string) {\n        // Replaces all occurrences of `#` with a unique number and template tokens with content.\n        return _.template(string.replace(/\\#/g, /\\d+/.exec(templateData.uid)))(templateData);\n      }\n\n      /*----------------------------------------------------------------------*/\n\n      // Detect Chrome's microsecond timer:\n      // enable benchmarking via the --enable-benchmarking command\n      // line switch in at least Chrome 7 to use chrome.Interval\n      try {\n        if ((timer.ns = new (context.chrome || context.chromium).Interval)) {\n          timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });\n        }\n      } catch(e) {}\n\n      // Detect Node.js's nanosecond resolution timer available in Node.js >= 0.8.\n      if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') {\n        timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });\n      }\n      // Detect Wade Simmons' Node.js `microtime` module.\n      if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') {\n        timers.push({ 'ns': timer.ns,  'res': getRes('us'), 'unit': 'us' });\n      }\n      // Pick timer with highest resolution.\n      timer = _.minBy(timers, 'res');\n\n      // Error if there are no working timers.\n      if (timer.res == Infinity) {\n        throw new Error('Benchmark.js was unable to find a working timer.');\n      }\n      // Resolve time span required to achieve a percent uncertainty of at most 1%.\n      // For more information see http://spiff.rit.edu/classes/phys273/uncert/uncert.html.\n      options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05));\n      return clock.apply(null, arguments);\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Computes stats on benchmark results.\n     *\n     * @private\n     * @param {Object} bench The benchmark instance.\n     * @param {Object} options The options object.\n     */\n    function compute(bench, options) {\n      options || (options = {});\n\n      var async = options.async,\n          elapsed = 0,\n          initCount = bench.initCount,\n          minSamples = bench.minSamples,\n          queue = [],\n          sample = bench.stats.sample;\n\n      /**\n       * Adds a clone to the queue.\n       */\n      function enqueue() {\n        queue.push(bench.clone({\n          '_original': bench,\n          'events': {\n            'abort': [update],\n            'cycle': [update],\n            'error': [update],\n            'start': [update]\n          }\n        }));\n      }\n\n      /**\n       * Updates the clone/original benchmarks to keep their data in sync.\n       */\n      function update(event) {\n        var clone = this,\n            type = event.type;\n\n        if (bench.running) {\n          if (type == 'start') {\n            // Note: `clone.minTime` prop is inited in `clock()`.\n            clone.count = bench.initCount;\n          }\n          else {\n            if (type == 'error') {\n              bench.error = clone.error;\n            }\n            if (type == 'abort') {\n              bench.abort();\n              bench.emit('cycle');\n            } else {\n              event.currentTarget = event.target = bench;\n              bench.emit(event);\n            }\n          }\n        } else if (bench.aborted) {\n          // Clear abort listeners to avoid triggering bench's abort/cycle again.\n          clone.events.abort.length = 0;\n          clone.abort();\n        }\n      }\n\n      /**\n       * Determines if more clones should be queued or if cycling should stop.\n       */\n      function evaluate(event) {\n        var critical,\n            df,\n            mean,\n            moe,\n            rme,\n            sd,\n            sem,\n            variance,\n            clone = event.target,\n            done = bench.aborted,\n            now = _.now(),\n            size = sample.push(clone.times.period),\n            maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime,\n            times = bench.times,\n            varOf = function(sum, x) { return sum + pow(x - mean, 2); };\n\n        // Exit early for aborted or unclockable tests.\n        if (done || clone.hz == Infinity) {\n          maxedOut = !(size = sample.length = queue.length = 0);\n        }\n\n        if (!done) {\n          // Compute the sample mean (estimate of the population mean).\n          mean = getMean(sample);\n          // Compute the sample variance (estimate of the population variance).\n          variance = _.reduce(sample, varOf, 0) / (size - 1) || 0;\n          // Compute the sample standard deviation (estimate of the population standard deviation).\n          sd = sqrt(variance);\n          // Compute the standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean).\n          sem = sd / sqrt(size);\n          // Compute the degrees of freedom.\n          df = size - 1;\n          // Compute the critical value.\n          critical = tTable[Math.round(df) || 1] || tTable.infinity;\n          // Compute the margin of error.\n          moe = sem * critical;\n          // Compute the relative margin of error.\n          rme = (moe / mean) * 100 || 0;\n\n          _.assign(bench.stats, {\n            'deviation': sd,\n            'mean': mean,\n            'moe': moe,\n            'rme': rme,\n            'sem': sem,\n            'variance': variance\n          });\n\n          // Abort the cycle loop when the minimum sample size has been collected\n          // and the elapsed time exceeds the maximum time allowed per benchmark.\n          // We don't count cycle delays toward the max time because delays may be\n          // increased by browsers that clamp timeouts for inactive tabs. For more\n          // information see https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs.\n          if (maxedOut) {\n            // Reset the `initCount` in case the benchmark is rerun.\n            bench.initCount = initCount;\n            bench.running = false;\n            done = true;\n            times.elapsed = (now - times.timeStamp) / 1e3;\n          }\n          if (bench.hz != Infinity) {\n            bench.hz = 1 / mean;\n            times.cycle = mean * bench.count;\n            times.period = mean;\n          }\n        }\n        // If time permits, increase sample size to reduce the margin of error.\n        if (queue.length < 2 && !maxedOut) {\n          enqueue();\n        }\n        // Abort the `invoke` cycle when done.\n        event.aborted = done;\n      }\n\n      // Init queue and begin.\n      enqueue();\n      invoke(queue, {\n        'name': 'run',\n        'args': { 'async': async },\n        'queued': true,\n        'onCycle': evaluate,\n        'onComplete': function() { bench.emit('complete'); }\n      });\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Cycles a benchmark until a run `count` can be established.\n     *\n     * @private\n     * @param {Object} clone The cloned benchmark instance.\n     * @param {Object} options The options object.\n     */\n    function cycle(clone, options) {\n      options || (options = {});\n\n      var deferred;\n      if (clone instanceof Deferred) {\n        deferred = clone;\n        clone = clone.benchmark;\n      }\n      var clocked,\n          cycles,\n          divisor,\n          event,\n          minTime,\n          period,\n          async = options.async,\n          bench = clone._original,\n          count = clone.count,\n          times = clone.times;\n\n      // Continue, if not aborted between cycles.\n      if (clone.running) {\n        // `minTime` is set to `Benchmark.options.minTime` in `clock()`.\n        cycles = ++clone.cycles;\n        clocked = deferred ? deferred.elapsed : clock(clone);\n        minTime = clone.minTime;\n\n        if (cycles > bench.cycles) {\n          bench.cycles = cycles;\n        }\n        if (clone.error) {\n          event = Event('error');\n          event.message = clone.error;\n          clone.emit(event);\n          if (!event.cancelled) {\n            clone.abort();\n          }\n        }\n      }\n      // Continue, if not errored.\n      if (clone.running) {\n        // Compute the time taken to complete last test cycle.\n        bench.times.cycle = times.cycle = clocked;\n        // Compute the seconds per operation.\n        period = bench.times.period = times.period = clocked / count;\n        // Compute the ops per second.\n        bench.hz = clone.hz = 1 / period;\n        // Avoid working our way up to this next time.\n        bench.initCount = clone.initCount = count;\n        // Do we need to do another cycle?\n        clone.running = clocked < minTime;\n\n        if (clone.running) {\n          // Tests may clock at `0` when `initCount` is a small number,\n          // to avoid that we set its count to something a bit higher.\n          if (!clocked && (divisor = divisors[clone.cycles]) != null) {\n            count = floor(4e6 / divisor);\n          }\n          // Calculate how many more iterations it will take to achieve the `minTime`.\n          if (count <= clone.count) {\n            count += Math.ceil((minTime - clocked) / period);\n          }\n          clone.running = count != Infinity;\n        }\n      }\n      // Should we exit early?\n      event = Event('cycle');\n      clone.emit(event);\n      if (event.aborted) {\n        clone.abort();\n      }\n      // Figure out what to do next.\n      if (clone.running) {\n        // Start a new cycle.\n        clone.count = count;\n        if (deferred) {\n          clone.compiled.call(deferred, context, timer);\n        } else if (async) {\n          delay(clone, function() { cycle(clone, options); });\n        } else {\n          cycle(clone);\n        }\n      }\n      else {\n        // Fix TraceMonkey bug associated with clock fallbacks.\n        // For more information see http://bugzil.la/509069.\n        if (support.browser) {\n          runScript(uid + '=1;delete ' + uid);\n        }\n        // We're done.\n        clone.emit('complete');\n      }\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Runs the benchmark.\n     *\n     * @memberOf Benchmark\n     * @param {Object} [options={}] Options object.\n     * @returns {Object} The benchmark instance.\n     * @example\n     *\n     * // basic usage\n     * bench.run();\n     *\n     * // or with options\n     * bench.run({ 'async': true });\n     */\n    function run(options) {\n      var bench = this,\n          event = Event('start');\n\n      // Set `running` to `false` so `reset()` won't call `abort()`.\n      bench.running = false;\n      bench.reset();\n      bench.running = true;\n\n      bench.count = bench.initCount;\n      bench.times.timeStamp = _.now();\n      bench.emit(event);\n\n      if (!event.cancelled) {\n        options = { 'async': ((options = options && options.async) == null ? bench.async : options) && support.timeout };\n\n        // For clones created within `compute()`.\n        if (bench._original) {\n          if (bench.defer) {\n            Deferred(bench);\n          } else {\n            cycle(bench, options);\n          }\n        }\n        // For original benchmarks.\n        else {\n          compute(bench, options);\n        }\n      }\n      return bench;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    // Firefox 1 erroneously defines variable and argument names of functions on\n    // the function itself as non-configurable properties with `undefined` values.\n    // The bugginess continues as the `Benchmark` constructor has an argument\n    // named `options` and Firefox 1 will not assign a value to `Benchmark.options`,\n    // making it non-writable in the process, unless it is the first property\n    // assigned by for-in loop of `_.assign()`.\n    _.assign(Benchmark, {\n\n      /**\n       * The default options copied by benchmark instances.\n       *\n       * @static\n       * @memberOf Benchmark\n       * @type Object\n       */\n      'options': {\n\n        /**\n         * A flag to indicate that benchmark cycles will execute asynchronously\n         * by default.\n         *\n         * @memberOf Benchmark.options\n         * @type boolean\n         */\n        'async': false,\n\n        /**\n         * A flag to indicate that the benchmark clock is deferred.\n         *\n         * @memberOf Benchmark.options\n         * @type boolean\n         */\n        'defer': false,\n\n        /**\n         * The delay between test cycles (secs).\n         * @memberOf Benchmark.options\n         * @type number\n         */\n        'delay': 0.005,\n\n        /**\n         * Displayed by `Benchmark#toString` when a `name` is not available\n         * (auto-generated if absent).\n         *\n         * @memberOf Benchmark.options\n         * @type string\n         */\n        'id': undefined,\n\n        /**\n         * The default number of times to execute a test on a benchmark's first cycle.\n         *\n         * @memberOf Benchmark.options\n         * @type number\n         */\n        'initCount': 1,\n\n        /**\n         * The maximum time a benchmark is allowed to run before finishing (secs).\n         *\n         * Note: Cycle delays aren't counted toward the maximum time.\n         *\n         * @memberOf Benchmark.options\n         * @type number\n         */\n        'maxTime': 5,\n\n        /**\n         * The minimum sample size required to perform statistical analysis.\n         *\n         * @memberOf Benchmark.options\n         * @type number\n         */\n        'minSamples': 5,\n\n        /**\n         * The time needed to reduce the percent uncertainty of measurement to 1% (secs).\n         *\n         * @memberOf Benchmark.options\n         * @type number\n         */\n        'minTime': 0,\n\n        /**\n         * The name of the benchmark.\n         *\n         * @memberOf Benchmark.options\n         * @type string\n         */\n        'name': undefined,\n\n        /**\n         * An event listener called when the benchmark is aborted.\n         *\n         * @memberOf Benchmark.options\n         * @type Function\n         */\n        'onAbort': undefined,\n\n        /**\n         * An event listener called when the benchmark completes running.\n         *\n         * @memberOf Benchmark.options\n         * @type Function\n         */\n        'onComplete': undefined,\n\n        /**\n         * An event listener called after each run cycle.\n         *\n         * @memberOf Benchmark.options\n         * @type Function\n         */\n        'onCycle': undefined,\n\n        /**\n         * An event listener called when a test errors.\n         *\n         * @memberOf Benchmark.options\n         * @type Function\n         */\n        'onError': undefined,\n\n        /**\n         * An event listener called when the benchmark is reset.\n         *\n         * @memberOf Benchmark.options\n         * @type Function\n         */\n        'onReset': undefined,\n\n        /**\n         * An event listener called when the benchmark starts running.\n         *\n         * @memberOf Benchmark.options\n         * @type Function\n         */\n        'onStart': undefined\n      },\n\n      /**\n       * Platform object with properties describing things like browser name,\n       * version, and operating system. See [`platform.js`](https://mths.be/platform).\n       *\n       * @static\n       * @memberOf Benchmark\n       * @type Object\n       */\n      'platform': context.platform || req('platform') || ({\n        'description': context.navigator && context.navigator.userAgent || null,\n        'layout': null,\n        'product': null,\n        'name': null,\n        'manufacturer': null,\n        'os': null,\n        'prerelease': null,\n        'version': null,\n        'toString': function() {\n          return this.description || '';\n        }\n      }),\n\n      /**\n       * The semantic version number.\n       *\n       * @static\n       * @memberOf Benchmark\n       * @type string\n       */\n      'version': '2.1.0'\n    });\n\n    _.assign(Benchmark, {\n      'filter': filter,\n      'formatNumber': formatNumber,\n      'invoke': invoke,\n      'join': join,\n      'runInContext': runInContext,\n      'support': support\n    });\n\n    // Add lodash methods to Benchmark.\n    _.each(['each', 'forEach', 'forOwn', 'has', 'indexOf', 'map', 'reduce'], function(methodName) {\n      Benchmark[methodName] = _[methodName];\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    _.assign(Benchmark.prototype, {\n\n      /**\n       * The number of times a test was executed.\n       *\n       * @memberOf Benchmark\n       * @type number\n       */\n      'count': 0,\n\n      /**\n       * The number of cycles performed while benchmarking.\n       *\n       * @memberOf Benchmark\n       * @type number\n       */\n      'cycles': 0,\n\n      /**\n       * The number of executions per second.\n       *\n       * @memberOf Benchmark\n       * @type number\n       */\n      'hz': 0,\n\n      /**\n       * The compiled test function.\n       *\n       * @memberOf Benchmark\n       * @type {Function|string}\n       */\n      'compiled': undefined,\n\n      /**\n       * The error object if the test failed.\n       *\n       * @memberOf Benchmark\n       * @type Object\n       */\n      'error': undefined,\n\n      /**\n       * The test to benchmark.\n       *\n       * @memberOf Benchmark\n       * @type {Function|string}\n       */\n      'fn': undefined,\n\n      /**\n       * A flag to indicate if the benchmark is aborted.\n       *\n       * @memberOf Benchmark\n       * @type boolean\n       */\n      'aborted': false,\n\n      /**\n       * A flag to indicate if the benchmark is running.\n       *\n       * @memberOf Benchmark\n       * @type boolean\n       */\n      'running': false,\n\n      /**\n       * Compiled into the test and executed immediately **before** the test loop.\n       *\n       * @memberOf Benchmark\n       * @type {Function|string}\n       * @example\n       *\n       * // basic usage\n       * var bench = Benchmark({\n       *   'setup': function() {\n       *     var c = this.count,\n       *         element = document.getElementById('container');\n       *     while (c--) {\n       *       element.appendChild(document.createElement('div'));\n       *     }\n       *   },\n       *   'fn': function() {\n       *     element.removeChild(element.lastChild);\n       *   }\n       * });\n       *\n       * // compiles to something like:\n       * var c = this.count,\n       *     element = document.getElementById('container');\n       * while (c--) {\n       *   element.appendChild(document.createElement('div'));\n       * }\n       * var start = new Date;\n       * while (count--) {\n       *   element.removeChild(element.lastChild);\n       * }\n       * var end = new Date - start;\n       *\n       * // or using strings\n       * var bench = Benchmark({\n       *   'setup': '\\\n       *     var a = 0;\\n\\\n       *     (function() {\\n\\\n       *       (function() {\\n\\\n       *         (function() {',\n       *   'fn': 'a += 1;',\n       *   'teardown': '\\\n       *          }())\\n\\\n       *        }())\\n\\\n       *      }())'\n       * });\n       *\n       * // compiles to something like:\n       * var a = 0;\n       * (function() {\n       *   (function() {\n       *     (function() {\n       *       var start = new Date;\n       *       while (count--) {\n       *         a += 1;\n       *       }\n       *       var end = new Date - start;\n       *     }())\n       *   }())\n       * }())\n       */\n      'setup': _.noop,\n\n      /**\n       * Compiled into the test and executed immediately **after** the test loop.\n       *\n       * @memberOf Benchmark\n       * @type {Function|string}\n       */\n      'teardown': _.noop,\n\n      /**\n       * An object of stats including mean, margin or error, and standard deviation.\n       *\n       * @memberOf Benchmark\n       * @type Object\n       */\n      'stats': {\n\n        /**\n         * The margin of error.\n         *\n         * @memberOf Benchmark#stats\n         * @type number\n         */\n        'moe': 0,\n\n        /**\n         * The relative margin of error (expressed as a percentage of the mean).\n         *\n         * @memberOf Benchmark#stats\n         * @type number\n         */\n        'rme': 0,\n\n        /**\n         * The standard error of the mean.\n         *\n         * @memberOf Benchmark#stats\n         * @type number\n         */\n        'sem': 0,\n\n        /**\n         * The sample standard deviation.\n         *\n         * @memberOf Benchmark#stats\n         * @type number\n         */\n        'deviation': 0,\n\n        /**\n         * The sample arithmetic mean (secs).\n         *\n         * @memberOf Benchmark#stats\n         * @type number\n         */\n        'mean': 0,\n\n        /**\n         * The array of sampled periods.\n         *\n         * @memberOf Benchmark#stats\n         * @type Array\n         */\n        'sample': [],\n\n        /**\n         * The sample variance.\n         *\n         * @memberOf Benchmark#stats\n         * @type number\n         */\n        'variance': 0\n      },\n\n      /**\n       * An object of timing data including cycle, elapsed, period, start, and stop.\n       *\n       * @memberOf Benchmark\n       * @type Object\n       */\n      'times': {\n\n        /**\n         * The time taken to complete the last cycle (secs).\n         *\n         * @memberOf Benchmark#times\n         * @type number\n         */\n        'cycle': 0,\n\n        /**\n         * The time taken to complete the benchmark (secs).\n         *\n         * @memberOf Benchmark#times\n         * @type number\n         */\n        'elapsed': 0,\n\n        /**\n         * The time taken to execute the test once (secs).\n         *\n         * @memberOf Benchmark#times\n         * @type number\n         */\n        'period': 0,\n\n        /**\n         * A timestamp of when the benchmark started (ms).\n         *\n         * @memberOf Benchmark#times\n         * @type number\n         */\n        'timeStamp': 0\n      }\n    });\n\n    _.assign(Benchmark.prototype, {\n      'abort': abort,\n      'clone': clone,\n      'compare': compare,\n      'emit': emit,\n      'listeners': listeners,\n      'off': off,\n      'on': on,\n      'reset': reset,\n      'run': run,\n      'toString': toStringBench\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    _.assign(Deferred.prototype, {\n\n      /**\n       * The deferred benchmark instance.\n       *\n       * @memberOf Benchmark.Deferred\n       * @type Object\n       */\n      'benchmark': null,\n\n      /**\n       * The number of deferred cycles performed while benchmarking.\n       *\n       * @memberOf Benchmark.Deferred\n       * @type number\n       */\n      'cycles': 0,\n\n      /**\n       * The time taken to complete the deferred benchmark (secs).\n       *\n       * @memberOf Benchmark.Deferred\n       * @type number\n       */\n      'elapsed': 0,\n\n      /**\n       * A timestamp of when the deferred benchmark started (ms).\n       *\n       * @memberOf Benchmark.Deferred\n       * @type number\n       */\n      'timeStamp': 0\n    });\n\n    _.assign(Deferred.prototype, {\n      'resolve': resolve\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    _.assign(Event.prototype, {\n\n      /**\n       * A flag to indicate if the emitters listener iteration is aborted.\n       *\n       * @memberOf Benchmark.Event\n       * @type boolean\n       */\n      'aborted': false,\n\n      /**\n       * A flag to indicate if the default action is cancelled.\n       *\n       * @memberOf Benchmark.Event\n       * @type boolean\n       */\n      'cancelled': false,\n\n      /**\n       * The object whose listeners are currently being processed.\n       *\n       * @memberOf Benchmark.Event\n       * @type Object\n       */\n      'currentTarget': undefined,\n\n      /**\n       * The return value of the last executed listener.\n       *\n       * @memberOf Benchmark.Event\n       * @type Mixed\n       */\n      'result': undefined,\n\n      /**\n       * The object to which the event was originally emitted.\n       *\n       * @memberOf Benchmark.Event\n       * @type Object\n       */\n      'target': undefined,\n\n      /**\n       * A timestamp of when the event was created (ms).\n       *\n       * @memberOf Benchmark.Event\n       * @type number\n       */\n      'timeStamp': 0,\n\n      /**\n       * The event type.\n       *\n       * @memberOf Benchmark.Event\n       * @type string\n       */\n      'type': ''\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * The default options copied by suite instances.\n     *\n     * @static\n     * @memberOf Benchmark.Suite\n     * @type Object\n     */\n    Suite.options = {\n\n      /**\n       * The name of the suite.\n       *\n       * @memberOf Benchmark.Suite.options\n       * @type string\n       */\n      'name': undefined\n    };\n\n    /*------------------------------------------------------------------------*/\n\n    _.assign(Suite.prototype, {\n\n      /**\n       * The number of benchmarks in the suite.\n       *\n       * @memberOf Benchmark.Suite\n       * @type number\n       */\n      'length': 0,\n\n      /**\n       * A flag to indicate if the suite is aborted.\n       *\n       * @memberOf Benchmark.Suite\n       * @type boolean\n       */\n      'aborted': false,\n\n      /**\n       * A flag to indicate if the suite is running.\n       *\n       * @memberOf Benchmark.Suite\n       * @type boolean\n       */\n      'running': false\n    });\n\n    _.assign(Suite.prototype, {\n      'abort': abortSuite,\n      'add': add,\n      'clone': cloneSuite,\n      'emit': emit,\n      'filter': filterSuite,\n      'join': arrayRef.join,\n      'listeners': listeners,\n      'off': off,\n      'on': on,\n      'pop': arrayRef.pop,\n      'push': push,\n      'reset': resetSuite,\n      'run': runSuite,\n      'reverse': arrayRef.reverse,\n      'shift': shift,\n      'slice': slice,\n      'sort': arrayRef.sort,\n      'splice': arrayRef.splice,\n      'unshift': unshift\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    // Expose Deferred, Event, and Suite.\n    _.assign(Benchmark, {\n      'Deferred': Deferred,\n      'Event': Event,\n      'Suite': Suite\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    // Add lodash methods as Suite methods.\n    _.each(['each', 'forEach', 'indexOf', 'map', 'reduce'], function(methodName) {\n      var func = _[methodName];\n      Suite.prototype[methodName] = function() {\n        var args = [this];\n        push.apply(args, arguments);\n        return func.apply(_, args);\n      };\n    });\n\n    // Avoid array-like object bugs with `Array#shift` and `Array#splice`\n    // in Firefox < 10 and IE < 9.\n    _.each(['pop', 'shift', 'splice'], function(methodName) {\n      var func = arrayRef[methodName];\n\n      Suite.prototype[methodName] = function() {\n        var value = this,\n            result = func.apply(value, arguments);\n\n        if (value.length === 0) {\n          delete value[0];\n        }\n        return result;\n      };\n    });\n\n    // Avoid buggy `Array#unshift` in IE < 8 which doesn't return the new\n    // length of the array.\n    Suite.prototype.unshift = function() {\n      var value = this;\n      unshift.apply(value, arguments);\n      return value.length;\n    };\n\n    return Benchmark;\n  }\n\n  /*--------------------------------------------------------------------------*/\n\n  // Export Benchmark.\n  // Some AMD build optimizers, like r.js, check for condition patterns like the following:\n  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {\n    // Define as an anonymous module so, through path mapping, it can be aliased.\n    define(['lodash', 'platform'], function(_, platform) {\n      return runInContext({\n        '_': _,\n        'platform': platform\n      });\n    });\n  }\n  else {\n    var Benchmark = runInContext();\n\n    // Check for `exports` after `define` in case a build optimizer adds an `exports` object.\n    if (freeExports && freeModule) {\n      // Export for Node.js.\n      if (moduleExports) {\n        (freeModule.exports = Benchmark).Benchmark = Benchmark;\n      }\n      // Export for CommonJS support.\n      freeExports.Benchmark = Benchmark;\n    }\n    else {\n      // Export to the global object.\n      root.Benchmark = Benchmark;\n    }\n  }\n}.call(this));\n"
  },
  {
    "path": "assets/js/vendor/lodash.js",
    "content": "/**\n * @license\n * lodash 4.11.1 (Custom Build) <https://lodash.com/>\n * Build: `lodash -o ./dist/lodash.js`\n * Copyright jQuery Foundation and other contributors <https://jquery.org/>\n * Released under MIT license <https://lodash.com/license>\n * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>\n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n;(function() {\n\n  /** Used as a safe reference for `undefined` in pre-ES5 environments. */\n  var undefined;\n\n  /** Used as the semantic version number. */\n  var VERSION = '4.11.1';\n\n  /** Used as the size to enable large array optimizations. */\n  var LARGE_ARRAY_SIZE = 200;\n\n  /** Used as the `TypeError` message for \"Functions\" methods. */\n  var FUNC_ERROR_TEXT = 'Expected a function';\n\n  /** Used to stand-in for `undefined` hash values. */\n  var HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n  /** Used as the internal argument placeholder. */\n  var PLACEHOLDER = '__lodash_placeholder__';\n\n  /** Used to compose bitmasks for wrapper metadata. */\n  var BIND_FLAG = 1,\n      BIND_KEY_FLAG = 2,\n      CURRY_BOUND_FLAG = 4,\n      CURRY_FLAG = 8,\n      CURRY_RIGHT_FLAG = 16,\n      PARTIAL_FLAG = 32,\n      PARTIAL_RIGHT_FLAG = 64,\n      ARY_FLAG = 128,\n      REARG_FLAG = 256,\n      FLIP_FLAG = 512;\n\n  /** Used to compose bitmasks for comparison styles. */\n  var UNORDERED_COMPARE_FLAG = 1,\n      PARTIAL_COMPARE_FLAG = 2;\n\n  /** Used as default options for `_.truncate`. */\n  var DEFAULT_TRUNC_LENGTH = 30,\n      DEFAULT_TRUNC_OMISSION = '...';\n\n  /** Used to detect hot functions by number of calls within a span of milliseconds. */\n  var HOT_COUNT = 150,\n      HOT_SPAN = 16;\n\n  /** Used to indicate the type of lazy iteratees. */\n  var LAZY_FILTER_FLAG = 1,\n      LAZY_MAP_FLAG = 2,\n      LAZY_WHILE_FLAG = 3;\n\n  /** Used as references for various `Number` constants. */\n  var INFINITY = 1 / 0,\n      MAX_SAFE_INTEGER = 9007199254740991,\n      MAX_INTEGER = 1.7976931348623157e+308,\n      NAN = 0 / 0;\n\n  /** Used as references for the maximum length and index of an array. */\n  var MAX_ARRAY_LENGTH = 4294967295,\n      MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,\n      HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;\n\n  /** `Object#toString` result references. */\n  var argsTag = '[object Arguments]',\n      arrayTag = '[object Array]',\n      boolTag = '[object Boolean]',\n      dateTag = '[object Date]',\n      errorTag = '[object Error]',\n      funcTag = '[object Function]',\n      genTag = '[object GeneratorFunction]',\n      mapTag = '[object Map]',\n      numberTag = '[object Number]',\n      objectTag = '[object Object]',\n      promiseTag = '[object Promise]',\n      regexpTag = '[object RegExp]',\n      setTag = '[object Set]',\n      stringTag = '[object String]',\n      symbolTag = '[object Symbol]',\n      weakMapTag = '[object WeakMap]',\n      weakSetTag = '[object WeakSet]';\n\n  var arrayBufferTag = '[object ArrayBuffer]',\n      dataViewTag = '[object DataView]',\n      float32Tag = '[object Float32Array]',\n      float64Tag = '[object Float64Array]',\n      int8Tag = '[object Int8Array]',\n      int16Tag = '[object Int16Array]',\n      int32Tag = '[object Int32Array]',\n      uint8Tag = '[object Uint8Array]',\n      uint8ClampedTag = '[object Uint8ClampedArray]',\n      uint16Tag = '[object Uint16Array]',\n      uint32Tag = '[object Uint32Array]';\n\n  /** Used to match empty string literals in compiled template source. */\n  var reEmptyStringLeading = /\\b__p \\+= '';/g,\n      reEmptyStringMiddle = /\\b(__p \\+=) '' \\+/g,\n      reEmptyStringTrailing = /(__e\\(.*?\\)|\\b__t\\)) \\+\\n'';/g;\n\n  /** Used to match HTML entities and HTML characters. */\n  var reEscapedHtml = /&(?:amp|lt|gt|quot|#39|#96);/g,\n      reUnescapedHtml = /[&<>\"'`]/g,\n      reHasEscapedHtml = RegExp(reEscapedHtml.source),\n      reHasUnescapedHtml = RegExp(reUnescapedHtml.source);\n\n  /** Used to match template delimiters. */\n  var reEscape = /<%-([\\s\\S]+?)%>/g,\n      reEvaluate = /<%([\\s\\S]+?)%>/g,\n      reInterpolate = /<%=([\\s\\S]+?)%>/g;\n\n  /** Used to match property names within property paths. */\n  var reIsDeepProp = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n      reIsPlainProp = /^\\w*$/,\n      rePropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]/g;\n\n  /**\n   * Used to match `RegExp`\n   * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns).\n   */\n  var reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g,\n      reHasRegExpChar = RegExp(reRegExpChar.source);\n\n  /** Used to match leading and trailing whitespace. */\n  var reTrim = /^\\s+|\\s+$/g,\n      reTrimStart = /^\\s+/,\n      reTrimEnd = /\\s+$/;\n\n  /** Used to match non-compound words composed of alphanumeric characters. */\n  var reBasicWord = /[a-zA-Z0-9]+/g;\n\n  /** Used to match backslashes in property paths. */\n  var reEscapeChar = /\\\\(\\\\)?/g;\n\n  /**\n   * Used to match\n   * [ES template delimiters](http://ecma-international.org/ecma-262/6.0/#sec-template-literal-lexical-components).\n   */\n  var reEsTemplate = /\\$\\{([^\\\\}]*(?:\\\\.[^\\\\}]*)*)\\}/g;\n\n  /** Used to match `RegExp` flags from their coerced string values. */\n  var reFlags = /\\w*$/;\n\n  /** Used to detect hexadecimal string values. */\n  var reHasHexPrefix = /^0x/i;\n\n  /** Used to detect bad signed hexadecimal string values. */\n  var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;\n\n  /** Used to detect binary string values. */\n  var reIsBinary = /^0b[01]+$/i;\n\n  /** Used to detect host constructors (Safari). */\n  var reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n  /** Used to detect octal string values. */\n  var reIsOctal = /^0o[0-7]+$/i;\n\n  /** Used to detect unsigned integer values. */\n  var reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n  /** Used to match latin-1 supplementary letters (excluding mathematical operators). */\n  var reLatin1 = /[\\xc0-\\xd6\\xd8-\\xde\\xdf-\\xf6\\xf8-\\xff]/g;\n\n  /** Used to ensure capturing order of template delimiters. */\n  var reNoMatch = /($^)/;\n\n  /** Used to match unescaped characters in compiled string literals. */\n  var reUnescapedString = /['\\n\\r\\u2028\\u2029\\\\]/g;\n\n  /** Used to compose unicode character classes. */\n  var rsAstralRange = '\\\\ud800-\\\\udfff',\n      rsComboMarksRange = '\\\\u0300-\\\\u036f\\\\ufe20-\\\\ufe23',\n      rsComboSymbolsRange = '\\\\u20d0-\\\\u20f0',\n      rsDingbatRange = '\\\\u2700-\\\\u27bf',\n      rsLowerRange = 'a-z\\\\xdf-\\\\xf6\\\\xf8-\\\\xff',\n      rsMathOpRange = '\\\\xac\\\\xb1\\\\xd7\\\\xf7',\n      rsNonCharRange = '\\\\x00-\\\\x2f\\\\x3a-\\\\x40\\\\x5b-\\\\x60\\\\x7b-\\\\xbf',\n      rsQuoteRange = '\\\\u2018\\\\u2019\\\\u201c\\\\u201d',\n      rsSpaceRange = ' \\\\t\\\\x0b\\\\f\\\\xa0\\\\ufeff\\\\n\\\\r\\\\u2028\\\\u2029\\\\u1680\\\\u180e\\\\u2000\\\\u2001\\\\u2002\\\\u2003\\\\u2004\\\\u2005\\\\u2006\\\\u2007\\\\u2008\\\\u2009\\\\u200a\\\\u202f\\\\u205f\\\\u3000',\n      rsUpperRange = 'A-Z\\\\xc0-\\\\xd6\\\\xd8-\\\\xde',\n      rsVarRange = '\\\\ufe0e\\\\ufe0f',\n      rsBreakRange = rsMathOpRange + rsNonCharRange + rsQuoteRange + rsSpaceRange;\n\n  /** Used to compose unicode capture groups. */\n  var rsApos = \"['\\u2019]\",\n      rsAstral = '[' + rsAstralRange + ']',\n      rsBreak = '[' + rsBreakRange + ']',\n      rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']',\n      rsDigits = '\\\\d+',\n      rsDingbat = '[' + rsDingbatRange + ']',\n      rsLower = '[' + rsLowerRange + ']',\n      rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',\n      rsFitz = '\\\\ud83c[\\\\udffb-\\\\udfff]',\n      rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',\n      rsNonAstral = '[^' + rsAstralRange + ']',\n      rsRegional = '(?:\\\\ud83c[\\\\udde6-\\\\uddff]){2}',\n      rsSurrPair = '[\\\\ud800-\\\\udbff][\\\\udc00-\\\\udfff]',\n      rsUpper = '[' + rsUpperRange + ']',\n      rsZWJ = '\\\\u200d';\n\n  /** Used to compose unicode regexes. */\n  var rsLowerMisc = '(?:' + rsLower + '|' + rsMisc + ')',\n      rsUpperMisc = '(?:' + rsUpper + '|' + rsMisc + ')',\n      rsOptLowerContr = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',\n      rsOptUpperContr = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',\n      reOptMod = rsModifier + '?',\n      rsOptVar = '[' + rsVarRange + ']?',\n      rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',\n      rsSeq = rsOptVar + reOptMod + rsOptJoin,\n      rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,\n      rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';\n\n  /** Used to match apostrophes. */\n  var reApos = RegExp(rsApos, 'g');\n\n  /**\n   * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and\n   * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).\n   */\n  var reComboMark = RegExp(rsCombo, 'g');\n\n  /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */\n  var reComplexSymbol = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');\n\n  /** Used to match complex or compound words. */\n  var reComplexWord = RegExp([\n    rsUpper + '?' + rsLower + '+' + rsOptLowerContr + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',\n    rsUpperMisc + '+' + rsOptUpperContr + '(?=' + [rsBreak, rsUpper + rsLowerMisc, '$'].join('|') + ')',\n    rsUpper + '?' + rsLowerMisc + '+' + rsOptLowerContr,\n    rsUpper + '+' + rsOptUpperContr,\n    rsDigits,\n    rsEmoji\n  ].join('|'), 'g');\n\n  /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */\n  var reHasComplexSymbol = RegExp('[' + rsZWJ + rsAstralRange  + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');\n\n  /** Used to detect strings that need a more robust regexp to match words. */\n  var reHasComplexWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;\n\n  /** Used to assign default `context` object properties. */\n  var contextProps = [\n    'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',\n    'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',\n    'Promise', 'Reflect', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError',\n    'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',\n    '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'\n  ];\n\n  /** Used to make template sourceURLs easier to identify. */\n  var templateCounter = -1;\n\n  /** Used to identify `toStringTag` values of typed arrays. */\n  var typedArrayTags = {};\n  typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =\n  typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =\n  typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =\n  typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =\n  typedArrayTags[uint32Tag] = true;\n  typedArrayTags[argsTag] = typedArrayTags[arrayTag] =\n  typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =\n  typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =\n  typedArrayTags[errorTag] = typedArrayTags[funcTag] =\n  typedArrayTags[mapTag] = typedArrayTags[numberTag] =\n  typedArrayTags[objectTag] = typedArrayTags[regexpTag] =\n  typedArrayTags[setTag] = typedArrayTags[stringTag] =\n  typedArrayTags[weakMapTag] = false;\n\n  /** Used to identify `toStringTag` values supported by `_.clone`. */\n  var cloneableTags = {};\n  cloneableTags[argsTag] = cloneableTags[arrayTag] =\n  cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =\n  cloneableTags[boolTag] = cloneableTags[dateTag] =\n  cloneableTags[float32Tag] = cloneableTags[float64Tag] =\n  cloneableTags[int8Tag] = cloneableTags[int16Tag] =\n  cloneableTags[int32Tag] = cloneableTags[mapTag] =\n  cloneableTags[numberTag] = cloneableTags[objectTag] =\n  cloneableTags[regexpTag] = cloneableTags[setTag] =\n  cloneableTags[stringTag] = cloneableTags[symbolTag] =\n  cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =\n  cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;\n  cloneableTags[errorTag] = cloneableTags[funcTag] =\n  cloneableTags[weakMapTag] = false;\n\n  /** Used to map latin-1 supplementary letters to basic latin letters. */\n  var deburredLetters = {\n    '\\xc0': 'A',  '\\xc1': 'A', '\\xc2': 'A', '\\xc3': 'A', '\\xc4': 'A', '\\xc5': 'A',\n    '\\xe0': 'a',  '\\xe1': 'a', '\\xe2': 'a', '\\xe3': 'a', '\\xe4': 'a', '\\xe5': 'a',\n    '\\xc7': 'C',  '\\xe7': 'c',\n    '\\xd0': 'D',  '\\xf0': 'd',\n    '\\xc8': 'E',  '\\xc9': 'E', '\\xca': 'E', '\\xcb': 'E',\n    '\\xe8': 'e',  '\\xe9': 'e', '\\xea': 'e', '\\xeb': 'e',\n    '\\xcC': 'I',  '\\xcd': 'I', '\\xce': 'I', '\\xcf': 'I',\n    '\\xeC': 'i',  '\\xed': 'i', '\\xee': 'i', '\\xef': 'i',\n    '\\xd1': 'N',  '\\xf1': 'n',\n    '\\xd2': 'O',  '\\xd3': 'O', '\\xd4': 'O', '\\xd5': 'O', '\\xd6': 'O', '\\xd8': 'O',\n    '\\xf2': 'o',  '\\xf3': 'o', '\\xf4': 'o', '\\xf5': 'o', '\\xf6': 'o', '\\xf8': 'o',\n    '\\xd9': 'U',  '\\xda': 'U', '\\xdb': 'U', '\\xdc': 'U',\n    '\\xf9': 'u',  '\\xfa': 'u', '\\xfb': 'u', '\\xfc': 'u',\n    '\\xdd': 'Y',  '\\xfd': 'y', '\\xff': 'y',\n    '\\xc6': 'Ae', '\\xe6': 'ae',\n    '\\xde': 'Th', '\\xfe': 'th',\n    '\\xdf': 'ss'\n  };\n\n  /** Used to map characters to HTML entities. */\n  var htmlEscapes = {\n    '&': '&amp;',\n    '<': '&lt;',\n    '>': '&gt;',\n    '\"': '&quot;',\n    \"'\": '&#39;',\n    '`': '&#96;'\n  };\n\n  /** Used to map HTML entities to characters. */\n  var htmlUnescapes = {\n    '&amp;': '&',\n    '&lt;': '<',\n    '&gt;': '>',\n    '&quot;': '\"',\n    '&#39;': \"'\",\n    '&#96;': '`'\n  };\n\n  /** Used to determine if values are of the language type `Object`. */\n  var objectTypes = {\n    'function': true,\n    'object': true\n  };\n\n  /** Used to escape characters for inclusion in compiled string literals. */\n  var stringEscapes = {\n    '\\\\': '\\\\',\n    \"'\": \"'\",\n    '\\n': 'n',\n    '\\r': 'r',\n    '\\u2028': 'u2028',\n    '\\u2029': 'u2029'\n  };\n\n  /** Built-in method references without a dependency on `root`. */\n  var freeParseFloat = parseFloat,\n      freeParseInt = parseInt;\n\n  /** Detect free variable `exports`. */\n  var freeExports = (objectTypes[typeof exports] && exports && !exports.nodeType)\n    ? exports\n    : undefined;\n\n  /** Detect free variable `module`. */\n  var freeModule = (objectTypes[typeof module] && module && !module.nodeType)\n    ? module\n    : undefined;\n\n  /** Detect the popular CommonJS extension `module.exports`. */\n  var moduleExports = (freeModule && freeModule.exports === freeExports)\n    ? freeExports\n    : undefined;\n\n  /** Detect free variable `global` from Node.js. */\n  var freeGlobal = checkGlobal(freeExports && freeModule && typeof global == 'object' && global);\n\n  /** Detect free variable `self`. */\n  var freeSelf = checkGlobal(objectTypes[typeof self] && self);\n\n  /** Detect free variable `window`. */\n  var freeWindow = checkGlobal(objectTypes[typeof window] && window);\n\n  /** Detect `this` as the global object. */\n  var thisGlobal = checkGlobal(objectTypes[typeof this] && this);\n\n  /**\n   * Used as a reference to the global object.\n   *\n   * The `this` value is used if it's the global object to avoid Greasemonkey's\n   * restricted `window` object, otherwise the `window` object is used.\n   */\n  var root = freeGlobal ||\n    ((freeWindow !== (thisGlobal && thisGlobal.window)) && freeWindow) ||\n      freeSelf || thisGlobal || Function('return this')();\n\n  /*--------------------------------------------------------------------------*/\n\n  /**\n   * Adds the key-value `pair` to `map`.\n   *\n   * @private\n   * @param {Object} map The map to modify.\n   * @param {Array} pair The key-value pair to add.\n   * @returns {Object} Returns `map`.\n   */\n  function addMapEntry(map, pair) {\n    // Don't return `Map#set` because it doesn't return the map instance in IE 11.\n    map.set(pair[0], pair[1]);\n    return map;\n  }\n\n  /**\n   * Adds `value` to `set`.\n   *\n   * @private\n   * @param {Object} set The set to modify.\n   * @param {*} value The value to add.\n   * @returns {Object} Returns `set`.\n   */\n  function addSetEntry(set, value) {\n    set.add(value);\n    return set;\n  }\n\n  /**\n   * A faster alternative to `Function#apply`, this function invokes `func`\n   * with the `this` binding of `thisArg` and the arguments of `args`.\n   *\n   * @private\n   * @param {Function} func The function to invoke.\n   * @param {*} thisArg The `this` binding of `func`.\n   * @param {Array} args The arguments to invoke `func` with.\n   * @returns {*} Returns the result of `func`.\n   */\n  function apply(func, thisArg, args) {\n    var length = args.length;\n    switch (length) {\n      case 0: return func.call(thisArg);\n      case 1: return func.call(thisArg, args[0]);\n      case 2: return func.call(thisArg, args[0], args[1]);\n      case 3: return func.call(thisArg, args[0], args[1], args[2]);\n    }\n    return func.apply(thisArg, args);\n  }\n\n  /**\n   * A specialized version of `baseAggregator` for arrays.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} setter The function to set `accumulator` values.\n   * @param {Function} iteratee The iteratee to transform keys.\n   * @param {Object} accumulator The initial aggregated object.\n   * @returns {Function} Returns `accumulator`.\n   */\n  function arrayAggregator(array, setter, iteratee, accumulator) {\n    var index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      var value = array[index];\n      setter(accumulator, value, iteratee(value), array);\n    }\n    return accumulator;\n  }\n\n  /**\n   * Creates a new array concatenating `array` with `other`.\n   *\n   * @private\n   * @param {Array} array The first array to concatenate.\n   * @param {Array} other The second array to concatenate.\n   * @returns {Array} Returns the new concatenated array.\n   */\n  function arrayConcat(array, other) {\n    var index = -1,\n        length = array.length,\n        othIndex = -1,\n        othLength = other.length,\n        result = Array(length + othLength);\n\n    while (++index < length) {\n      result[index] = array[index];\n    }\n    while (++othIndex < othLength) {\n      result[index++] = other[othIndex];\n    }\n    return result;\n  }\n\n  /**\n   * A specialized version of `_.forEach` for arrays without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @returns {Array} Returns `array`.\n   */\n  function arrayEach(array, iteratee) {\n    var index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      if (iteratee(array[index], index, array) === false) {\n        break;\n      }\n    }\n    return array;\n  }\n\n  /**\n   * A specialized version of `_.forEachRight` for arrays without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @returns {Array} Returns `array`.\n   */\n  function arrayEachRight(array, iteratee) {\n    var length = array.length;\n\n    while (length--) {\n      if (iteratee(array[length], length, array) === false) {\n        break;\n      }\n    }\n    return array;\n  }\n\n  /**\n   * A specialized version of `_.every` for arrays without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} predicate The function invoked per iteration.\n   * @returns {boolean} Returns `true` if all elements pass the predicate check,\n   *  else `false`.\n   */\n  function arrayEvery(array, predicate) {\n    var index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      if (!predicate(array[index], index, array)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  /**\n   * A specialized version of `_.filter` for arrays without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} predicate The function invoked per iteration.\n   * @returns {Array} Returns the new filtered array.\n   */\n  function arrayFilter(array, predicate) {\n    var index = -1,\n        length = array.length,\n        resIndex = 0,\n        result = [];\n\n    while (++index < length) {\n      var value = array[index];\n      if (predicate(value, index, array)) {\n        result[resIndex++] = value;\n      }\n    }\n    return result;\n  }\n\n  /**\n   * A specialized version of `_.includes` for arrays without support for\n   * specifying an index to search from.\n   *\n   * @private\n   * @param {Array} array The array to search.\n   * @param {*} target The value to search for.\n   * @returns {boolean} Returns `true` if `target` is found, else `false`.\n   */\n  function arrayIncludes(array, value) {\n    return !!array.length && baseIndexOf(array, value, 0) > -1;\n  }\n\n  /**\n   * This function is like `arrayIncludes` except that it accepts a comparator.\n   *\n   * @private\n   * @param {Array} array The array to search.\n   * @param {*} target The value to search for.\n   * @param {Function} comparator The comparator invoked per element.\n   * @returns {boolean} Returns `true` if `target` is found, else `false`.\n   */\n  function arrayIncludesWith(array, value, comparator) {\n    var index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      if (comparator(value, array[index])) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  /**\n   * A specialized version of `_.map` for arrays without support for iteratee\n   * shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @returns {Array} Returns the new mapped array.\n   */\n  function arrayMap(array, iteratee) {\n    var index = -1,\n        length = array.length,\n        result = Array(length);\n\n    while (++index < length) {\n      result[index] = iteratee(array[index], index, array);\n    }\n    return result;\n  }\n\n  /**\n   * Appends the elements of `values` to `array`.\n   *\n   * @private\n   * @param {Array} array The array to modify.\n   * @param {Array} values The values to append.\n   * @returns {Array} Returns `array`.\n   */\n  function arrayPush(array, values) {\n    var index = -1,\n        length = values.length,\n        offset = array.length;\n\n    while (++index < length) {\n      array[offset + index] = values[index];\n    }\n    return array;\n  }\n\n  /**\n   * A specialized version of `_.reduce` for arrays without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @param {*} [accumulator] The initial value.\n   * @param {boolean} [initAccum] Specify using the first element of `array` as\n   *  the initial value.\n   * @returns {*} Returns the accumulated value.\n   */\n  function arrayReduce(array, iteratee, accumulator, initAccum) {\n    var index = -1,\n        length = array.length;\n\n    if (initAccum && length) {\n      accumulator = array[++index];\n    }\n    while (++index < length) {\n      accumulator = iteratee(accumulator, array[index], index, array);\n    }\n    return accumulator;\n  }\n\n  /**\n   * A specialized version of `_.reduceRight` for arrays without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @param {*} [accumulator] The initial value.\n   * @param {boolean} [initAccum] Specify using the last element of `array` as\n   *  the initial value.\n   * @returns {*} Returns the accumulated value.\n   */\n  function arrayReduceRight(array, iteratee, accumulator, initAccum) {\n    var length = array.length;\n    if (initAccum && length) {\n      accumulator = array[--length];\n    }\n    while (length--) {\n      accumulator = iteratee(accumulator, array[length], length, array);\n    }\n    return accumulator;\n  }\n\n  /**\n   * A specialized version of `_.some` for arrays without support for iteratee\n   * shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} predicate The function invoked per iteration.\n   * @returns {boolean} Returns `true` if any element passes the predicate check,\n   *  else `false`.\n   */\n  function arraySome(array, predicate) {\n    var index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      if (predicate(array[index], index, array)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  /**\n   * The base implementation of methods like `_.max` and `_.min` which accepts a\n   * `comparator` to determine the extremum value.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The iteratee invoked per iteration.\n   * @param {Function} comparator The comparator used to compare values.\n   * @returns {*} Returns the extremum value.\n   */\n  function baseExtremum(array, iteratee, comparator) {\n    var index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      var value = array[index],\n          current = iteratee(value);\n\n      if (current != null && (computed === undefined\n            ? current === current\n            : comparator(current, computed)\n          )) {\n        var computed = current,\n            result = value;\n      }\n    }\n    return result;\n  }\n\n  /**\n   * The base implementation of methods like `_.find` and `_.findKey`, without\n   * support for iteratee shorthands, which iterates over `collection` using\n   * `eachFunc`.\n   *\n   * @private\n   * @param {Array|Object} collection The collection to search.\n   * @param {Function} predicate The function invoked per iteration.\n   * @param {Function} eachFunc The function to iterate over `collection`.\n   * @param {boolean} [retKey] Specify returning the key of the found element\n   *  instead of the element itself.\n   * @returns {*} Returns the found element or its key, else `undefined`.\n   */\n  function baseFind(collection, predicate, eachFunc, retKey) {\n    var result;\n    eachFunc(collection, function(value, key, collection) {\n      if (predicate(value, key, collection)) {\n        result = retKey ? key : value;\n        return false;\n      }\n    });\n    return result;\n  }\n\n  /**\n   * The base implementation of `_.findIndex` and `_.findLastIndex` without\n   * support for iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to search.\n   * @param {Function} predicate The function invoked per iteration.\n   * @param {boolean} [fromRight] Specify iterating from right to left.\n   * @returns {number} Returns the index of the matched value, else `-1`.\n   */\n  function baseFindIndex(array, predicate, fromRight) {\n    var length = array.length,\n        index = fromRight ? length : -1;\n\n    while ((fromRight ? index-- : ++index < length)) {\n      if (predicate(array[index], index, array)) {\n        return index;\n      }\n    }\n    return -1;\n  }\n\n  /**\n   * The base implementation of `_.indexOf` without `fromIndex` bounds checks.\n   *\n   * @private\n   * @param {Array} array The array to search.\n   * @param {*} value The value to search for.\n   * @param {number} fromIndex The index to search from.\n   * @returns {number} Returns the index of the matched value, else `-1`.\n   */\n  function baseIndexOf(array, value, fromIndex) {\n    if (value !== value) {\n      return indexOfNaN(array, fromIndex);\n    }\n    var index = fromIndex - 1,\n        length = array.length;\n\n    while (++index < length) {\n      if (array[index] === value) {\n        return index;\n      }\n    }\n    return -1;\n  }\n\n  /**\n   * This function is like `baseIndexOf` except that it accepts a comparator.\n   *\n   * @private\n   * @param {Array} array The array to search.\n   * @param {*} value The value to search for.\n   * @param {number} fromIndex The index to search from.\n   * @param {Function} comparator The comparator invoked per element.\n   * @returns {number} Returns the index of the matched value, else `-1`.\n   */\n  function baseIndexOfWith(array, value, fromIndex, comparator) {\n    var index = fromIndex - 1,\n        length = array.length;\n\n    while (++index < length) {\n      if (comparator(array[index], value)) {\n        return index;\n      }\n    }\n    return -1;\n  }\n\n  /**\n   * The base implementation of `_.mean` and `_.meanBy` without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @returns {number} Returns the mean.\n   */\n  function baseMean(array, iteratee) {\n    var length = array ? array.length : 0;\n    return length ? (baseSum(array, iteratee) / length) : NAN;\n  }\n\n  /**\n   * The base implementation of `_.reduce` and `_.reduceRight`, without support\n   * for iteratee shorthands, which iterates over `collection` using `eachFunc`.\n   *\n   * @private\n   * @param {Array|Object} collection The collection to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @param {*} accumulator The initial value.\n   * @param {boolean} initAccum Specify using the first or last element of\n   *  `collection` as the initial value.\n   * @param {Function} eachFunc The function to iterate over `collection`.\n   * @returns {*} Returns the accumulated value.\n   */\n  function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {\n    eachFunc(collection, function(value, index, collection) {\n      accumulator = initAccum\n        ? (initAccum = false, value)\n        : iteratee(accumulator, value, index, collection);\n    });\n    return accumulator;\n  }\n\n  /**\n   * The base implementation of `_.sortBy` which uses `comparer` to define the\n   * sort order of `array` and replaces criteria objects with their corresponding\n   * values.\n   *\n   * @private\n   * @param {Array} array The array to sort.\n   * @param {Function} comparer The function to define sort order.\n   * @returns {Array} Returns `array`.\n   */\n  function baseSortBy(array, comparer) {\n    var length = array.length;\n\n    array.sort(comparer);\n    while (length--) {\n      array[length] = array[length].value;\n    }\n    return array;\n  }\n\n  /**\n   * The base implementation of `_.sum` and `_.sumBy` without support for\n   * iteratee shorthands.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @returns {number} Returns the sum.\n   */\n  function baseSum(array, iteratee) {\n    var result,\n        index = -1,\n        length = array.length;\n\n    while (++index < length) {\n      var current = iteratee(array[index]);\n      if (current !== undefined) {\n        result = result === undefined ? current : (result + current);\n      }\n    }\n    return result;\n  }\n\n  /**\n   * The base implementation of `_.times` without support for iteratee shorthands\n   * or max array length checks.\n   *\n   * @private\n   * @param {number} n The number of times to invoke `iteratee`.\n   * @param {Function} iteratee The function invoked per iteration.\n   * @returns {Array} Returns the array of results.\n   */\n  function baseTimes(n, iteratee) {\n    var index = -1,\n        result = Array(n);\n\n    while (++index < n) {\n      result[index] = iteratee(index);\n    }\n    return result;\n  }\n\n  /**\n   * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array\n   * of key-value pairs for `object` corresponding to the property names of `props`.\n   *\n   * @private\n   * @param {Object} object The object to query.\n   * @param {Array} props The property names to get values for.\n   * @returns {Object} Returns the new array of key-value pairs.\n   */\n  function baseToPairs(object, props) {\n    return arrayMap(props, function(key) {\n      return [key, object[key]];\n    });\n  }\n\n  /**\n   * The base implementation of `_.unary` without support for storing wrapper metadata.\n   *\n   * @private\n   * @param {Function} func The function to cap arguments for.\n   * @returns {Function} Returns the new function.\n   */\n  function baseUnary(func) {\n    return function(value) {\n      return func(value);\n    };\n  }\n\n  /**\n   * The base implementation of `_.values` and `_.valuesIn` which creates an\n   * array of `object` property values corresponding to the property names\n   * of `props`.\n   *\n   * @private\n   * @param {Object} object The object to query.\n   * @param {Array} props The property names to get values for.\n   * @returns {Object} Returns the array of property values.\n   */\n  function baseValues(object, props) {\n    return arrayMap(props, function(key) {\n      return object[key];\n    });\n  }\n\n  /**\n   * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol\n   * that is not found in the character symbols.\n   *\n   * @private\n   * @param {Array} strSymbols The string symbols to inspect.\n   * @param {Array} chrSymbols The character symbols to find.\n   * @returns {number} Returns the index of the first unmatched string symbol.\n   */\n  function charsStartIndex(strSymbols, chrSymbols) {\n    var index = -1,\n        length = strSymbols.length;\n\n    while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n    return index;\n  }\n\n  /**\n   * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol\n   * that is not found in the character symbols.\n   *\n   * @private\n   * @param {Array} strSymbols The string symbols to inspect.\n   * @param {Array} chrSymbols The character symbols to find.\n   * @returns {number} Returns the index of the last unmatched string symbol.\n   */\n  function charsEndIndex(strSymbols, chrSymbols) {\n    var index = strSymbols.length;\n\n    while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}\n    return index;\n  }\n\n  /**\n   * Checks if `value` is a global object.\n   *\n   * @private\n   * @param {*} value The value to check.\n   * @returns {null|Object} Returns `value` if it's a global object, else `null`.\n   */\n  function checkGlobal(value) {\n    return (value && value.Object === Object) ? value : null;\n  }\n\n  /**\n   * Compares values to sort them in ascending order.\n   *\n   * @private\n   * @param {*} value The value to compare.\n   * @param {*} other The other value to compare.\n   * @returns {number} Returns the sort order indicator for `value`.\n   */\n  function compareAscending(value, other) {\n    if (value !== other) {\n      var valIsNull = value === null,\n          valIsUndef = value === undefined,\n          valIsReflexive = value === value;\n\n      var othIsNull = other === null,\n          othIsUndef = other === undefined,\n          othIsReflexive = other === other;\n\n      if ((value > other && !othIsNull) || !valIsReflexive ||\n          (valIsNull && !othIsUndef && othIsReflexive) ||\n          (valIsUndef && othIsReflexive)) {\n        return 1;\n      }\n      if ((value < other && !valIsNull) || !othIsReflexive ||\n          (othIsNull && !valIsUndef && valIsReflexive) ||\n          (othIsUndef && valIsReflexive)) {\n        return -1;\n      }\n    }\n    return 0;\n  }\n\n  /**\n   * Used by `_.orderBy` to compare multiple properties of a value to another\n   * and stable sort them.\n   *\n   * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,\n   * specify an order of \"desc\" for descending or \"asc\" for ascending sort order\n   * of corresponding values.\n   *\n   * @private\n   * @param {Object} object The object to compare.\n   * @param {Object} other The other object to compare.\n   * @param {boolean[]|string[]} orders The order to sort by for each property.\n   * @returns {number} Returns the sort order indicator for `object`.\n   */\n  function compareMultiple(object, other, orders) {\n    var index = -1,\n        objCriteria = object.criteria,\n        othCriteria = other.criteria,\n        length = objCriteria.length,\n        ordersLength = orders.length;\n\n    while (++index < length) {\n      var result = compareAscending(objCriteria[index], othCriteria[index]);\n      if (result) {\n        if (index >= ordersLength) {\n          return result;\n        }\n        var order = orders[index];\n        return result * (order == 'desc' ? -1 : 1);\n      }\n    }\n    // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications\n    // that causes it, under certain circumstances, to provide the same value for\n    // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247\n    // for more details.\n    //\n    // This also ensures a stable sort in V8 and other engines.\n    // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.\n    return object.index - other.index;\n  }\n\n  /**\n   * Gets the number of `placeholder` occurrences in `array`.\n   *\n   * @private\n   * @param {Array} array The array to inspect.\n   * @param {*} placeholder The placeholder to search for.\n   * @returns {number} Returns the placeholder count.\n   */\n  function countHolders(array, placeholder) {\n    var length = array.length,\n        result = 0;\n\n    while (length--) {\n      if (array[length] === placeholder) {\n        result++;\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Creates a function that performs a mathematical operation on two values.\n   *\n   * @private\n   * @param {Function} operator The function to perform the operation.\n   * @returns {Function} Returns the new mathematical operation function.\n   */\n  function createMathOperation(operator) {\n    return function(value, other) {\n      var result;\n      if (value === undefined && other === undefined) {\n        return 0;\n      }\n      if (value !== undefined) {\n        result = value;\n      }\n      if (other !== undefined) {\n        result = result === undefined ? other : operator(result, other);\n      }\n      return result;\n    };\n  }\n\n  /**\n   * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters.\n   *\n   * @private\n   * @param {string} letter The matched letter to deburr.\n   * @returns {string} Returns the deburred letter.\n   */\n  function deburrLetter(letter) {\n    return deburredLetters[letter];\n  }\n\n  /**\n   * Used by `_.escape` to convert characters to HTML entities.\n   *\n   * @private\n   * @param {string} chr The matched character to escape.\n   * @returns {string} Returns the escaped character.\n   */\n  function escapeHtmlChar(chr) {\n    return htmlEscapes[chr];\n  }\n\n  /**\n   * Used by `_.template` to escape characters for inclusion in compiled string literals.\n   *\n   * @private\n   * @param {string} chr The matched character to escape.\n   * @returns {string} Returns the escaped character.\n   */\n  function escapeStringChar(chr) {\n    return '\\\\' + stringEscapes[chr];\n  }\n\n  /**\n   * Gets the index at which the first occurrence of `NaN` is found in `array`.\n   *\n   * @private\n   * @param {Array} array The array to search.\n   * @param {number} fromIndex The index to search from.\n   * @param {boolean} [fromRight] Specify iterating from right to left.\n   * @returns {number} Returns the index of the matched `NaN`, else `-1`.\n   */\n  function indexOfNaN(array, fromIndex, fromRight) {\n    var length = array.length,\n        index = fromIndex + (fromRight ? 0 : -1);\n\n    while ((fromRight ? index-- : ++index < length)) {\n      var other = array[index];\n      if (other !== other) {\n        return index;\n      }\n    }\n    return -1;\n  }\n\n  /**\n   * Checks if `value` is a host object in IE < 9.\n   *\n   * @private\n   * @param {*} value The value to check.\n   * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n   */\n  function isHostObject(value) {\n    // Many host objects are `Object` objects that can coerce to strings\n    // despite having improperly defined `toString` methods.\n    var result = false;\n    if (value != null && typeof value.toString != 'function') {\n      try {\n        result = !!(value + '');\n      } catch (e) {}\n    }\n    return result;\n  }\n\n  /**\n   * Checks if `value` is a valid array-like index.\n   *\n   * @private\n   * @param {*} value The value to check.\n   * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n   * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n   */\n  function isIndex(value, length) {\n    value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;\n    length = length == null ? MAX_SAFE_INTEGER : length;\n    return value > -1 && value % 1 == 0 && value < length;\n  }\n\n  /**\n   * Converts `iterator` to an array.\n   *\n   * @private\n   * @param {Object} iterator The iterator to convert.\n   * @returns {Array} Returns the converted array.\n   */\n  function iteratorToArray(iterator) {\n    var data,\n        result = [];\n\n    while (!(data = iterator.next()).done) {\n      result.push(data.value);\n    }\n    return result;\n  }\n\n  /**\n   * Converts `map` to an array.\n   *\n   * @private\n   * @param {Object} map The map to convert.\n   * @returns {Array} Returns the converted array.\n   */\n  function mapToArray(map) {\n    var index = -1,\n        result = Array(map.size);\n\n    map.forEach(function(value, key) {\n      result[++index] = [key, value];\n    });\n    return result;\n  }\n\n  /**\n   * Replaces all `placeholder` elements in `array` with an internal placeholder\n   * and returns an array of their indexes.\n   *\n   * @private\n   * @param {Array} array The array to modify.\n   * @param {*} placeholder The placeholder to replace.\n   * @returns {Array} Returns the new array of placeholder indexes.\n   */\n  function replaceHolders(array, placeholder) {\n    var index = -1,\n        length = array.length,\n        resIndex = 0,\n        result = [];\n\n    while (++index < length) {\n      var value = array[index];\n      if (value === placeholder || value === PLACEHOLDER) {\n        array[index] = PLACEHOLDER;\n        result[resIndex++] = index;\n      }\n    }\n    return result;\n  }\n\n  /**\n   * Converts `set` to an array.\n   *\n   * @private\n   * @param {Object} set The set to convert.\n   * @returns {Array} Returns the converted array.\n   */\n  function setToArray(set) {\n    var index = -1,\n        result = Array(set.size);\n\n    set.forEach(function(value) {\n      result[++index] = value;\n    });\n    return result;\n  }\n\n  /**\n   * Gets the number of symbols in `string`.\n   *\n   * @private\n   * @param {string} string The string to inspect.\n   * @returns {number} Returns the string size.\n   */\n  function stringSize(string) {\n    if (!(string && reHasComplexSymbol.test(string))) {\n      return string.length;\n    }\n    var result = reComplexSymbol.lastIndex = 0;\n    while (reComplexSymbol.test(string)) {\n      result++;\n    }\n    return result;\n  }\n\n  /**\n   * Converts `string` to an array.\n   *\n   * @private\n   * @param {string} string The string to convert.\n   * @returns {Array} Returns the converted array.\n   */\n  function stringToArray(string) {\n    return string.match(reComplexSymbol);\n  }\n\n  /**\n   * Used by `_.unescape` to convert HTML entities to characters.\n   *\n   * @private\n   * @param {string} chr The matched character to unescape.\n   * @returns {string} Returns the unescaped character.\n   */\n  function unescapeHtmlChar(chr) {\n    return htmlUnescapes[chr];\n  }\n\n  /*--------------------------------------------------------------------------*/\n\n  /**\n   * Create a new pristine `lodash` function using the `context` object.\n   *\n   * @static\n   * @memberOf _\n   * @since 1.1.0\n   * @category Util\n   * @param {Object} [context=root] The context object.\n   * @returns {Function} Returns a new `lodash` function.\n   * @example\n   *\n   * _.mixin({ 'foo': _.constant('foo') });\n   *\n   * var lodash = _.runInContext();\n   * lodash.mixin({ 'bar': lodash.constant('bar') });\n   *\n   * _.isFunction(_.foo);\n   * // => true\n   * _.isFunction(_.bar);\n   * // => false\n   *\n   * lodash.isFunction(lodash.foo);\n   * // => false\n   * lodash.isFunction(lodash.bar);\n   * // => true\n   *\n   * // Use `context` to mock `Date#getTime` use in `_.now`.\n   * var mock = _.runInContext({\n   *   'Date': function() {\n   *     return { 'getTime': getTimeMock };\n   *   }\n   * });\n   *\n   * // Create a suped-up `defer` in Node.js.\n   * var defer = _.runInContext({ 'setTimeout': setImmediate }).defer;\n   */\n  function runInContext(context) {\n    context = context ? _.defaults({}, context, _.pick(root, contextProps)) : root;\n\n    /** Built-in constructor references. */\n    var Date = context.Date,\n        Error = context.Error,\n        Math = context.Math,\n        RegExp = context.RegExp,\n        TypeError = context.TypeError;\n\n    /** Used for built-in method references. */\n    var arrayProto = context.Array.prototype,\n        objectProto = context.Object.prototype,\n        stringProto = context.String.prototype;\n\n    /** Used to resolve the decompiled source of functions. */\n    var funcToString = context.Function.prototype.toString;\n\n    /** Used to check objects for own properties. */\n    var hasOwnProperty = objectProto.hasOwnProperty;\n\n    /** Used to generate unique IDs. */\n    var idCounter = 0;\n\n    /** Used to infer the `Object` constructor. */\n    var objectCtorString = funcToString.call(Object);\n\n    /**\n     * Used to resolve the\n     * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)\n     * of values.\n     */\n    var objectToString = objectProto.toString;\n\n    /** Used to restore the original `_` reference in `_.noConflict`. */\n    var oldDash = root._;\n\n    /** Used to detect if a method is native. */\n    var reIsNative = RegExp('^' +\n      funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n      .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n    );\n\n    /** Built-in value references. */\n    var Buffer = moduleExports ? context.Buffer : undefined,\n        Reflect = context.Reflect,\n        Symbol = context.Symbol,\n        Uint8Array = context.Uint8Array,\n        clearTimeout = context.clearTimeout,\n        enumerate = Reflect ? Reflect.enumerate : undefined,\n        getOwnPropertySymbols = Object.getOwnPropertySymbols,\n        iteratorSymbol = typeof (iteratorSymbol = Symbol && Symbol.iterator) == 'symbol' ? iteratorSymbol : undefined,\n        objectCreate = Object.create,\n        propertyIsEnumerable = objectProto.propertyIsEnumerable,\n        setTimeout = context.setTimeout,\n        splice = arrayProto.splice;\n\n    /* Built-in method references for those with the same name as other `lodash` methods. */\n    var nativeCeil = Math.ceil,\n        nativeFloor = Math.floor,\n        nativeGetPrototype = Object.getPrototypeOf,\n        nativeIsFinite = context.isFinite,\n        nativeJoin = arrayProto.join,\n        nativeKeys = Object.keys,\n        nativeMax = Math.max,\n        nativeMin = Math.min,\n        nativeParseInt = context.parseInt,\n        nativeRandom = Math.random,\n        nativeReplace = stringProto.replace,\n        nativeReverse = arrayProto.reverse,\n        nativeSplit = stringProto.split;\n\n    /* Built-in method references that are verified to be native. */\n    var DataView = getNative(context, 'DataView'),\n        Map = getNative(context, 'Map'),\n        Promise = getNative(context, 'Promise'),\n        Set = getNative(context, 'Set'),\n        WeakMap = getNative(context, 'WeakMap'),\n        nativeCreate = getNative(Object, 'create');\n\n    /** Used to store function metadata. */\n    var metaMap = WeakMap && new WeakMap;\n\n    /** Detect if properties shadowing those on `Object.prototype` are non-enumerable. */\n    var nonEnumShadows = !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf');\n\n    /** Used to lookup unminified function names. */\n    var realNames = {};\n\n    /** Used to detect maps, sets, and weakmaps. */\n    var dataViewCtorString = toSource(DataView),\n        mapCtorString = toSource(Map),\n        promiseCtorString = toSource(Promise),\n        setCtorString = toSource(Set),\n        weakMapCtorString = toSource(WeakMap);\n\n    /** Used to convert symbols to primitives and strings. */\n    var symbolProto = Symbol ? Symbol.prototype : undefined,\n        symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,\n        symbolToString = symbolProto ? symbolProto.toString : undefined;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates a `lodash` object which wraps `value` to enable implicit method\n     * chain sequences. Methods that operate on and return arrays, collections,\n     * and functions can be chained together. Methods that retrieve a single value\n     * or may return a primitive value will automatically end the chain sequence\n     * and return the unwrapped value. Otherwise, the value must be unwrapped\n     * with `_#value`.\n     *\n     * Explicit chain sequences, which must be unwrapped with `_#value`, may be\n     * enabled using `_.chain`.\n     *\n     * The execution of chained methods is lazy, that is, it's deferred until\n     * `_#value` is implicitly or explicitly called.\n     *\n     * Lazy evaluation allows several methods to support shortcut fusion.\n     * Shortcut fusion is an optimization to merge iteratee calls; this avoids\n     * the creation of intermediate arrays and can greatly reduce the number of\n     * iteratee executions. Sections of a chain sequence qualify for shortcut\n     * fusion if the section is applied to an array of at least `200` elements\n     * and any iteratees accept only one argument. The heuristic for whether a\n     * section qualifies for shortcut fusion is subject to change.\n     *\n     * Chaining is supported in custom builds as long as the `_#value` method is\n     * directly or indirectly included in the build.\n     *\n     * In addition to lodash methods, wrappers have `Array` and `String` methods.\n     *\n     * The wrapper `Array` methods are:\n     * `concat`, `join`, `pop`, `push`, `shift`, `sort`, `splice`, and `unshift`\n     *\n     * The wrapper `String` methods are:\n     * `replace` and `split`\n     *\n     * The wrapper methods that support shortcut fusion are:\n     * `at`, `compact`, `drop`, `dropRight`, `dropWhile`, `filter`, `find`,\n     * `findLast`, `head`, `initial`, `last`, `map`, `reject`, `reverse`, `slice`,\n     * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray`\n     *\n     * The chainable wrapper methods are:\n     * `after`, `ary`, `assign`, `assignIn`, `assignInWith`, `assignWith`, `at`,\n     * `before`, `bind`, `bindAll`, `bindKey`, `castArray`, `chain`, `chunk`,\n     * `commit`, `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`,\n     * `curry`, `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`,\n     * `difference`, `differenceBy`, `differenceWith`, `drop`, `dropRight`,\n     * `dropRightWhile`, `dropWhile`, `extend`, `extendWith`, `fill`, `filter`,\n     * `flatMap`, `flatMapDeep`, `flatMapDepth`, `flatten`, `flattenDeep`,\n     * `flattenDepth`, `flip`, `flow`, `flowRight`, `fromPairs`, `functions`,\n     * `functionsIn`, `groupBy`, `initial`, `intersection`, `intersectionBy`,\n     * `intersectionWith`, `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`,\n     * `keys`, `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`,\n     * `memoize`, `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`,\n     * `nthArg`, `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`,\n     * `overEvery`, `overSome`, `partial`, `partialRight`, `partition`, `pick`,\n     * `pickBy`, `plant`, `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`,\n     * `pullAllWith`, `pullAt`, `push`, `range`, `rangeRight`, `rearg`, `reject`,\n     * `remove`, `rest`, `reverse`, `sampleSize`, `set`, `setWith`, `shuffle`,\n     * `slice`, `sort`, `sortBy`, `splice`, `spread`, `tail`, `take`, `takeRight`,\n     * `takeRightWhile`, `takeWhile`, `tap`, `throttle`, `thru`, `toArray`,\n     * `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, `transform`, `unary`,\n     * `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`,\n     * `unshift`, `unzip`, `unzipWith`, `update`, `updateWith`, `values`,\n     * `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`,\n     * `zipObject`, `zipObjectDeep`, and `zipWith`\n     *\n     * The wrapper methods that are **not** chainable by default are:\n     * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`,\n     * `cloneDeep`, `cloneDeepWith`, `cloneWith`, `deburr`, `divide`, `each`,\n     * `eachRight`, `endsWith`, `eq`, `escape`, `escapeRegExp`, `every`, `find`,\n     * `findIndex`, `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `first`,\n     * `floor`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,\n     * `forOwnRight`, `get`, `gt`, `gte`, `has`, `hasIn`, `head`, `identity`,\n     * `includes`, `indexOf`, `inRange`, `invoke`, `isArguments`, `isArray`,\n     * `isArrayBuffer`, `isArrayLike`, `isArrayLikeObject`, `isBoolean`, `isBuffer`,\n     * `isDate`, `isElement`, `isEmpty`, `isEqual`, `isEqualWith`, `isError`,\n     * `isFinite`, `isFunction`, `isInteger`, `isLength`, `isMap`, `isMatch`,\n     * `isMatchWith`, `isNaN`, `isNative`, `isNil`, `isNull`, `isNumber`,\n     * `isObject`, `isObjectLike`, `isPlainObject`, `isRegExp`, `isSafeInteger`,\n     * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`,\n     * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`,\n     * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`,\n     * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`,\n     * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`,\n     * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`,\n     * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`,\n     * `startsWith`, `subtract`, `sum`, `sumBy`, `template`, `times`, `toInteger`,\n     * `toJSON`, `toLength`, `toLower`, `toNumber`, `toSafeInteger`, `toString`,\n     * `toUpper`, `trim`, `trimEnd`, `trimStart`, `truncate`, `unescape`,\n     * `uniqueId`, `upperCase`, `upperFirst`, `value`, and `words`\n     *\n     * @name _\n     * @constructor\n     * @category Seq\n     * @param {*} value The value to wrap in a `lodash` instance.\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * function square(n) {\n     *   return n * n;\n     * }\n     *\n     * var wrapped = _([1, 2, 3]);\n     *\n     * // Returns an unwrapped value.\n     * wrapped.reduce(_.add);\n     * // => 6\n     *\n     * // Returns a wrapped value.\n     * var squares = wrapped.map(square);\n     *\n     * _.isArray(squares);\n     * // => false\n     *\n     * _.isArray(squares.value());\n     * // => true\n     */\n    function lodash(value) {\n      if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {\n        if (value instanceof LodashWrapper) {\n          return value;\n        }\n        if (hasOwnProperty.call(value, '__wrapped__')) {\n          return wrapperClone(value);\n        }\n      }\n      return new LodashWrapper(value);\n    }\n\n    /**\n     * The function whose prototype chain sequence wrappers inherit from.\n     *\n     * @private\n     */\n    function baseLodash() {\n      // No operation performed.\n    }\n\n    /**\n     * The base constructor for creating `lodash` wrapper objects.\n     *\n     * @private\n     * @param {*} value The value to wrap.\n     * @param {boolean} [chainAll] Enable explicit method chain sequences.\n     */\n    function LodashWrapper(value, chainAll) {\n      this.__wrapped__ = value;\n      this.__actions__ = [];\n      this.__chain__ = !!chainAll;\n      this.__index__ = 0;\n      this.__values__ = undefined;\n    }\n\n    /**\n     * By default, the template delimiters used by lodash are like those in\n     * embedded Ruby (ERB). Change the following template settings to use\n     * alternative delimiters.\n     *\n     * @static\n     * @memberOf _\n     * @type {Object}\n     */\n    lodash.templateSettings = {\n\n      /**\n       * Used to detect `data` property values to be HTML-escaped.\n       *\n       * @memberOf _.templateSettings\n       * @type {RegExp}\n       */\n      'escape': reEscape,\n\n      /**\n       * Used to detect code to be evaluated.\n       *\n       * @memberOf _.templateSettings\n       * @type {RegExp}\n       */\n      'evaluate': reEvaluate,\n\n      /**\n       * Used to detect `data` property values to inject.\n       *\n       * @memberOf _.templateSettings\n       * @type {RegExp}\n       */\n      'interpolate': reInterpolate,\n\n      /**\n       * Used to reference the data object in the template text.\n       *\n       * @memberOf _.templateSettings\n       * @type {string}\n       */\n      'variable': '',\n\n      /**\n       * Used to import variables into the compiled template.\n       *\n       * @memberOf _.templateSettings\n       * @type {Object}\n       */\n      'imports': {\n\n        /**\n         * A reference to the `lodash` function.\n         *\n         * @memberOf _.templateSettings.imports\n         * @type {Function}\n         */\n        '_': lodash\n      }\n    };\n\n    // Ensure wrappers are instances of `baseLodash`.\n    lodash.prototype = baseLodash.prototype;\n    lodash.prototype.constructor = lodash;\n\n    LodashWrapper.prototype = baseCreate(baseLodash.prototype);\n    LodashWrapper.prototype.constructor = LodashWrapper;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates a lazy wrapper object which wraps `value` to enable lazy evaluation.\n     *\n     * @private\n     * @constructor\n     * @param {*} value The value to wrap.\n     */\n    function LazyWrapper(value) {\n      this.__wrapped__ = value;\n      this.__actions__ = [];\n      this.__dir__ = 1;\n      this.__filtered__ = false;\n      this.__iteratees__ = [];\n      this.__takeCount__ = MAX_ARRAY_LENGTH;\n      this.__views__ = [];\n    }\n\n    /**\n     * Creates a clone of the lazy wrapper object.\n     *\n     * @private\n     * @name clone\n     * @memberOf LazyWrapper\n     * @returns {Object} Returns the cloned `LazyWrapper` object.\n     */\n    function lazyClone() {\n      var result = new LazyWrapper(this.__wrapped__);\n      result.__actions__ = copyArray(this.__actions__);\n      result.__dir__ = this.__dir__;\n      result.__filtered__ = this.__filtered__;\n      result.__iteratees__ = copyArray(this.__iteratees__);\n      result.__takeCount__ = this.__takeCount__;\n      result.__views__ = copyArray(this.__views__);\n      return result;\n    }\n\n    /**\n     * Reverses the direction of lazy iteration.\n     *\n     * @private\n     * @name reverse\n     * @memberOf LazyWrapper\n     * @returns {Object} Returns the new reversed `LazyWrapper` object.\n     */\n    function lazyReverse() {\n      if (this.__filtered__) {\n        var result = new LazyWrapper(this);\n        result.__dir__ = -1;\n        result.__filtered__ = true;\n      } else {\n        result = this.clone();\n        result.__dir__ *= -1;\n      }\n      return result;\n    }\n\n    /**\n     * Extracts the unwrapped value from its lazy wrapper.\n     *\n     * @private\n     * @name value\n     * @memberOf LazyWrapper\n     * @returns {*} Returns the unwrapped value.\n     */\n    function lazyValue() {\n      var array = this.__wrapped__.value(),\n          dir = this.__dir__,\n          isArr = isArray(array),\n          isRight = dir < 0,\n          arrLength = isArr ? array.length : 0,\n          view = getView(0, arrLength, this.__views__),\n          start = view.start,\n          end = view.end,\n          length = end - start,\n          index = isRight ? end : (start - 1),\n          iteratees = this.__iteratees__,\n          iterLength = iteratees.length,\n          resIndex = 0,\n          takeCount = nativeMin(length, this.__takeCount__);\n\n      if (!isArr || arrLength < LARGE_ARRAY_SIZE ||\n          (arrLength == length && takeCount == length)) {\n        return baseWrapperValue(array, this.__actions__);\n      }\n      var result = [];\n\n      outer:\n      while (length-- && resIndex < takeCount) {\n        index += dir;\n\n        var iterIndex = -1,\n            value = array[index];\n\n        while (++iterIndex < iterLength) {\n          var data = iteratees[iterIndex],\n              iteratee = data.iteratee,\n              type = data.type,\n              computed = iteratee(value);\n\n          if (type == LAZY_MAP_FLAG) {\n            value = computed;\n          } else if (!computed) {\n            if (type == LAZY_FILTER_FLAG) {\n              continue outer;\n            } else {\n              break outer;\n            }\n          }\n        }\n        result[resIndex++] = value;\n      }\n      return result;\n    }\n\n    // Ensure `LazyWrapper` is an instance of `baseLodash`.\n    LazyWrapper.prototype = baseCreate(baseLodash.prototype);\n    LazyWrapper.prototype.constructor = LazyWrapper;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates a hash object.\n     *\n     * @private\n     * @constructor\n     * @returns {Object} Returns the new hash object.\n     */\n    function Hash() {}\n\n    /**\n     * Removes `key` and its value from the hash.\n     *\n     * @private\n     * @param {Object} hash The hash to modify.\n     * @param {string} key The key of the value to remove.\n     * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n     */\n    function hashDelete(hash, key) {\n      return hashHas(hash, key) && delete hash[key];\n    }\n\n    /**\n     * Gets the hash value for `key`.\n     *\n     * @private\n     * @param {Object} hash The hash to query.\n     * @param {string} key The key of the value to get.\n     * @returns {*} Returns the entry value.\n     */\n    function hashGet(hash, key) {\n      if (nativeCreate) {\n        var result = hash[key];\n        return result === HASH_UNDEFINED ? undefined : result;\n      }\n      return hasOwnProperty.call(hash, key) ? hash[key] : undefined;\n    }\n\n    /**\n     * Checks if a hash value for `key` exists.\n     *\n     * @private\n     * @param {Object} hash The hash to query.\n     * @param {string} key The key of the entry to check.\n     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n     */\n    function hashHas(hash, key) {\n      return nativeCreate ? hash[key] !== undefined : hasOwnProperty.call(hash, key);\n    }\n\n    /**\n     * Sets the hash `key` to `value`.\n     *\n     * @private\n     * @param {Object} hash The hash to modify.\n     * @param {string} key The key of the value to set.\n     * @param {*} value The value to set.\n     */\n    function hashSet(hash, key, value) {\n      hash[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n    }\n\n    // Avoid inheriting from `Object.prototype` when possible.\n    Hash.prototype = nativeCreate ? nativeCreate(null) : objectProto;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates a map cache object to store key-value pairs.\n     *\n     * @private\n     * @constructor\n     * @param {Array} [values] The values to cache.\n     */\n    function MapCache(values) {\n      var index = -1,\n          length = values ? values.length : 0;\n\n      this.clear();\n      while (++index < length) {\n        var entry = values[index];\n        this.set(entry[0], entry[1]);\n      }\n    }\n\n    /**\n     * Removes all key-value entries from the map.\n     *\n     * @private\n     * @name clear\n     * @memberOf MapCache\n     */\n    function mapClear() {\n      this.__data__ = {\n        'hash': new Hash,\n        'map': Map ? new Map : [],\n        'string': new Hash\n      };\n    }\n\n    /**\n     * Removes `key` and its value from the map.\n     *\n     * @private\n     * @name delete\n     * @memberOf MapCache\n     * @param {string} key The key of the value to remove.\n     * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n     */\n    function mapDelete(key) {\n      var data = this.__data__;\n      if (isKeyable(key)) {\n        return hashDelete(typeof key == 'string' ? data.string : data.hash, key);\n      }\n      return Map ? data.map['delete'](key) : assocDelete(data.map, key);\n    }\n\n    /**\n     * Gets the map value for `key`.\n     *\n     * @private\n     * @name get\n     * @memberOf MapCache\n     * @param {string} key The key of the value to get.\n     * @returns {*} Returns the entry value.\n     */\n    function mapGet(key) {\n      var data = this.__data__;\n      if (isKeyable(key)) {\n        return hashGet(typeof key == 'string' ? data.string : data.hash, key);\n      }\n      return Map ? data.map.get(key) : assocGet(data.map, key);\n    }\n\n    /**\n     * Checks if a map value for `key` exists.\n     *\n     * @private\n     * @name has\n     * @memberOf MapCache\n     * @param {string} key The key of the entry to check.\n     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n     */\n    function mapHas(key) {\n      var data = this.__data__;\n      if (isKeyable(key)) {\n        return hashHas(typeof key == 'string' ? data.string : data.hash, key);\n      }\n      return Map ? data.map.has(key) : assocHas(data.map, key);\n    }\n\n    /**\n     * Sets the map `key` to `value`.\n     *\n     * @private\n     * @name set\n     * @memberOf MapCache\n     * @param {string} key The key of the value to set.\n     * @param {*} value The value to set.\n     * @returns {Object} Returns the map cache instance.\n     */\n    function mapSet(key, value) {\n      var data = this.__data__;\n      if (isKeyable(key)) {\n        hashSet(typeof key == 'string' ? data.string : data.hash, key, value);\n      } else if (Map) {\n        data.map.set(key, value);\n      } else {\n        assocSet(data.map, key, value);\n      }\n      return this;\n    }\n\n    // Add methods to `MapCache`.\n    MapCache.prototype.clear = mapClear;\n    MapCache.prototype['delete'] = mapDelete;\n    MapCache.prototype.get = mapGet;\n    MapCache.prototype.has = mapHas;\n    MapCache.prototype.set = mapSet;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     *\n     * Creates a set cache object to store unique values.\n     *\n     * @private\n     * @constructor\n     * @param {Array} [values] The values to cache.\n     */\n    function SetCache(values) {\n      var index = -1,\n          length = values ? values.length : 0;\n\n      this.__data__ = new MapCache;\n      while (++index < length) {\n        this.push(values[index]);\n      }\n    }\n\n    /**\n     * Checks if `value` is in `cache`.\n     *\n     * @private\n     * @param {Object} cache The set cache to search.\n     * @param {*} value The value to search for.\n     * @returns {number} Returns `true` if `value` is found, else `false`.\n     */\n    function cacheHas(cache, value) {\n      var map = cache.__data__;\n      if (isKeyable(value)) {\n        var data = map.__data__,\n            hash = typeof value == 'string' ? data.string : data.hash;\n\n        return hash[value] === HASH_UNDEFINED;\n      }\n      return map.has(value);\n    }\n\n    /**\n     * Adds `value` to the set cache.\n     *\n     * @private\n     * @name push\n     * @memberOf SetCache\n     * @param {*} value The value to cache.\n     */\n    function cachePush(value) {\n      var map = this.__data__;\n      if (isKeyable(value)) {\n        var data = map.__data__,\n            hash = typeof value == 'string' ? data.string : data.hash;\n\n        hash[value] = HASH_UNDEFINED;\n      }\n      else {\n        map.set(value, HASH_UNDEFINED);\n      }\n    }\n\n    // Add methods to `SetCache`.\n    SetCache.prototype.push = cachePush;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates a stack cache object to store key-value pairs.\n     *\n     * @private\n     * @constructor\n     * @param {Array} [values] The values to cache.\n     */\n    function Stack(values) {\n      var index = -1,\n          length = values ? values.length : 0;\n\n      this.clear();\n      while (++index < length) {\n        var entry = values[index];\n        this.set(entry[0], entry[1]);\n      }\n    }\n\n    /**\n     * Removes all key-value entries from the stack.\n     *\n     * @private\n     * @name clear\n     * @memberOf Stack\n     */\n    function stackClear() {\n      this.__data__ = { 'array': [], 'map': null };\n    }\n\n    /**\n     * Removes `key` and its value from the stack.\n     *\n     * @private\n     * @name delete\n     * @memberOf Stack\n     * @param {string} key The key of the value to remove.\n     * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n     */\n    function stackDelete(key) {\n      var data = this.__data__,\n          array = data.array;\n\n      return array ? assocDelete(array, key) : data.map['delete'](key);\n    }\n\n    /**\n     * Gets the stack value for `key`.\n     *\n     * @private\n     * @name get\n     * @memberOf Stack\n     * @param {string} key The key of the value to get.\n     * @returns {*} Returns the entry value.\n     */\n    function stackGet(key) {\n      var data = this.__data__,\n          array = data.array;\n\n      return array ? assocGet(array, key) : data.map.get(key);\n    }\n\n    /**\n     * Checks if a stack value for `key` exists.\n     *\n     * @private\n     * @name has\n     * @memberOf Stack\n     * @param {string} key The key of the entry to check.\n     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n     */\n    function stackHas(key) {\n      var data = this.__data__,\n          array = data.array;\n\n      return array ? assocHas(array, key) : data.map.has(key);\n    }\n\n    /**\n     * Sets the stack `key` to `value`.\n     *\n     * @private\n     * @name set\n     * @memberOf Stack\n     * @param {string} key The key of the value to set.\n     * @param {*} value The value to set.\n     * @returns {Object} Returns the stack cache instance.\n     */\n    function stackSet(key, value) {\n      var data = this.__data__,\n          array = data.array;\n\n      if (array) {\n        if (array.length < (LARGE_ARRAY_SIZE - 1)) {\n          assocSet(array, key, value);\n        } else {\n          data.array = null;\n          data.map = new MapCache(array);\n        }\n      }\n      var map = data.map;\n      if (map) {\n        map.set(key, value);\n      }\n      return this;\n    }\n\n    // Add methods to `Stack`.\n    Stack.prototype.clear = stackClear;\n    Stack.prototype['delete'] = stackDelete;\n    Stack.prototype.get = stackGet;\n    Stack.prototype.has = stackHas;\n    Stack.prototype.set = stackSet;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Removes `key` and its value from the associative array.\n     *\n     * @private\n     * @param {Array} array The array to modify.\n     * @param {string} key The key of the value to remove.\n     * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n     */\n    function assocDelete(array, key) {\n      var index = assocIndexOf(array, key);\n      if (index < 0) {\n        return false;\n      }\n      var lastIndex = array.length - 1;\n      if (index == lastIndex) {\n        array.pop();\n      } else {\n        splice.call(array, index, 1);\n      }\n      return true;\n    }\n\n    /**\n     * Gets the associative array value for `key`.\n     *\n     * @private\n     * @param {Array} array The array to query.\n     * @param {string} key The key of the value to get.\n     * @returns {*} Returns the entry value.\n     */\n    function assocGet(array, key) {\n      var index = assocIndexOf(array, key);\n      return index < 0 ? undefined : array[index][1];\n    }\n\n    /**\n     * Checks if an associative array value for `key` exists.\n     *\n     * @private\n     * @param {Array} array The array to query.\n     * @param {string} key The key of the entry to check.\n     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n     */\n    function assocHas(array, key) {\n      return assocIndexOf(array, key) > -1;\n    }\n\n    /**\n     * Gets the index at which the `key` is found in `array` of key-value pairs.\n     *\n     * @private\n     * @param {Array} array The array to search.\n     * @param {*} key The key to search for.\n     * @returns {number} Returns the index of the matched value, else `-1`.\n     */\n    function assocIndexOf(array, key) {\n      var length = array.length;\n      while (length--) {\n        if (eq(array[length][0], key)) {\n          return length;\n        }\n      }\n      return -1;\n    }\n\n    /**\n     * Sets the associative array `key` to `value`.\n     *\n     * @private\n     * @param {Array} array The array to modify.\n     * @param {string} key The key of the value to set.\n     * @param {*} value The value to set.\n     */\n    function assocSet(array, key, value) {\n      var index = assocIndexOf(array, key);\n      if (index < 0) {\n        array.push([key, value]);\n      } else {\n        array[index][1] = value;\n      }\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Used by `_.defaults` to customize its `_.assignIn` use.\n     *\n     * @private\n     * @param {*} objValue The destination value.\n     * @param {*} srcValue The source value.\n     * @param {string} key The key of the property to assign.\n     * @param {Object} object The parent object of `objValue`.\n     * @returns {*} Returns the value to assign.\n     */\n    function assignInDefaults(objValue, srcValue, key, object) {\n      if (objValue === undefined ||\n          (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {\n        return srcValue;\n      }\n      return objValue;\n    }\n\n    /**\n     * This function is like `assignValue` except that it doesn't assign\n     * `undefined` values.\n     *\n     * @private\n     * @param {Object} object The object to modify.\n     * @param {string} key The key of the property to assign.\n     * @param {*} value The value to assign.\n     */\n    function assignMergeValue(object, key, value) {\n      if ((value !== undefined && !eq(object[key], value)) ||\n          (typeof key == 'number' && value === undefined && !(key in object))) {\n        object[key] = value;\n      }\n    }\n\n    /**\n     * Assigns `value` to `key` of `object` if the existing value is not equivalent\n     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons.\n     *\n     * @private\n     * @param {Object} object The object to modify.\n     * @param {string} key The key of the property to assign.\n     * @param {*} value The value to assign.\n     */\n    function assignValue(object, key, value) {\n      var objValue = object[key];\n      if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||\n          (value === undefined && !(key in object))) {\n        object[key] = value;\n      }\n    }\n\n    /**\n     * Aggregates elements of `collection` on `accumulator` with keys transformed\n     * by `iteratee` and values set by `setter`.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} setter The function to set `accumulator` values.\n     * @param {Function} iteratee The iteratee to transform keys.\n     * @param {Object} accumulator The initial aggregated object.\n     * @returns {Function} Returns `accumulator`.\n     */\n    function baseAggregator(collection, setter, iteratee, accumulator) {\n      baseEach(collection, function(value, key, collection) {\n        setter(accumulator, value, iteratee(value), collection);\n      });\n      return accumulator;\n    }\n\n    /**\n     * The base implementation of `_.assign` without support for multiple sources\n     * or `customizer` functions.\n     *\n     * @private\n     * @param {Object} object The destination object.\n     * @param {Object} source The source object.\n     * @returns {Object} Returns `object`.\n     */\n    function baseAssign(object, source) {\n      return object && copyObject(source, keys(source), object);\n    }\n\n    /**\n     * The base implementation of `_.at` without support for individual paths.\n     *\n     * @private\n     * @param {Object} object The object to iterate over.\n     * @param {string[]} paths The property paths of elements to pick.\n     * @returns {Array} Returns the new array of picked elements.\n     */\n    function baseAt(object, paths) {\n      var index = -1,\n          isNil = object == null,\n          length = paths.length,\n          result = Array(length);\n\n      while (++index < length) {\n        result[index] = isNil ? undefined : get(object, paths[index]);\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.clamp` which doesn't coerce arguments to numbers.\n     *\n     * @private\n     * @param {number} number The number to clamp.\n     * @param {number} [lower] The lower bound.\n     * @param {number} upper The upper bound.\n     * @returns {number} Returns the clamped number.\n     */\n    function baseClamp(number, lower, upper) {\n      if (number === number) {\n        if (upper !== undefined) {\n          number = number <= upper ? number : upper;\n        }\n        if (lower !== undefined) {\n          number = number >= lower ? number : lower;\n        }\n      }\n      return number;\n    }\n\n    /**\n     * The base implementation of `_.clone` and `_.cloneDeep` which tracks\n     * traversed objects.\n     *\n     * @private\n     * @param {*} value The value to clone.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @param {boolean} [isFull] Specify a clone including symbols.\n     * @param {Function} [customizer] The function to customize cloning.\n     * @param {string} [key] The key of `value`.\n     * @param {Object} [object] The parent object of `value`.\n     * @param {Object} [stack] Tracks traversed objects and their clone counterparts.\n     * @returns {*} Returns the cloned value.\n     */\n    function baseClone(value, isDeep, isFull, customizer, key, object, stack) {\n      var result;\n      if (customizer) {\n        result = object ? customizer(value, key, object, stack) : customizer(value);\n      }\n      if (result !== undefined) {\n        return result;\n      }\n      if (!isObject(value)) {\n        return value;\n      }\n      var isArr = isArray(value);\n      if (isArr) {\n        result = initCloneArray(value);\n        if (!isDeep) {\n          return copyArray(value, result);\n        }\n      } else {\n        var tag = getTag(value),\n            isFunc = tag == funcTag || tag == genTag;\n\n        if (isBuffer(value)) {\n          return cloneBuffer(value, isDeep);\n        }\n        if (tag == objectTag || tag == argsTag || (isFunc && !object)) {\n          if (isHostObject(value)) {\n            return object ? value : {};\n          }\n          result = initCloneObject(isFunc ? {} : value);\n          if (!isDeep) {\n            return copySymbols(value, baseAssign(result, value));\n          }\n        } else {\n          if (!cloneableTags[tag]) {\n            return object ? value : {};\n          }\n          result = initCloneByTag(value, tag, baseClone, isDeep);\n        }\n      }\n      // Check for circular references and return its corresponding clone.\n      stack || (stack = new Stack);\n      var stacked = stack.get(value);\n      if (stacked) {\n        return stacked;\n      }\n      stack.set(value, result);\n\n      if (!isArr) {\n        var props = isFull ? getAllKeys(value) : keys(value);\n      }\n      // Recursively populate clone (susceptible to call stack limits).\n      arrayEach(props || value, function(subValue, key) {\n        if (props) {\n          key = subValue;\n          subValue = value[key];\n        }\n        assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack));\n      });\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.conforms` which doesn't clone `source`.\n     *\n     * @private\n     * @param {Object} source The object of property predicates to conform to.\n     * @returns {Function} Returns the new function.\n     */\n    function baseConforms(source) {\n      var props = keys(source),\n          length = props.length;\n\n      return function(object) {\n        if (object == null) {\n          return !length;\n        }\n        var index = length;\n        while (index--) {\n          var key = props[index],\n              predicate = source[key],\n              value = object[key];\n\n          if ((value === undefined &&\n              !(key in Object(object))) || !predicate(value)) {\n            return false;\n          }\n        }\n        return true;\n      };\n    }\n\n    /**\n     * The base implementation of `_.create` without support for assigning\n     * properties to the created object.\n     *\n     * @private\n     * @param {Object} prototype The object to inherit from.\n     * @returns {Object} Returns the new object.\n     */\n    function baseCreate(proto) {\n      return isObject(proto) ? objectCreate(proto) : {};\n    }\n\n    /**\n     * The base implementation of `_.delay` and `_.defer` which accepts an array\n     * of `func` arguments.\n     *\n     * @private\n     * @param {Function} func The function to delay.\n     * @param {number} wait The number of milliseconds to delay invocation.\n     * @param {Object} args The arguments to provide to `func`.\n     * @returns {number} Returns the timer id.\n     */\n    function baseDelay(func, wait, args) {\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      return setTimeout(function() { func.apply(undefined, args); }, wait);\n    }\n\n    /**\n     * The base implementation of methods like `_.difference` without support\n     * for excluding multiple arrays or iteratee shorthands.\n     *\n     * @private\n     * @param {Array} array The array to inspect.\n     * @param {Array} values The values to exclude.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of filtered values.\n     */\n    function baseDifference(array, values, iteratee, comparator) {\n      var index = -1,\n          includes = arrayIncludes,\n          isCommon = true,\n          length = array.length,\n          result = [],\n          valuesLength = values.length;\n\n      if (!length) {\n        return result;\n      }\n      if (iteratee) {\n        values = arrayMap(values, baseUnary(iteratee));\n      }\n      if (comparator) {\n        includes = arrayIncludesWith;\n        isCommon = false;\n      }\n      else if (values.length >= LARGE_ARRAY_SIZE) {\n        includes = cacheHas;\n        isCommon = false;\n        values = new SetCache(values);\n      }\n      outer:\n      while (++index < length) {\n        var value = array[index],\n            computed = iteratee ? iteratee(value) : value;\n\n        if (isCommon && computed === computed) {\n          var valuesIndex = valuesLength;\n          while (valuesIndex--) {\n            if (values[valuesIndex] === computed) {\n              continue outer;\n            }\n          }\n          result.push(value);\n        }\n        else if (!includes(values, computed, comparator)) {\n          result.push(value);\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.forEach` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @returns {Array|Object} Returns `collection`.\n     */\n    var baseEach = createBaseEach(baseForOwn);\n\n    /**\n     * The base implementation of `_.forEachRight` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @returns {Array|Object} Returns `collection`.\n     */\n    var baseEachRight = createBaseEach(baseForOwnRight, true);\n\n    /**\n     * The base implementation of `_.every` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} predicate The function invoked per iteration.\n     * @returns {boolean} Returns `true` if all elements pass the predicate check,\n     *  else `false`\n     */\n    function baseEvery(collection, predicate) {\n      var result = true;\n      baseEach(collection, function(value, index, collection) {\n        result = !!predicate(value, index, collection);\n        return result;\n      });\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.fill` without an iteratee call guard.\n     *\n     * @private\n     * @param {Array} array The array to fill.\n     * @param {*} value The value to fill `array` with.\n     * @param {number} [start=0] The start position.\n     * @param {number} [end=array.length] The end position.\n     * @returns {Array} Returns `array`.\n     */\n    function baseFill(array, value, start, end) {\n      var length = array.length;\n\n      start = toInteger(start);\n      if (start < 0) {\n        start = -start > length ? 0 : (length + start);\n      }\n      end = (end === undefined || end > length) ? length : toInteger(end);\n      if (end < 0) {\n        end += length;\n      }\n      end = start > end ? 0 : toLength(end);\n      while (start < end) {\n        array[start++] = value;\n      }\n      return array;\n    }\n\n    /**\n     * The base implementation of `_.filter` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} predicate The function invoked per iteration.\n     * @returns {Array} Returns the new filtered array.\n     */\n    function baseFilter(collection, predicate) {\n      var result = [];\n      baseEach(collection, function(value, index, collection) {\n        if (predicate(value, index, collection)) {\n          result.push(value);\n        }\n      });\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.flatten` with support for restricting flattening.\n     *\n     * @private\n     * @param {Array} array The array to flatten.\n     * @param {number} depth The maximum recursion depth.\n     * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.\n     * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.\n     * @param {Array} [result=[]] The initial result value.\n     * @returns {Array} Returns the new flattened array.\n     */\n    function baseFlatten(array, depth, predicate, isStrict, result) {\n      var index = -1,\n          length = array.length;\n\n      predicate || (predicate = isFlattenable);\n      result || (result = []);\n\n      while (++index < length) {\n        var value = array[index];\n        if (depth > 0 && predicate(value)) {\n          if (depth > 1) {\n            // Recursively flatten arrays (susceptible to call stack limits).\n            baseFlatten(value, depth - 1, predicate, isStrict, result);\n          } else {\n            arrayPush(result, value);\n          }\n        } else if (!isStrict) {\n          result[result.length] = value;\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `baseForOwn` which iterates over `object`\n     * properties returned by `keysFunc` and invokes `iteratee` for each property.\n     * Iteratee functions may exit iteration early by explicitly returning `false`.\n     *\n     * @private\n     * @param {Object} object The object to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @param {Function} keysFunc The function to get the keys of `object`.\n     * @returns {Object} Returns `object`.\n     */\n    var baseFor = createBaseFor();\n\n    /**\n     * This function is like `baseFor` except that it iterates over properties\n     * in the opposite order.\n     *\n     * @private\n     * @param {Object} object The object to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @param {Function} keysFunc The function to get the keys of `object`.\n     * @returns {Object} Returns `object`.\n     */\n    var baseForRight = createBaseFor(true);\n\n    /**\n     * The base implementation of `_.forOwn` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Object} object The object to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @returns {Object} Returns `object`.\n     */\n    function baseForOwn(object, iteratee) {\n      return object && baseFor(object, iteratee, keys);\n    }\n\n    /**\n     * The base implementation of `_.forOwnRight` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Object} object The object to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @returns {Object} Returns `object`.\n     */\n    function baseForOwnRight(object, iteratee) {\n      return object && baseForRight(object, iteratee, keys);\n    }\n\n    /**\n     * The base implementation of `_.functions` which creates an array of\n     * `object` function property names filtered from `props`.\n     *\n     * @private\n     * @param {Object} object The object to inspect.\n     * @param {Array} props The property names to filter.\n     * @returns {Array} Returns the new array of filtered property names.\n     */\n    function baseFunctions(object, props) {\n      return arrayFilter(props, function(key) {\n        return isFunction(object[key]);\n      });\n    }\n\n    /**\n     * The base implementation of `_.get` without support for default values.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the property to get.\n     * @returns {*} Returns the resolved value.\n     */\n    function baseGet(object, path) {\n      path = isKey(path, object) ? [path] : castPath(path);\n\n      var index = 0,\n          length = path.length;\n\n      while (object != null && index < length) {\n        object = object[path[index++]];\n      }\n      return (index && index == length) ? object : undefined;\n    }\n\n    /**\n     * The base implementation of `getAllKeys` and `getAllKeysIn` which uses\n     * `keysFunc` and `symbolsFunc` to get the enumerable property names and\n     * symbols of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Function} keysFunc The function to get the keys of `object`.\n     * @param {Function} symbolsFunc The function to get the symbols of `object`.\n     * @returns {Array} Returns the array of property names and symbols.\n     */\n    function baseGetAllKeys(object, keysFunc, symbolsFunc) {\n      var result = keysFunc(object);\n      return isArray(object)\n        ? result\n        : arrayPush(result, symbolsFunc(object));\n    }\n\n    /**\n     * The base implementation of `_.has` without support for deep paths.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} key The key to check.\n     * @returns {boolean} Returns `true` if `key` exists, else `false`.\n     */\n    function baseHas(object, key) {\n      // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`,\n      // that are composed entirely of index properties, return `false` for\n      // `hasOwnProperty` checks of them.\n      return hasOwnProperty.call(object, key) ||\n        (typeof object == 'object' && key in object && getPrototype(object) === null);\n    }\n\n    /**\n     * The base implementation of `_.hasIn` without support for deep paths.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} key The key to check.\n     * @returns {boolean} Returns `true` if `key` exists, else `false`.\n     */\n    function baseHasIn(object, key) {\n      return key in Object(object);\n    }\n\n    /**\n     * The base implementation of `_.inRange` which doesn't coerce arguments to numbers.\n     *\n     * @private\n     * @param {number} number The number to check.\n     * @param {number} start The start of the range.\n     * @param {number} end The end of the range.\n     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n     */\n    function baseInRange(number, start, end) {\n      return number >= nativeMin(start, end) && number < nativeMax(start, end);\n    }\n\n    /**\n     * The base implementation of methods like `_.intersection`, without support\n     * for iteratee shorthands, that accepts an array of arrays to inspect.\n     *\n     * @private\n     * @param {Array} arrays The arrays to inspect.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of shared values.\n     */\n    function baseIntersection(arrays, iteratee, comparator) {\n      var includes = comparator ? arrayIncludesWith : arrayIncludes,\n          length = arrays[0].length,\n          othLength = arrays.length,\n          othIndex = othLength,\n          caches = Array(othLength),\n          maxLength = Infinity,\n          result = [];\n\n      while (othIndex--) {\n        var array = arrays[othIndex];\n        if (othIndex && iteratee) {\n          array = arrayMap(array, baseUnary(iteratee));\n        }\n        maxLength = nativeMin(array.length, maxLength);\n        caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))\n          ? new SetCache(othIndex && array)\n          : undefined;\n      }\n      array = arrays[0];\n\n      var index = -1,\n          seen = caches[0];\n\n      outer:\n      while (++index < length && result.length < maxLength) {\n        var value = array[index],\n            computed = iteratee ? iteratee(value) : value;\n\n        if (!(seen\n              ? cacheHas(seen, computed)\n              : includes(result, computed, comparator)\n            )) {\n          othIndex = othLength;\n          while (--othIndex) {\n            var cache = caches[othIndex];\n            if (!(cache\n                  ? cacheHas(cache, computed)\n                  : includes(arrays[othIndex], computed, comparator))\n                ) {\n              continue outer;\n            }\n          }\n          if (seen) {\n            seen.push(computed);\n          }\n          result.push(value);\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.invert` and `_.invertBy` which inverts\n     * `object` with values transformed by `iteratee` and set by `setter`.\n     *\n     * @private\n     * @param {Object} object The object to iterate over.\n     * @param {Function} setter The function to set `accumulator` values.\n     * @param {Function} iteratee The iteratee to transform values.\n     * @param {Object} accumulator The initial inverted object.\n     * @returns {Function} Returns `accumulator`.\n     */\n    function baseInverter(object, setter, iteratee, accumulator) {\n      baseForOwn(object, function(value, key, object) {\n        setter(accumulator, iteratee(value), key, object);\n      });\n      return accumulator;\n    }\n\n    /**\n     * The base implementation of `_.invoke` without support for individual\n     * method arguments.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the method to invoke.\n     * @param {Array} args The arguments to invoke the method with.\n     * @returns {*} Returns the result of the invoked method.\n     */\n    function baseInvoke(object, path, args) {\n      if (!isKey(path, object)) {\n        path = castPath(path);\n        object = parent(object, path);\n        path = last(path);\n      }\n      var func = object == null ? object : object[path];\n      return func == null ? undefined : apply(func, object, args);\n    }\n\n    /**\n     * The base implementation of `_.isEqual` which supports partial comparisons\n     * and tracks traversed objects.\n     *\n     * @private\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @param {Function} [customizer] The function to customize comparisons.\n     * @param {boolean} [bitmask] The bitmask of comparison flags.\n     *  The bitmask may be composed of the following flags:\n     *     1 - Unordered comparison\n     *     2 - Partial comparison\n     * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n     */\n    function baseIsEqual(value, other, customizer, bitmask, stack) {\n      if (value === other) {\n        return true;\n      }\n      if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {\n        return value !== value && other !== other;\n      }\n      return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);\n    }\n\n    /**\n     * A specialized version of `baseIsEqual` for arrays and objects which performs\n     * deep comparisons and tracks traversed objects enabling objects with circular\n     * references to be compared.\n     *\n     * @private\n     * @param {Object} object The object to compare.\n     * @param {Object} other The other object to compare.\n     * @param {Function} equalFunc The function to determine equivalents of values.\n     * @param {Function} [customizer] The function to customize comparisons.\n     * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`\n     *  for more details.\n     * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n     */\n    function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {\n      var objIsArr = isArray(object),\n          othIsArr = isArray(other),\n          objTag = arrayTag,\n          othTag = arrayTag;\n\n      if (!objIsArr) {\n        objTag = getTag(object);\n        objTag = objTag == argsTag ? objectTag : objTag;\n      }\n      if (!othIsArr) {\n        othTag = getTag(other);\n        othTag = othTag == argsTag ? objectTag : othTag;\n      }\n      var objIsObj = objTag == objectTag && !isHostObject(object),\n          othIsObj = othTag == objectTag && !isHostObject(other),\n          isSameTag = objTag == othTag;\n\n      if (isSameTag && !objIsObj) {\n        stack || (stack = new Stack);\n        return (objIsArr || isTypedArray(object))\n          ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)\n          : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);\n      }\n      if (!(bitmask & PARTIAL_COMPARE_FLAG)) {\n        var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n            othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n\n        if (objIsWrapped || othIsWrapped) {\n          var objUnwrapped = objIsWrapped ? object.value() : object,\n              othUnwrapped = othIsWrapped ? other.value() : other;\n\n          stack || (stack = new Stack);\n          return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);\n        }\n      }\n      if (!isSameTag) {\n        return false;\n      }\n      stack || (stack = new Stack);\n      return equalObjects(object, other, equalFunc, customizer, bitmask, stack);\n    }\n\n    /**\n     * The base implementation of `_.isMatch` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Object} object The object to inspect.\n     * @param {Object} source The object of property values to match.\n     * @param {Array} matchData The property names, values, and compare flags to match.\n     * @param {Function} [customizer] The function to customize comparisons.\n     * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n     */\n    function baseIsMatch(object, source, matchData, customizer) {\n      var index = matchData.length,\n          length = index,\n          noCustomizer = !customizer;\n\n      if (object == null) {\n        return !length;\n      }\n      object = Object(object);\n      while (index--) {\n        var data = matchData[index];\n        if ((noCustomizer && data[2])\n              ? data[1] !== object[data[0]]\n              : !(data[0] in object)\n            ) {\n          return false;\n        }\n      }\n      while (++index < length) {\n        data = matchData[index];\n        var key = data[0],\n            objValue = object[key],\n            srcValue = data[1];\n\n        if (noCustomizer && data[2]) {\n          if (objValue === undefined && !(key in object)) {\n            return false;\n          }\n        } else {\n          var stack = new Stack;\n          if (customizer) {\n            var result = customizer(objValue, srcValue, key, object, source, stack);\n          }\n          if (!(result === undefined\n                ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)\n                : result\n              )) {\n            return false;\n          }\n        }\n      }\n      return true;\n    }\n\n    /**\n     * The base implementation of `_.iteratee`.\n     *\n     * @private\n     * @param {*} [value=_.identity] The value to convert to an iteratee.\n     * @returns {Function} Returns the iteratee.\n     */\n    function baseIteratee(value) {\n      // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.\n      // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.\n      if (typeof value == 'function') {\n        return value;\n      }\n      if (value == null) {\n        return identity;\n      }\n      if (typeof value == 'object') {\n        return isArray(value)\n          ? baseMatchesProperty(value[0], value[1])\n          : baseMatches(value);\n      }\n      return property(value);\n    }\n\n    /**\n     * The base implementation of `_.keys` which doesn't skip the constructor\n     * property of prototypes or treat sparse arrays as dense.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property names.\n     */\n    function baseKeys(object) {\n      return nativeKeys(Object(object));\n    }\n\n    /**\n     * The base implementation of `_.keysIn` which doesn't skip the constructor\n     * property of prototypes or treat sparse arrays as dense.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property names.\n     */\n    function baseKeysIn(object) {\n      object = object == null ? object : Object(object);\n\n      var result = [];\n      for (var key in object) {\n        result.push(key);\n      }\n      return result;\n    }\n\n    // Fallback for IE < 9 with es6-shim.\n    if (enumerate && !propertyIsEnumerable.call({ 'valueOf': 1 }, 'valueOf')) {\n      baseKeysIn = function(object) {\n        return iteratorToArray(enumerate(object));\n      };\n    }\n\n    /**\n     * The base implementation of `_.map` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} iteratee The function invoked per iteration.\n     * @returns {Array} Returns the new mapped array.\n     */\n    function baseMap(collection, iteratee) {\n      var index = -1,\n          result = isArrayLike(collection) ? Array(collection.length) : [];\n\n      baseEach(collection, function(value, key, collection) {\n        result[++index] = iteratee(value, key, collection);\n      });\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.matches` which doesn't clone `source`.\n     *\n     * @private\n     * @param {Object} source The object of property values to match.\n     * @returns {Function} Returns the new function.\n     */\n    function baseMatches(source) {\n      var matchData = getMatchData(source);\n      if (matchData.length == 1 && matchData[0][2]) {\n        return matchesStrictComparable(matchData[0][0], matchData[0][1]);\n      }\n      return function(object) {\n        return object === source || baseIsMatch(object, source, matchData);\n      };\n    }\n\n    /**\n     * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.\n     *\n     * @private\n     * @param {string} path The path of the property to get.\n     * @param {*} srcValue The value to match.\n     * @returns {Function} Returns the new function.\n     */\n    function baseMatchesProperty(path, srcValue) {\n      if (isKey(path) && isStrictComparable(srcValue)) {\n        return matchesStrictComparable(path, srcValue);\n      }\n      return function(object) {\n        var objValue = get(object, path);\n        return (objValue === undefined && objValue === srcValue)\n          ? hasIn(object, path)\n          : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);\n      };\n    }\n\n    /**\n     * The base implementation of `_.merge` without support for multiple sources.\n     *\n     * @private\n     * @param {Object} object The destination object.\n     * @param {Object} source The source object.\n     * @param {number} srcIndex The index of `source`.\n     * @param {Function} [customizer] The function to customize merged values.\n     * @param {Object} [stack] Tracks traversed source values and their merged\n     *  counterparts.\n     */\n    function baseMerge(object, source, srcIndex, customizer, stack) {\n      if (object === source) {\n        return;\n      }\n      if (!(isArray(source) || isTypedArray(source))) {\n        var props = keysIn(source);\n      }\n      arrayEach(props || source, function(srcValue, key) {\n        if (props) {\n          key = srcValue;\n          srcValue = source[key];\n        }\n        if (isObject(srcValue)) {\n          stack || (stack = new Stack);\n          baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);\n        }\n        else {\n          var newValue = customizer\n            ? customizer(object[key], srcValue, (key + ''), object, source, stack)\n            : undefined;\n\n          if (newValue === undefined) {\n            newValue = srcValue;\n          }\n          assignMergeValue(object, key, newValue);\n        }\n      });\n    }\n\n    /**\n     * A specialized version of `baseMerge` for arrays and objects which performs\n     * deep merges and tracks traversed objects enabling objects with circular\n     * references to be merged.\n     *\n     * @private\n     * @param {Object} object The destination object.\n     * @param {Object} source The source object.\n     * @param {string} key The key of the value to merge.\n     * @param {number} srcIndex The index of `source`.\n     * @param {Function} mergeFunc The function to merge values.\n     * @param {Function} [customizer] The function to customize assigned values.\n     * @param {Object} [stack] Tracks traversed source values and their merged\n     *  counterparts.\n     */\n    function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {\n      var objValue = object[key],\n          srcValue = source[key],\n          stacked = stack.get(srcValue);\n\n      if (stacked) {\n        assignMergeValue(object, key, stacked);\n        return;\n      }\n      var newValue = customizer\n        ? customizer(objValue, srcValue, (key + ''), object, source, stack)\n        : undefined;\n\n      var isCommon = newValue === undefined;\n\n      if (isCommon) {\n        newValue = srcValue;\n        if (isArray(srcValue) || isTypedArray(srcValue)) {\n          if (isArray(objValue)) {\n            newValue = objValue;\n          }\n          else if (isArrayLikeObject(objValue)) {\n            newValue = copyArray(objValue);\n          }\n          else {\n            isCommon = false;\n            newValue = baseClone(srcValue, true);\n          }\n        }\n        else if (isPlainObject(srcValue) || isArguments(srcValue)) {\n          if (isArguments(objValue)) {\n            newValue = toPlainObject(objValue);\n          }\n          else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {\n            isCommon = false;\n            newValue = baseClone(srcValue, true);\n          }\n          else {\n            newValue = objValue;\n          }\n        }\n        else {\n          isCommon = false;\n        }\n      }\n      stack.set(srcValue, newValue);\n\n      if (isCommon) {\n        // Recursively merge objects and arrays (susceptible to call stack limits).\n        mergeFunc(newValue, srcValue, srcIndex, customizer, stack);\n      }\n      stack['delete'](srcValue);\n      assignMergeValue(object, key, newValue);\n    }\n\n    /**\n     * The base implementation of `_.nth` which doesn't coerce `n` to an integer.\n     *\n     * @private\n     * @param {Array} array The array to query.\n     * @param {number} n The index of the element to return.\n     * @returns {*} Returns the nth element of `array`.\n     */\n    function baseNth(array, n) {\n      var length = array.length;\n      if (!length) {\n        return;\n      }\n      n += n < 0 ? length : 0;\n      return isIndex(n, length) ? array[n] : undefined;\n    }\n\n    /**\n     * The base implementation of `_.orderBy` without param guards.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.\n     * @param {string[]} orders The sort orders of `iteratees`.\n     * @returns {Array} Returns the new sorted array.\n     */\n    function baseOrderBy(collection, iteratees, orders) {\n      var index = -1;\n      iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee()));\n\n      var result = baseMap(collection, function(value, key, collection) {\n        var criteria = arrayMap(iteratees, function(iteratee) {\n          return iteratee(value);\n        });\n        return { 'criteria': criteria, 'index': ++index, 'value': value };\n      });\n\n      return baseSortBy(result, function(object, other) {\n        return compareMultiple(object, other, orders);\n      });\n    }\n\n    /**\n     * The base implementation of `_.pick` without support for individual\n     * property identifiers.\n     *\n     * @private\n     * @param {Object} object The source object.\n     * @param {string[]} props The property identifiers to pick.\n     * @returns {Object} Returns the new object.\n     */\n    function basePick(object, props) {\n      object = Object(object);\n      return arrayReduce(props, function(result, key) {\n        if (key in object) {\n          result[key] = object[key];\n        }\n        return result;\n      }, {});\n    }\n\n    /**\n     * The base implementation of  `_.pickBy` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Object} object The source object.\n     * @param {Function} predicate The function invoked per property.\n     * @returns {Object} Returns the new object.\n     */\n    function basePickBy(object, predicate) {\n      var index = -1,\n          props = getAllKeysIn(object),\n          length = props.length,\n          result = {};\n\n      while (++index < length) {\n        var key = props[index],\n            value = object[key];\n\n        if (predicate(value, key)) {\n          result[key] = value;\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.property` without support for deep paths.\n     *\n     * @private\n     * @param {string} key The key of the property to get.\n     * @returns {Function} Returns the new function.\n     */\n    function baseProperty(key) {\n      return function(object) {\n        return object == null ? undefined : object[key];\n      };\n    }\n\n    /**\n     * A specialized version of `baseProperty` which supports deep paths.\n     *\n     * @private\n     * @param {Array|string} path The path of the property to get.\n     * @returns {Function} Returns the new function.\n     */\n    function basePropertyDeep(path) {\n      return function(object) {\n        return baseGet(object, path);\n      };\n    }\n\n    /**\n     * The base implementation of `_.pullAllBy` without support for iteratee\n     * shorthands.\n     *\n     * @private\n     * @param {Array} array The array to modify.\n     * @param {Array} values The values to remove.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns `array`.\n     */\n    function basePullAll(array, values, iteratee, comparator) {\n      var indexOf = comparator ? baseIndexOfWith : baseIndexOf,\n          index = -1,\n          length = values.length,\n          seen = array;\n\n      if (iteratee) {\n        seen = arrayMap(array, baseUnary(iteratee));\n      }\n      while (++index < length) {\n        var fromIndex = 0,\n            value = values[index],\n            computed = iteratee ? iteratee(value) : value;\n\n        while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {\n          if (seen !== array) {\n            splice.call(seen, fromIndex, 1);\n          }\n          splice.call(array, fromIndex, 1);\n        }\n      }\n      return array;\n    }\n\n    /**\n     * The base implementation of `_.pullAt` without support for individual\n     * indexes or capturing the removed elements.\n     *\n     * @private\n     * @param {Array} array The array to modify.\n     * @param {number[]} indexes The indexes of elements to remove.\n     * @returns {Array} Returns `array`.\n     */\n    function basePullAt(array, indexes) {\n      var length = array ? indexes.length : 0,\n          lastIndex = length - 1;\n\n      while (length--) {\n        var index = indexes[length];\n        if (lastIndex == length || index != previous) {\n          var previous = index;\n          if (isIndex(index)) {\n            splice.call(array, index, 1);\n          }\n          else if (!isKey(index, array)) {\n            var path = castPath(index),\n                object = parent(array, path);\n\n            if (object != null) {\n              delete object[last(path)];\n            }\n          }\n          else {\n            delete array[index];\n          }\n        }\n      }\n      return array;\n    }\n\n    /**\n     * The base implementation of `_.random` without support for returning\n     * floating-point numbers.\n     *\n     * @private\n     * @param {number} lower The lower bound.\n     * @param {number} upper The upper bound.\n     * @returns {number} Returns the random number.\n     */\n    function baseRandom(lower, upper) {\n      return lower + nativeFloor(nativeRandom() * (upper - lower + 1));\n    }\n\n    /**\n     * The base implementation of `_.range` and `_.rangeRight` which doesn't\n     * coerce arguments to numbers.\n     *\n     * @private\n     * @param {number} start The start of the range.\n     * @param {number} end The end of the range.\n     * @param {number} step The value to increment or decrement by.\n     * @param {boolean} [fromRight] Specify iterating from right to left.\n     * @returns {Array} Returns the new array of numbers.\n     */\n    function baseRange(start, end, step, fromRight) {\n      var index = -1,\n          length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),\n          result = Array(length);\n\n      while (length--) {\n        result[fromRight ? length : ++index] = start;\n        start += step;\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.repeat` which doesn't coerce arguments.\n     *\n     * @private\n     * @param {string} string The string to repeat.\n     * @param {number} n The number of times to repeat the string.\n     * @returns {string} Returns the repeated string.\n     */\n    function baseRepeat(string, n) {\n      var result = '';\n      if (!string || n < 1 || n > MAX_SAFE_INTEGER) {\n        return result;\n      }\n      // Leverage the exponentiation by squaring algorithm for a faster repeat.\n      // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.\n      do {\n        if (n % 2) {\n          result += string;\n        }\n        n = nativeFloor(n / 2);\n        if (n) {\n          string += string;\n        }\n      } while (n);\n\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.set`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the property to set.\n     * @param {*} value The value to set.\n     * @param {Function} [customizer] The function to customize path creation.\n     * @returns {Object} Returns `object`.\n     */\n    function baseSet(object, path, value, customizer) {\n      path = isKey(path, object) ? [path] : castPath(path);\n\n      var index = -1,\n          length = path.length,\n          lastIndex = length - 1,\n          nested = object;\n\n      while (nested != null && ++index < length) {\n        var key = path[index];\n        if (isObject(nested)) {\n          var newValue = value;\n          if (index != lastIndex) {\n            var objValue = nested[key];\n            newValue = customizer ? customizer(objValue, key, nested) : undefined;\n            if (newValue === undefined) {\n              newValue = objValue == null\n                ? (isIndex(path[index + 1]) ? [] : {})\n                : objValue;\n            }\n          }\n          assignValue(nested, key, newValue);\n        }\n        nested = nested[key];\n      }\n      return object;\n    }\n\n    /**\n     * The base implementation of `setData` without support for hot loop detection.\n     *\n     * @private\n     * @param {Function} func The function to associate metadata with.\n     * @param {*} data The metadata.\n     * @returns {Function} Returns `func`.\n     */\n    var baseSetData = !metaMap ? identity : function(func, data) {\n      metaMap.set(func, data);\n      return func;\n    };\n\n    /**\n     * The base implementation of `_.slice` without an iteratee call guard.\n     *\n     * @private\n     * @param {Array} array The array to slice.\n     * @param {number} [start=0] The start position.\n     * @param {number} [end=array.length] The end position.\n     * @returns {Array} Returns the slice of `array`.\n     */\n    function baseSlice(array, start, end) {\n      var index = -1,\n          length = array.length;\n\n      if (start < 0) {\n        start = -start > length ? 0 : (length + start);\n      }\n      end = end > length ? length : end;\n      if (end < 0) {\n        end += length;\n      }\n      length = start > end ? 0 : ((end - start) >>> 0);\n      start >>>= 0;\n\n      var result = Array(length);\n      while (++index < length) {\n        result[index] = array[index + start];\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.some` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} predicate The function invoked per iteration.\n     * @returns {boolean} Returns `true` if any element passes the predicate check,\n     *  else `false`.\n     */\n    function baseSome(collection, predicate) {\n      var result;\n\n      baseEach(collection, function(value, index, collection) {\n        result = predicate(value, index, collection);\n        return !result;\n      });\n      return !!result;\n    }\n\n    /**\n     * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which\n     * performs a binary search of `array` to determine the index at which `value`\n     * should be inserted into `array` in order to maintain its sort order.\n     *\n     * @private\n     * @param {Array} array The sorted array to inspect.\n     * @param {*} value The value to evaluate.\n     * @param {boolean} [retHighest] Specify returning the highest qualified index.\n     * @returns {number} Returns the index at which `value` should be inserted\n     *  into `array`.\n     */\n    function baseSortedIndex(array, value, retHighest) {\n      var low = 0,\n          high = array ? array.length : low;\n\n      if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {\n        while (low < high) {\n          var mid = (low + high) >>> 1,\n              computed = array[mid];\n\n          if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) {\n            low = mid + 1;\n          } else {\n            high = mid;\n          }\n        }\n        return high;\n      }\n      return baseSortedIndexBy(array, value, identity, retHighest);\n    }\n\n    /**\n     * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`\n     * which invokes `iteratee` for `value` and each element of `array` to compute\n     * their sort ranking. The iteratee is invoked with one argument; (value).\n     *\n     * @private\n     * @param {Array} array The sorted array to inspect.\n     * @param {*} value The value to evaluate.\n     * @param {Function} iteratee The iteratee invoked per element.\n     * @param {boolean} [retHighest] Specify returning the highest qualified index.\n     * @returns {number} Returns the index at which `value` should be inserted\n     *  into `array`.\n     */\n    function baseSortedIndexBy(array, value, iteratee, retHighest) {\n      value = iteratee(value);\n\n      var low = 0,\n          high = array ? array.length : 0,\n          valIsNaN = value !== value,\n          valIsNull = value === null,\n          valIsUndef = value === undefined;\n\n      while (low < high) {\n        var mid = nativeFloor((low + high) / 2),\n            computed = iteratee(array[mid]),\n            isDef = computed !== undefined,\n            isReflexive = computed === computed;\n\n        if (valIsNaN) {\n          var setLow = isReflexive || retHighest;\n        } else if (valIsNull) {\n          setLow = isReflexive && isDef && (retHighest || computed != null);\n        } else if (valIsUndef) {\n          setLow = isReflexive && (retHighest || isDef);\n        } else if (computed == null) {\n          setLow = false;\n        } else {\n          setLow = retHighest ? (computed <= value) : (computed < value);\n        }\n        if (setLow) {\n          low = mid + 1;\n        } else {\n          high = mid;\n        }\n      }\n      return nativeMin(high, MAX_ARRAY_INDEX);\n    }\n\n    /**\n     * The base implementation of `_.sortedUniq`.\n     *\n     * @private\n     * @param {Array} array The array to inspect.\n     * @returns {Array} Returns the new duplicate free array.\n     */\n    function baseSortedUniq(array) {\n      return baseSortedUniqBy(array);\n    }\n\n    /**\n     * The base implementation of `_.sortedUniqBy` without support for iteratee\n     * shorthands.\n     *\n     * @private\n     * @param {Array} array The array to inspect.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @returns {Array} Returns the new duplicate free array.\n     */\n    function baseSortedUniqBy(array, iteratee) {\n      var index = 0,\n          length = array.length,\n          value = array[0],\n          computed = iteratee ? iteratee(value) : value,\n          seen = computed,\n          resIndex = 1,\n          result = [value];\n\n      while (++index < length) {\n        value = array[index],\n        computed = iteratee ? iteratee(value) : value;\n\n        if (!eq(computed, seen)) {\n          seen = computed;\n          result[resIndex++] = value;\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.uniqBy` without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array} array The array to inspect.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new duplicate free array.\n     */\n    function baseUniq(array, iteratee, comparator) {\n      var index = -1,\n          includes = arrayIncludes,\n          length = array.length,\n          isCommon = true,\n          result = [],\n          seen = result;\n\n      if (comparator) {\n        isCommon = false;\n        includes = arrayIncludesWith;\n      }\n      else if (length >= LARGE_ARRAY_SIZE) {\n        var set = iteratee ? null : createSet(array);\n        if (set) {\n          return setToArray(set);\n        }\n        isCommon = false;\n        includes = cacheHas;\n        seen = new SetCache;\n      }\n      else {\n        seen = iteratee ? [] : result;\n      }\n      outer:\n      while (++index < length) {\n        var value = array[index],\n            computed = iteratee ? iteratee(value) : value;\n\n        if (isCommon && computed === computed) {\n          var seenIndex = seen.length;\n          while (seenIndex--) {\n            if (seen[seenIndex] === computed) {\n              continue outer;\n            }\n          }\n          if (iteratee) {\n            seen.push(computed);\n          }\n          result.push(value);\n        }\n        else if (!includes(seen, computed, comparator)) {\n          if (seen !== result) {\n            seen.push(computed);\n          }\n          result.push(value);\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The base implementation of `_.unset`.\n     *\n     * @private\n     * @param {Object} object The object to modify.\n     * @param {Array|string} path The path of the property to unset.\n     * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n     */\n    function baseUnset(object, path) {\n      path = isKey(path, object) ? [path] : castPath(path);\n      object = parent(object, path);\n      var key = last(path);\n      return (object != null && has(object, key)) ? delete object[key] : true;\n    }\n\n    /**\n     * The base implementation of `_.update`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the property to update.\n     * @param {Function} updater The function to produce the updated value.\n     * @param {Function} [customizer] The function to customize path creation.\n     * @returns {Object} Returns `object`.\n     */\n    function baseUpdate(object, path, updater, customizer) {\n      return baseSet(object, path, updater(baseGet(object, path)), customizer);\n    }\n\n    /**\n     * The base implementation of methods like `_.dropWhile` and `_.takeWhile`\n     * without support for iteratee shorthands.\n     *\n     * @private\n     * @param {Array} array The array to query.\n     * @param {Function} predicate The function invoked per iteration.\n     * @param {boolean} [isDrop] Specify dropping elements instead of taking them.\n     * @param {boolean} [fromRight] Specify iterating from right to left.\n     * @returns {Array} Returns the slice of `array`.\n     */\n    function baseWhile(array, predicate, isDrop, fromRight) {\n      var length = array.length,\n          index = fromRight ? length : -1;\n\n      while ((fromRight ? index-- : ++index < length) &&\n        predicate(array[index], index, array)) {}\n\n      return isDrop\n        ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))\n        : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));\n    }\n\n    /**\n     * The base implementation of `wrapperValue` which returns the result of\n     * performing a sequence of actions on the unwrapped `value`, where each\n     * successive action is supplied the return value of the previous.\n     *\n     * @private\n     * @param {*} value The unwrapped value.\n     * @param {Array} actions Actions to perform to resolve the unwrapped value.\n     * @returns {*} Returns the resolved value.\n     */\n    function baseWrapperValue(value, actions) {\n      var result = value;\n      if (result instanceof LazyWrapper) {\n        result = result.value();\n      }\n      return arrayReduce(actions, function(result, action) {\n        return action.func.apply(action.thisArg, arrayPush([result], action.args));\n      }, result);\n    }\n\n    /**\n     * The base implementation of methods like `_.xor`, without support for\n     * iteratee shorthands, that accepts an array of arrays to inspect.\n     *\n     * @private\n     * @param {Array} arrays The arrays to inspect.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of values.\n     */\n    function baseXor(arrays, iteratee, comparator) {\n      var index = -1,\n          length = arrays.length;\n\n      while (++index < length) {\n        var result = result\n          ? arrayPush(\n              baseDifference(result, arrays[index], iteratee, comparator),\n              baseDifference(arrays[index], result, iteratee, comparator)\n            )\n          : arrays[index];\n      }\n      return (result && result.length) ? baseUniq(result, iteratee, comparator) : [];\n    }\n\n    /**\n     * This base implementation of `_.zipObject` which assigns values using `assignFunc`.\n     *\n     * @private\n     * @param {Array} props The property identifiers.\n     * @param {Array} values The property values.\n     * @param {Function} assignFunc The function to assign values.\n     * @returns {Object} Returns the new object.\n     */\n    function baseZipObject(props, values, assignFunc) {\n      var index = -1,\n          length = props.length,\n          valsLength = values.length,\n          result = {};\n\n      while (++index < length) {\n        var value = index < valsLength ? values[index] : undefined;\n        assignFunc(result, props[index], value);\n      }\n      return result;\n    }\n\n    /**\n     * Casts `value` to an empty array if it's not an array like object.\n     *\n     * @private\n     * @param {*} value The value to inspect.\n     * @returns {Array|Object} Returns the cast array-like object.\n     */\n    function castArrayLikeObject(value) {\n      return isArrayLikeObject(value) ? value : [];\n    }\n\n    /**\n     * Casts `value` to `identity` if it's not a function.\n     *\n     * @private\n     * @param {*} value The value to inspect.\n     * @returns {Function} Returns cast function.\n     */\n    function castFunction(value) {\n      return typeof value == 'function' ? value : identity;\n    }\n\n    /**\n     * Casts `value` to a path array if it's not one.\n     *\n     * @private\n     * @param {*} value The value to inspect.\n     * @returns {Array} Returns the cast property path array.\n     */\n    function castPath(value) {\n      return isArray(value) ? value : stringToPath(value);\n    }\n\n    /**\n     * Casts `array` to a slice if it's needed.\n     *\n     * @private\n     * @param {Array} array The array to inspect.\n     * @param {number} start The start position.\n     * @param {number} [end=array.length] The end position.\n     * @returns {Array} Returns the cast slice.\n     */\n    function castSlice(array, start, end) {\n      var length = array.length;\n      end = end === undefined ? length : end;\n      return (!start && end >= length) ? array : baseSlice(array, start, end);\n    }\n\n    /**\n     * Creates a clone of  `buffer`.\n     *\n     * @private\n     * @param {Buffer} buffer The buffer to clone.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @returns {Buffer} Returns the cloned buffer.\n     */\n    function cloneBuffer(buffer, isDeep) {\n      if (isDeep) {\n        return buffer.slice();\n      }\n      var result = new buffer.constructor(buffer.length);\n      buffer.copy(result);\n      return result;\n    }\n\n    /**\n     * Creates a clone of `arrayBuffer`.\n     *\n     * @private\n     * @param {ArrayBuffer} arrayBuffer The array buffer to clone.\n     * @returns {ArrayBuffer} Returns the cloned array buffer.\n     */\n    function cloneArrayBuffer(arrayBuffer) {\n      var result = new arrayBuffer.constructor(arrayBuffer.byteLength);\n      new Uint8Array(result).set(new Uint8Array(arrayBuffer));\n      return result;\n    }\n\n    /**\n     * Creates a clone of `dataView`.\n     *\n     * @private\n     * @param {Object} dataView The data view to clone.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @returns {Object} Returns the cloned data view.\n     */\n    function cloneDataView(dataView, isDeep) {\n      var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;\n      return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);\n    }\n\n    /**\n     * Creates a clone of `map`.\n     *\n     * @private\n     * @param {Object} map The map to clone.\n     * @param {Function} cloneFunc The function to clone values.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @returns {Object} Returns the cloned map.\n     */\n    function cloneMap(map, isDeep, cloneFunc) {\n      var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map);\n      return arrayReduce(array, addMapEntry, new map.constructor);\n    }\n\n    /**\n     * Creates a clone of `regexp`.\n     *\n     * @private\n     * @param {Object} regexp The regexp to clone.\n     * @returns {Object} Returns the cloned regexp.\n     */\n    function cloneRegExp(regexp) {\n      var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n      result.lastIndex = regexp.lastIndex;\n      return result;\n    }\n\n    /**\n     * Creates a clone of `set`.\n     *\n     * @private\n     * @param {Object} set The set to clone.\n     * @param {Function} cloneFunc The function to clone values.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @returns {Object} Returns the cloned set.\n     */\n    function cloneSet(set, isDeep, cloneFunc) {\n      var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set);\n      return arrayReduce(array, addSetEntry, new set.constructor);\n    }\n\n    /**\n     * Creates a clone of the `symbol` object.\n     *\n     * @private\n     * @param {Object} symbol The symbol object to clone.\n     * @returns {Object} Returns the cloned symbol object.\n     */\n    function cloneSymbol(symbol) {\n      return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};\n    }\n\n    /**\n     * Creates a clone of `typedArray`.\n     *\n     * @private\n     * @param {Object} typedArray The typed array to clone.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @returns {Object} Returns the cloned typed array.\n     */\n    function cloneTypedArray(typedArray, isDeep) {\n      var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;\n      return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);\n    }\n\n    /**\n     * Creates an array that is the composition of partially applied arguments,\n     * placeholders, and provided arguments into a single array of arguments.\n     *\n     * @private\n     * @param {Array|Object} args The provided arguments.\n     * @param {Array} partials The arguments to prepend to those provided.\n     * @param {Array} holders The `partials` placeholder indexes.\n     * @params {boolean} [isCurried] Specify composing for a curried function.\n     * @returns {Array} Returns the new array of composed arguments.\n     */\n    function composeArgs(args, partials, holders, isCurried) {\n      var argsIndex = -1,\n          argsLength = args.length,\n          holdersLength = holders.length,\n          leftIndex = -1,\n          leftLength = partials.length,\n          rangeLength = nativeMax(argsLength - holdersLength, 0),\n          result = Array(leftLength + rangeLength),\n          isUncurried = !isCurried;\n\n      while (++leftIndex < leftLength) {\n        result[leftIndex] = partials[leftIndex];\n      }\n      while (++argsIndex < holdersLength) {\n        if (isUncurried || argsIndex < argsLength) {\n          result[holders[argsIndex]] = args[argsIndex];\n        }\n      }\n      while (rangeLength--) {\n        result[leftIndex++] = args[argsIndex++];\n      }\n      return result;\n    }\n\n    /**\n     * This function is like `composeArgs` except that the arguments composition\n     * is tailored for `_.partialRight`.\n     *\n     * @private\n     * @param {Array|Object} args The provided arguments.\n     * @param {Array} partials The arguments to append to those provided.\n     * @param {Array} holders The `partials` placeholder indexes.\n     * @params {boolean} [isCurried] Specify composing for a curried function.\n     * @returns {Array} Returns the new array of composed arguments.\n     */\n    function composeArgsRight(args, partials, holders, isCurried) {\n      var argsIndex = -1,\n          argsLength = args.length,\n          holdersIndex = -1,\n          holdersLength = holders.length,\n          rightIndex = -1,\n          rightLength = partials.length,\n          rangeLength = nativeMax(argsLength - holdersLength, 0),\n          result = Array(rangeLength + rightLength),\n          isUncurried = !isCurried;\n\n      while (++argsIndex < rangeLength) {\n        result[argsIndex] = args[argsIndex];\n      }\n      var offset = argsIndex;\n      while (++rightIndex < rightLength) {\n        result[offset + rightIndex] = partials[rightIndex];\n      }\n      while (++holdersIndex < holdersLength) {\n        if (isUncurried || argsIndex < argsLength) {\n          result[offset + holders[holdersIndex]] = args[argsIndex++];\n        }\n      }\n      return result;\n    }\n\n    /**\n     * Copies the values of `source` to `array`.\n     *\n     * @private\n     * @param {Array} source The array to copy values from.\n     * @param {Array} [array=[]] The array to copy values to.\n     * @returns {Array} Returns `array`.\n     */\n    function copyArray(source, array) {\n      var index = -1,\n          length = source.length;\n\n      array || (array = Array(length));\n      while (++index < length) {\n        array[index] = source[index];\n      }\n      return array;\n    }\n\n    /**\n     * Copies properties of `source` to `object`.\n     *\n     * @private\n     * @param {Object} source The object to copy properties from.\n     * @param {Array} props The property identifiers to copy.\n     * @param {Object} [object={}] The object to copy properties to.\n     * @param {Function} [customizer] The function to customize copied values.\n     * @returns {Object} Returns `object`.\n     */\n    function copyObject(source, props, object, customizer) {\n      object || (object = {});\n\n      var index = -1,\n          length = props.length;\n\n      while (++index < length) {\n        var key = props[index];\n\n        var newValue = customizer\n          ? customizer(object[key], source[key], key, object, source)\n          : source[key];\n\n        assignValue(object, key, newValue);\n      }\n      return object;\n    }\n\n    /**\n     * Copies own symbol properties of `source` to `object`.\n     *\n     * @private\n     * @param {Object} source The object to copy symbols from.\n     * @param {Object} [object={}] The object to copy symbols to.\n     * @returns {Object} Returns `object`.\n     */\n    function copySymbols(source, object) {\n      return copyObject(source, getSymbols(source), object);\n    }\n\n    /**\n     * Creates a function like `_.groupBy`.\n     *\n     * @private\n     * @param {Function} setter The function to set accumulator values.\n     * @param {Function} [initializer] The accumulator object initializer.\n     * @returns {Function} Returns the new aggregator function.\n     */\n    function createAggregator(setter, initializer) {\n      return function(collection, iteratee) {\n        var func = isArray(collection) ? arrayAggregator : baseAggregator,\n            accumulator = initializer ? initializer() : {};\n\n        return func(collection, setter, getIteratee(iteratee), accumulator);\n      };\n    }\n\n    /**\n     * Creates a function like `_.assign`.\n     *\n     * @private\n     * @param {Function} assigner The function to assign values.\n     * @returns {Function} Returns the new assigner function.\n     */\n    function createAssigner(assigner) {\n      return rest(function(object, sources) {\n        var index = -1,\n            length = sources.length,\n            customizer = length > 1 ? sources[length - 1] : undefined,\n            guard = length > 2 ? sources[2] : undefined;\n\n        customizer = typeof customizer == 'function'\n          ? (length--, customizer)\n          : undefined;\n\n        if (guard && isIterateeCall(sources[0], sources[1], guard)) {\n          customizer = length < 3 ? undefined : customizer;\n          length = 1;\n        }\n        object = Object(object);\n        while (++index < length) {\n          var source = sources[index];\n          if (source) {\n            assigner(object, source, index, customizer);\n          }\n        }\n        return object;\n      });\n    }\n\n    /**\n     * Creates a `baseEach` or `baseEachRight` function.\n     *\n     * @private\n     * @param {Function} eachFunc The function to iterate over a collection.\n     * @param {boolean} [fromRight] Specify iterating from right to left.\n     * @returns {Function} Returns the new base function.\n     */\n    function createBaseEach(eachFunc, fromRight) {\n      return function(collection, iteratee) {\n        if (collection == null) {\n          return collection;\n        }\n        if (!isArrayLike(collection)) {\n          return eachFunc(collection, iteratee);\n        }\n        var length = collection.length,\n            index = fromRight ? length : -1,\n            iterable = Object(collection);\n\n        while ((fromRight ? index-- : ++index < length)) {\n          if (iteratee(iterable[index], index, iterable) === false) {\n            break;\n          }\n        }\n        return collection;\n      };\n    }\n\n    /**\n     * Creates a base function for methods like `_.forIn` and `_.forOwn`.\n     *\n     * @private\n     * @param {boolean} [fromRight] Specify iterating from right to left.\n     * @returns {Function} Returns the new base function.\n     */\n    function createBaseFor(fromRight) {\n      return function(object, iteratee, keysFunc) {\n        var index = -1,\n            iterable = Object(object),\n            props = keysFunc(object),\n            length = props.length;\n\n        while (length--) {\n          var key = props[fromRight ? length : ++index];\n          if (iteratee(iterable[key], key, iterable) === false) {\n            break;\n          }\n        }\n        return object;\n      };\n    }\n\n    /**\n     * Creates a function that wraps `func` to invoke it with the optional `this`\n     * binding of `thisArg`.\n     *\n     * @private\n     * @param {Function} func The function to wrap.\n     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`\n     *  for more details.\n     * @param {*} [thisArg] The `this` binding of `func`.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createBaseWrapper(func, bitmask, thisArg) {\n      var isBind = bitmask & BIND_FLAG,\n          Ctor = createCtorWrapper(func);\n\n      function wrapper() {\n        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n        return fn.apply(isBind ? thisArg : this, arguments);\n      }\n      return wrapper;\n    }\n\n    /**\n     * Creates a function like `_.lowerFirst`.\n     *\n     * @private\n     * @param {string} methodName The name of the `String` case method to use.\n     * @returns {Function} Returns the new function.\n     */\n    function createCaseFirst(methodName) {\n      return function(string) {\n        string = toString(string);\n\n        var strSymbols = reHasComplexSymbol.test(string)\n          ? stringToArray(string)\n          : undefined;\n\n        var chr = strSymbols\n          ? strSymbols[0]\n          : string.charAt(0);\n\n        var trailing = strSymbols\n          ? castSlice(strSymbols, 1).join('')\n          : string.slice(1);\n\n        return chr[methodName]() + trailing;\n      };\n    }\n\n    /**\n     * Creates a function like `_.camelCase`.\n     *\n     * @private\n     * @param {Function} callback The function to combine each word.\n     * @returns {Function} Returns the new compounder function.\n     */\n    function createCompounder(callback) {\n      return function(string) {\n        return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');\n      };\n    }\n\n    /**\n     * Creates a function that produces an instance of `Ctor` regardless of\n     * whether it was invoked as part of a `new` expression or by `call` or `apply`.\n     *\n     * @private\n     * @param {Function} Ctor The constructor to wrap.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createCtorWrapper(Ctor) {\n      return function() {\n        // Use a `switch` statement to work with class constructors. See\n        // http://ecma-international.org/ecma-262/6.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist\n        // for more details.\n        var args = arguments;\n        switch (args.length) {\n          case 0: return new Ctor;\n          case 1: return new Ctor(args[0]);\n          case 2: return new Ctor(args[0], args[1]);\n          case 3: return new Ctor(args[0], args[1], args[2]);\n          case 4: return new Ctor(args[0], args[1], args[2], args[3]);\n          case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);\n          case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);\n          case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);\n        }\n        var thisBinding = baseCreate(Ctor.prototype),\n            result = Ctor.apply(thisBinding, args);\n\n        // Mimic the constructor's `return` behavior.\n        // See https://es5.github.io/#x13.2.2 for more details.\n        return isObject(result) ? result : thisBinding;\n      };\n    }\n\n    /**\n     * Creates a function that wraps `func` to enable currying.\n     *\n     * @private\n     * @param {Function} func The function to wrap.\n     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`\n     *  for more details.\n     * @param {number} arity The arity of `func`.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createCurryWrapper(func, bitmask, arity) {\n      var Ctor = createCtorWrapper(func);\n\n      function wrapper() {\n        var length = arguments.length,\n            args = Array(length),\n            index = length,\n            placeholder = getPlaceholder(wrapper);\n\n        while (index--) {\n          args[index] = arguments[index];\n        }\n        var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)\n          ? []\n          : replaceHolders(args, placeholder);\n\n        length -= holders.length;\n        if (length < arity) {\n          return createRecurryWrapper(\n            func, bitmask, createHybridWrapper, wrapper.placeholder, undefined,\n            args, holders, undefined, undefined, arity - length);\n        }\n        var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n        return apply(fn, this, args);\n      }\n      return wrapper;\n    }\n\n    /**\n     * Creates a `_.flow` or `_.flowRight` function.\n     *\n     * @private\n     * @param {boolean} [fromRight] Specify iterating from right to left.\n     * @returns {Function} Returns the new flow function.\n     */\n    function createFlow(fromRight) {\n      return rest(function(funcs) {\n        funcs = baseFlatten(funcs, 1);\n\n        var length = funcs.length,\n            index = length,\n            prereq = LodashWrapper.prototype.thru;\n\n        if (fromRight) {\n          funcs.reverse();\n        }\n        while (index--) {\n          var func = funcs[index];\n          if (typeof func != 'function') {\n            throw new TypeError(FUNC_ERROR_TEXT);\n          }\n          if (prereq && !wrapper && getFuncName(func) == 'wrapper') {\n            var wrapper = new LodashWrapper([], true);\n          }\n        }\n        index = wrapper ? index : length;\n        while (++index < length) {\n          func = funcs[index];\n\n          var funcName = getFuncName(func),\n              data = funcName == 'wrapper' ? getData(func) : undefined;\n\n          if (data && isLaziable(data[0]) &&\n                data[1] == (ARY_FLAG | CURRY_FLAG | PARTIAL_FLAG | REARG_FLAG) &&\n                !data[4].length && data[9] == 1\n              ) {\n            wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);\n          } else {\n            wrapper = (func.length == 1 && isLaziable(func))\n              ? wrapper[funcName]()\n              : wrapper.thru(func);\n          }\n        }\n        return function() {\n          var args = arguments,\n              value = args[0];\n\n          if (wrapper && args.length == 1 &&\n              isArray(value) && value.length >= LARGE_ARRAY_SIZE) {\n            return wrapper.plant(value).value();\n          }\n          var index = 0,\n              result = length ? funcs[index].apply(this, args) : value;\n\n          while (++index < length) {\n            result = funcs[index].call(this, result);\n          }\n          return result;\n        };\n      });\n    }\n\n    /**\n     * Creates a function that wraps `func` to invoke it with optional `this`\n     * binding of `thisArg`, partial application, and currying.\n     *\n     * @private\n     * @param {Function|string} func The function or method name to wrap.\n     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`\n     *  for more details.\n     * @param {*} [thisArg] The `this` binding of `func`.\n     * @param {Array} [partials] The arguments to prepend to those provided to\n     *  the new function.\n     * @param {Array} [holders] The `partials` placeholder indexes.\n     * @param {Array} [partialsRight] The arguments to append to those provided\n     *  to the new function.\n     * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.\n     * @param {Array} [argPos] The argument positions of the new function.\n     * @param {number} [ary] The arity cap of `func`.\n     * @param {number} [arity] The arity of `func`.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {\n      var isAry = bitmask & ARY_FLAG,\n          isBind = bitmask & BIND_FLAG,\n          isBindKey = bitmask & BIND_KEY_FLAG,\n          isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),\n          isFlip = bitmask & FLIP_FLAG,\n          Ctor = isBindKey ? undefined : createCtorWrapper(func);\n\n      function wrapper() {\n        var length = arguments.length,\n            index = length,\n            args = Array(length);\n\n        while (index--) {\n          args[index] = arguments[index];\n        }\n        if (isCurried) {\n          var placeholder = getPlaceholder(wrapper),\n              holdersCount = countHolders(args, placeholder);\n        }\n        if (partials) {\n          args = composeArgs(args, partials, holders, isCurried);\n        }\n        if (partialsRight) {\n          args = composeArgsRight(args, partialsRight, holdersRight, isCurried);\n        }\n        length -= holdersCount;\n        if (isCurried && length < arity) {\n          var newHolders = replaceHolders(args, placeholder);\n          return createRecurryWrapper(\n            func, bitmask, createHybridWrapper, wrapper.placeholder, thisArg,\n            args, newHolders, argPos, ary, arity - length\n          );\n        }\n        var thisBinding = isBind ? thisArg : this,\n            fn = isBindKey ? thisBinding[func] : func;\n\n        length = args.length;\n        if (argPos) {\n          args = reorder(args, argPos);\n        } else if (isFlip && length > 1) {\n          args.reverse();\n        }\n        if (isAry && ary < length) {\n          args.length = ary;\n        }\n        if (this && this !== root && this instanceof wrapper) {\n          fn = Ctor || createCtorWrapper(fn);\n        }\n        return fn.apply(thisBinding, args);\n      }\n      return wrapper;\n    }\n\n    /**\n     * Creates a function like `_.invertBy`.\n     *\n     * @private\n     * @param {Function} setter The function to set accumulator values.\n     * @param {Function} toIteratee The function to resolve iteratees.\n     * @returns {Function} Returns the new inverter function.\n     */\n    function createInverter(setter, toIteratee) {\n      return function(object, iteratee) {\n        return baseInverter(object, setter, toIteratee(iteratee), {});\n      };\n    }\n\n    /**\n     * Creates a function like `_.over`.\n     *\n     * @private\n     * @param {Function} arrayFunc The function to iterate over iteratees.\n     * @returns {Function} Returns the new invoker function.\n     */\n    function createOver(arrayFunc) {\n      return rest(function(iteratees) {\n        iteratees = (iteratees.length == 1 && isArray(iteratees[0]))\n          ? arrayMap(iteratees[0], baseUnary(getIteratee()))\n          : arrayMap(baseFlatten(iteratees, 1, isFlattenableIteratee), baseUnary(getIteratee()));\n\n        return rest(function(args) {\n          var thisArg = this;\n          return arrayFunc(iteratees, function(iteratee) {\n            return apply(iteratee, thisArg, args);\n          });\n        });\n      });\n    }\n\n    /**\n     * Creates the padding for `string` based on `length`. The `chars` string\n     * is truncated if the number of characters exceeds `length`.\n     *\n     * @private\n     * @param {number} length The padding length.\n     * @param {string} [chars=' '] The string used as padding.\n     * @returns {string} Returns the padding for `string`.\n     */\n    function createPadding(length, chars) {\n      chars = chars === undefined ? ' ' : (chars + '');\n\n      var charsLength = chars.length;\n      if (charsLength < 2) {\n        return charsLength ? baseRepeat(chars, length) : chars;\n      }\n      var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));\n      return reHasComplexSymbol.test(chars)\n        ? castSlice(stringToArray(result), 0, length).join('')\n        : result.slice(0, length);\n    }\n\n    /**\n     * Creates a function that wraps `func` to invoke it with the `this` binding\n     * of `thisArg` and `partials` prepended to the arguments it receives.\n     *\n     * @private\n     * @param {Function} func The function to wrap.\n     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`\n     *  for more details.\n     * @param {*} thisArg The `this` binding of `func`.\n     * @param {Array} partials The arguments to prepend to those provided to\n     *  the new function.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createPartialWrapper(func, bitmask, thisArg, partials) {\n      var isBind = bitmask & BIND_FLAG,\n          Ctor = createCtorWrapper(func);\n\n      function wrapper() {\n        var argsIndex = -1,\n            argsLength = arguments.length,\n            leftIndex = -1,\n            leftLength = partials.length,\n            args = Array(leftLength + argsLength),\n            fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;\n\n        while (++leftIndex < leftLength) {\n          args[leftIndex] = partials[leftIndex];\n        }\n        while (argsLength--) {\n          args[leftIndex++] = arguments[++argsIndex];\n        }\n        return apply(fn, isBind ? thisArg : this, args);\n      }\n      return wrapper;\n    }\n\n    /**\n     * Creates a `_.range` or `_.rangeRight` function.\n     *\n     * @private\n     * @param {boolean} [fromRight] Specify iterating from right to left.\n     * @returns {Function} Returns the new range function.\n     */\n    function createRange(fromRight) {\n      return function(start, end, step) {\n        if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {\n          end = step = undefined;\n        }\n        // Ensure the sign of `-0` is preserved.\n        start = toNumber(start);\n        start = start === start ? start : 0;\n        if (end === undefined) {\n          end = start;\n          start = 0;\n        } else {\n          end = toNumber(end) || 0;\n        }\n        step = step === undefined ? (start < end ? 1 : -1) : (toNumber(step) || 0);\n        return baseRange(start, end, step, fromRight);\n      };\n    }\n\n    /**\n     * Creates a function that wraps `func` to continue currying.\n     *\n     * @private\n     * @param {Function} func The function to wrap.\n     * @param {number} bitmask The bitmask of wrapper flags. See `createWrapper`\n     *  for more details.\n     * @param {Function} wrapFunc The function to create the `func` wrapper.\n     * @param {*} placeholder The placeholder value.\n     * @param {*} [thisArg] The `this` binding of `func`.\n     * @param {Array} [partials] The arguments to prepend to those provided to\n     *  the new function.\n     * @param {Array} [holders] The `partials` placeholder indexes.\n     * @param {Array} [argPos] The argument positions of the new function.\n     * @param {number} [ary] The arity cap of `func`.\n     * @param {number} [arity] The arity of `func`.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createRecurryWrapper(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {\n      var isCurry = bitmask & CURRY_FLAG,\n          newHolders = isCurry ? holders : undefined,\n          newHoldersRight = isCurry ? undefined : holders,\n          newPartials = isCurry ? partials : undefined,\n          newPartialsRight = isCurry ? undefined : partials;\n\n      bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG);\n      bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG);\n\n      if (!(bitmask & CURRY_BOUND_FLAG)) {\n        bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);\n      }\n      var newData = [\n        func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,\n        newHoldersRight, argPos, ary, arity\n      ];\n\n      var result = wrapFunc.apply(undefined, newData);\n      if (isLaziable(func)) {\n        setData(result, newData);\n      }\n      result.placeholder = placeholder;\n      return result;\n    }\n\n    /**\n     * Creates a function like `_.round`.\n     *\n     * @private\n     * @param {string} methodName The name of the `Math` method to use when rounding.\n     * @returns {Function} Returns the new round function.\n     */\n    function createRound(methodName) {\n      var func = Math[methodName];\n      return function(number, precision) {\n        number = toNumber(number);\n        precision = toInteger(precision);\n        if (precision) {\n          // Shift with exponential notation to avoid floating-point issues.\n          // See [MDN](https://mdn.io/round#Examples) for more details.\n          var pair = (toString(number) + 'e').split('e'),\n              value = func(pair[0] + 'e' + (+pair[1] + precision));\n\n          pair = (toString(value) + 'e').split('e');\n          return +(pair[0] + 'e' + (+pair[1] - precision));\n        }\n        return func(number);\n      };\n    }\n\n    /**\n     * Creates a set of `values`.\n     *\n     * @private\n     * @param {Array} values The values to add to the set.\n     * @returns {Object} Returns the new set.\n     */\n    var createSet = !(Set && new Set([1, 2]).size === 2) ? noop : function(values) {\n      return new Set(values);\n    };\n\n    /**\n     * Creates a function that either curries or invokes `func` with optional\n     * `this` binding and partially applied arguments.\n     *\n     * @private\n     * @param {Function|string} func The function or method name to wrap.\n     * @param {number} bitmask The bitmask of wrapper flags.\n     *  The bitmask may be composed of the following flags:\n     *     1 - `_.bind`\n     *     2 - `_.bindKey`\n     *     4 - `_.curry` or `_.curryRight` of a bound function\n     *     8 - `_.curry`\n     *    16 - `_.curryRight`\n     *    32 - `_.partial`\n     *    64 - `_.partialRight`\n     *   128 - `_.rearg`\n     *   256 - `_.ary`\n     * @param {*} [thisArg] The `this` binding of `func`.\n     * @param {Array} [partials] The arguments to be partially applied.\n     * @param {Array} [holders] The `partials` placeholder indexes.\n     * @param {Array} [argPos] The argument positions of the new function.\n     * @param {number} [ary] The arity cap of `func`.\n     * @param {number} [arity] The arity of `func`.\n     * @returns {Function} Returns the new wrapped function.\n     */\n    function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {\n      var isBindKey = bitmask & BIND_KEY_FLAG;\n      if (!isBindKey && typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      var length = partials ? partials.length : 0;\n      if (!length) {\n        bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG);\n        partials = holders = undefined;\n      }\n      ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);\n      arity = arity === undefined ? arity : toInteger(arity);\n      length -= holders ? holders.length : 0;\n\n      if (bitmask & PARTIAL_RIGHT_FLAG) {\n        var partialsRight = partials,\n            holdersRight = holders;\n\n        partials = holders = undefined;\n      }\n      var data = isBindKey ? undefined : getData(func);\n\n      var newData = [\n        func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,\n        argPos, ary, arity\n      ];\n\n      if (data) {\n        mergeData(newData, data);\n      }\n      func = newData[0];\n      bitmask = newData[1];\n      thisArg = newData[2];\n      partials = newData[3];\n      holders = newData[4];\n      arity = newData[9] = newData[9] == null\n        ? (isBindKey ? 0 : func.length)\n        : nativeMax(newData[9] - length, 0);\n\n      if (!arity && bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG)) {\n        bitmask &= ~(CURRY_FLAG | CURRY_RIGHT_FLAG);\n      }\n      if (!bitmask || bitmask == BIND_FLAG) {\n        var result = createBaseWrapper(func, bitmask, thisArg);\n      } else if (bitmask == CURRY_FLAG || bitmask == CURRY_RIGHT_FLAG) {\n        result = createCurryWrapper(func, bitmask, arity);\n      } else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !holders.length) {\n        result = createPartialWrapper(func, bitmask, thisArg, partials);\n      } else {\n        result = createHybridWrapper.apply(undefined, newData);\n      }\n      var setter = data ? baseSetData : setData;\n      return setter(result, newData);\n    }\n\n    /**\n     * A specialized version of `baseIsEqualDeep` for arrays with support for\n     * partial deep comparisons.\n     *\n     * @private\n     * @param {Array} array The array to compare.\n     * @param {Array} other The other array to compare.\n     * @param {Function} equalFunc The function to determine equivalents of values.\n     * @param {Function} customizer The function to customize comparisons.\n     * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`\n     *  for more details.\n     * @param {Object} stack Tracks traversed `array` and `other` objects.\n     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n     */\n    function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {\n      var index = -1,\n          isPartial = bitmask & PARTIAL_COMPARE_FLAG,\n          isUnordered = bitmask & UNORDERED_COMPARE_FLAG,\n          arrLength = array.length,\n          othLength = other.length;\n\n      if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n        return false;\n      }\n      // Assume cyclic values are equal.\n      var stacked = stack.get(array);\n      if (stacked) {\n        return stacked == other;\n      }\n      var result = true;\n      stack.set(array, other);\n\n      // Ignore non-index properties.\n      while (++index < arrLength) {\n        var arrValue = array[index],\n            othValue = other[index];\n\n        if (customizer) {\n          var compared = isPartial\n            ? customizer(othValue, arrValue, index, other, array, stack)\n            : customizer(arrValue, othValue, index, array, other, stack);\n        }\n        if (compared !== undefined) {\n          if (compared) {\n            continue;\n          }\n          result = false;\n          break;\n        }\n        // Recursively compare arrays (susceptible to call stack limits).\n        if (isUnordered) {\n          if (!arraySome(other, function(othValue) {\n                return arrValue === othValue ||\n                  equalFunc(arrValue, othValue, customizer, bitmask, stack);\n              })) {\n            result = false;\n            break;\n          }\n        } else if (!(\n              arrValue === othValue ||\n                equalFunc(arrValue, othValue, customizer, bitmask, stack)\n            )) {\n          result = false;\n          break;\n        }\n      }\n      stack['delete'](array);\n      return result;\n    }\n\n    /**\n     * A specialized version of `baseIsEqualDeep` for comparing objects of\n     * the same `toStringTag`.\n     *\n     * **Note:** This function only supports comparing values with tags of\n     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n     *\n     * @private\n     * @param {Object} object The object to compare.\n     * @param {Object} other The other object to compare.\n     * @param {string} tag The `toStringTag` of the objects to compare.\n     * @param {Function} equalFunc The function to determine equivalents of values.\n     * @param {Function} customizer The function to customize comparisons.\n     * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`\n     *  for more details.\n     * @param {Object} stack Tracks traversed `object` and `other` objects.\n     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n     */\n    function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {\n      switch (tag) {\n        case dataViewTag:\n          if ((object.byteLength != other.byteLength) ||\n              (object.byteOffset != other.byteOffset)) {\n            return false;\n          }\n          object = object.buffer;\n          other = other.buffer;\n\n        case arrayBufferTag:\n          if ((object.byteLength != other.byteLength) ||\n              !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n            return false;\n          }\n          return true;\n\n        case boolTag:\n        case dateTag:\n          // Coerce dates and booleans to numbers, dates to milliseconds and\n          // booleans to `1` or `0` treating invalid dates coerced to `NaN` as\n          // not equal.\n          return +object == +other;\n\n        case errorTag:\n          return object.name == other.name && object.message == other.message;\n\n        case numberTag:\n          // Treat `NaN` vs. `NaN` as equal.\n          return (object != +object) ? other != +other : object == +other;\n\n        case regexpTag:\n        case stringTag:\n          // Coerce regexes to strings and treat strings, primitives and objects,\n          // as equal. See http://www.ecma-international.org/ecma-262/6.0/#sec-regexp.prototype.tostring\n          // for more details.\n          return object == (other + '');\n\n        case mapTag:\n          var convert = mapToArray;\n\n        case setTag:\n          var isPartial = bitmask & PARTIAL_COMPARE_FLAG;\n          convert || (convert = setToArray);\n\n          if (object.size != other.size && !isPartial) {\n            return false;\n          }\n          // Assume cyclic values are equal.\n          var stacked = stack.get(object);\n          if (stacked) {\n            return stacked == other;\n          }\n          bitmask |= UNORDERED_COMPARE_FLAG;\n          stack.set(object, other);\n\n          // Recursively compare objects (susceptible to call stack limits).\n          return equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);\n\n        case symbolTag:\n          if (symbolValueOf) {\n            return symbolValueOf.call(object) == symbolValueOf.call(other);\n          }\n      }\n      return false;\n    }\n\n    /**\n     * A specialized version of `baseIsEqualDeep` for objects with support for\n     * partial deep comparisons.\n     *\n     * @private\n     * @param {Object} object The object to compare.\n     * @param {Object} other The other object to compare.\n     * @param {Function} equalFunc The function to determine equivalents of values.\n     * @param {Function} customizer The function to customize comparisons.\n     * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`\n     *  for more details.\n     * @param {Object} stack Tracks traversed `object` and `other` objects.\n     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n     */\n    function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {\n      var isPartial = bitmask & PARTIAL_COMPARE_FLAG,\n          objProps = keys(object),\n          objLength = objProps.length,\n          othProps = keys(other),\n          othLength = othProps.length;\n\n      if (objLength != othLength && !isPartial) {\n        return false;\n      }\n      var index = objLength;\n      while (index--) {\n        var key = objProps[index];\n        if (!(isPartial ? key in other : baseHas(other, key))) {\n          return false;\n        }\n      }\n      // Assume cyclic values are equal.\n      var stacked = stack.get(object);\n      if (stacked) {\n        return stacked == other;\n      }\n      var result = true;\n      stack.set(object, other);\n\n      var skipCtor = isPartial;\n      while (++index < objLength) {\n        key = objProps[index];\n        var objValue = object[key],\n            othValue = other[key];\n\n        if (customizer) {\n          var compared = isPartial\n            ? customizer(othValue, objValue, key, other, object, stack)\n            : customizer(objValue, othValue, key, object, other, stack);\n        }\n        // Recursively compare objects (susceptible to call stack limits).\n        if (!(compared === undefined\n              ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))\n              : compared\n            )) {\n          result = false;\n          break;\n        }\n        skipCtor || (skipCtor = key == 'constructor');\n      }\n      if (result && !skipCtor) {\n        var objCtor = object.constructor,\n            othCtor = other.constructor;\n\n        // Non `Object` object instances with different constructors are not equal.\n        if (objCtor != othCtor &&\n            ('constructor' in object && 'constructor' in other) &&\n            !(typeof objCtor == 'function' && objCtor instanceof objCtor &&\n              typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n          result = false;\n        }\n      }\n      stack['delete'](object);\n      return result;\n    }\n\n    /**\n     * Creates an array of own enumerable property names and symbols of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property names and symbols.\n     */\n    function getAllKeys(object) {\n      return baseGetAllKeys(object, keys, getSymbols);\n    }\n\n    /**\n     * Creates an array of own and inherited enumerable property names and\n     * symbols of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property names and symbols.\n     */\n    function getAllKeysIn(object) {\n      return baseGetAllKeys(object, keysIn, getSymbolsIn);\n    }\n\n    /**\n     * Gets metadata for `func`.\n     *\n     * @private\n     * @param {Function} func The function to query.\n     * @returns {*} Returns the metadata for `func`.\n     */\n    var getData = !metaMap ? noop : function(func) {\n      return metaMap.get(func);\n    };\n\n    /**\n     * Gets the name of `func`.\n     *\n     * @private\n     * @param {Function} func The function to query.\n     * @returns {string} Returns the function name.\n     */\n    function getFuncName(func) {\n      var result = (func.name + ''),\n          array = realNames[result],\n          length = hasOwnProperty.call(realNames, result) ? array.length : 0;\n\n      while (length--) {\n        var data = array[length],\n            otherFunc = data.func;\n        if (otherFunc == null || otherFunc == func) {\n          return data.name;\n        }\n      }\n      return result;\n    }\n\n    /**\n     * Gets the appropriate \"iteratee\" function. If `_.iteratee` is customized,\n     * this function returns the custom method, otherwise it returns `baseIteratee`.\n     * If arguments are provided, the chosen function is invoked with them and\n     * its result is returned.\n     *\n     * @private\n     * @param {*} [value] The value to convert to an iteratee.\n     * @param {number} [arity] The arity of the created iteratee.\n     * @returns {Function} Returns the chosen function or its result.\n     */\n    function getIteratee() {\n      var result = lodash.iteratee || iteratee;\n      result = result === iteratee ? baseIteratee : result;\n      return arguments.length ? result(arguments[0], arguments[1]) : result;\n    }\n\n    /**\n     * Gets the \"length\" property value of `object`.\n     *\n     * **Note:** This function is used to avoid a\n     * [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) that affects\n     * Safari on at least iOS 8.1-8.3 ARM64.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {*} Returns the \"length\" value.\n     */\n    var getLength = baseProperty('length');\n\n    /**\n     * Gets the property names, values, and compare flags of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the match data of `object`.\n     */\n    function getMatchData(object) {\n      var result = toPairs(object),\n          length = result.length;\n\n      while (length--) {\n        result[length][2] = isStrictComparable(result[length][1]);\n      }\n      return result;\n    }\n\n    /**\n     * Gets the native function at `key` of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {string} key The key of the method to get.\n     * @returns {*} Returns the function if it's native, else `undefined`.\n     */\n    function getNative(object, key) {\n      var value = object[key];\n      return isNative(value) ? value : undefined;\n    }\n\n    /**\n     * Gets the argument placeholder value for `func`.\n     *\n     * @private\n     * @param {Function} func The function to inspect.\n     * @returns {*} Returns the placeholder value.\n     */\n    function getPlaceholder(func) {\n      var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;\n      return object.placeholder;\n    }\n\n    /**\n     * Gets the `[[Prototype]]` of `value`.\n     *\n     * @private\n     * @param {*} value The value to query.\n     * @returns {null|Object} Returns the `[[Prototype]]`.\n     */\n    function getPrototype(value) {\n      return nativeGetPrototype(Object(value));\n    }\n\n    /**\n     * Creates an array of the own enumerable symbol properties of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of symbols.\n     */\n    function getSymbols(object) {\n      // Coerce `object` to an object to avoid non-object errors in V8.\n      // See https://bugs.chromium.org/p/v8/issues/detail?id=3443 for more details.\n      return getOwnPropertySymbols(Object(object));\n    }\n\n    // Fallback for IE < 11.\n    if (!getOwnPropertySymbols) {\n      getSymbols = function() {\n        return [];\n      };\n    }\n\n    /**\n     * Creates an array of the own and inherited enumerable symbol properties\n     * of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of symbols.\n     */\n    var getSymbolsIn = !getOwnPropertySymbols ? getSymbols : function(object) {\n      var result = [];\n      while (object) {\n        arrayPush(result, getSymbols(object));\n        object = getPrototype(object);\n      }\n      return result;\n    };\n\n    /**\n     * Gets the `toStringTag` of `value`.\n     *\n     * @private\n     * @param {*} value The value to query.\n     * @returns {string} Returns the `toStringTag`.\n     */\n    function getTag(value) {\n      return objectToString.call(value);\n    }\n\n    // Fallback for data views, maps, sets, and weak maps in IE 11,\n    // for data views in Edge, and promises in Node.js.\n    if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n        (Map && getTag(new Map) != mapTag) ||\n        (Promise && getTag(Promise.resolve()) != promiseTag) ||\n        (Set && getTag(new Set) != setTag) ||\n        (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n      getTag = function(value) {\n        var result = objectToString.call(value),\n            Ctor = result == objectTag ? value.constructor : undefined,\n            ctorString = Ctor ? toSource(Ctor) : undefined;\n\n        if (ctorString) {\n          switch (ctorString) {\n            case dataViewCtorString: return dataViewTag;\n            case mapCtorString: return mapTag;\n            case promiseCtorString: return promiseTag;\n            case setCtorString: return setTag;\n            case weakMapCtorString: return weakMapTag;\n          }\n        }\n        return result;\n      };\n    }\n\n    /**\n     * Gets the view, applying any `transforms` to the `start` and `end` positions.\n     *\n     * @private\n     * @param {number} start The start of the view.\n     * @param {number} end The end of the view.\n     * @param {Array} transforms The transformations to apply to the view.\n     * @returns {Object} Returns an object containing the `start` and `end`\n     *  positions of the view.\n     */\n    function getView(start, end, transforms) {\n      var index = -1,\n          length = transforms.length;\n\n      while (++index < length) {\n        var data = transforms[index],\n            size = data.size;\n\n        switch (data.type) {\n          case 'drop':      start += size; break;\n          case 'dropRight': end -= size; break;\n          case 'take':      end = nativeMin(end, start + size); break;\n          case 'takeRight': start = nativeMax(start, end - size); break;\n        }\n      }\n      return { 'start': start, 'end': end };\n    }\n\n    /**\n     * Checks if `path` exists on `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path to check.\n     * @param {Function} hasFunc The function to check properties.\n     * @returns {boolean} Returns `true` if `path` exists, else `false`.\n     */\n    function hasPath(object, path, hasFunc) {\n      path = isKey(path, object) ? [path] : castPath(path);\n\n      var result,\n          index = -1,\n          length = path.length;\n\n      while (++index < length) {\n        var key = path[index];\n        if (!(result = object != null && hasFunc(object, key))) {\n          break;\n        }\n        object = object[key];\n      }\n      if (result) {\n        return result;\n      }\n      var length = object ? object.length : 0;\n      return !!length && isLength(length) && isIndex(key, length) &&\n        (isArray(object) || isString(object) || isArguments(object));\n    }\n\n    /**\n     * Initializes an array clone.\n     *\n     * @private\n     * @param {Array} array The array to clone.\n     * @returns {Array} Returns the initialized clone.\n     */\n    function initCloneArray(array) {\n      var length = array.length,\n          result = array.constructor(length);\n\n      // Add properties assigned by `RegExp#exec`.\n      if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {\n        result.index = array.index;\n        result.input = array.input;\n      }\n      return result;\n    }\n\n    /**\n     * Initializes an object clone.\n     *\n     * @private\n     * @param {Object} object The object to clone.\n     * @returns {Object} Returns the initialized clone.\n     */\n    function initCloneObject(object) {\n      return (typeof object.constructor == 'function' && !isPrototype(object))\n        ? baseCreate(getPrototype(object))\n        : {};\n    }\n\n    /**\n     * Initializes an object clone based on its `toStringTag`.\n     *\n     * **Note:** This function only supports cloning values with tags of\n     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n     *\n     * @private\n     * @param {Object} object The object to clone.\n     * @param {string} tag The `toStringTag` of the object to clone.\n     * @param {Function} cloneFunc The function to clone values.\n     * @param {boolean} [isDeep] Specify a deep clone.\n     * @returns {Object} Returns the initialized clone.\n     */\n    function initCloneByTag(object, tag, cloneFunc, isDeep) {\n      var Ctor = object.constructor;\n      switch (tag) {\n        case arrayBufferTag:\n          return cloneArrayBuffer(object);\n\n        case boolTag:\n        case dateTag:\n          return new Ctor(+object);\n\n        case dataViewTag:\n          return cloneDataView(object, isDeep);\n\n        case float32Tag: case float64Tag:\n        case int8Tag: case int16Tag: case int32Tag:\n        case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:\n          return cloneTypedArray(object, isDeep);\n\n        case mapTag:\n          return cloneMap(object, isDeep, cloneFunc);\n\n        case numberTag:\n        case stringTag:\n          return new Ctor(object);\n\n        case regexpTag:\n          return cloneRegExp(object);\n\n        case setTag:\n          return cloneSet(object, isDeep, cloneFunc);\n\n        case symbolTag:\n          return cloneSymbol(object);\n      }\n    }\n\n    /**\n     * Creates an array of index keys for `object` values of arrays,\n     * `arguments` objects, and strings, otherwise `null` is returned.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @returns {Array|null} Returns index keys, else `null`.\n     */\n    function indexKeys(object) {\n      var length = object ? object.length : undefined;\n      if (isLength(length) &&\n          (isArray(object) || isString(object) || isArguments(object))) {\n        return baseTimes(length, String);\n      }\n      return null;\n    }\n\n    /**\n     * Checks if `value` is a flattenable `arguments` object or array.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n     */\n    function isFlattenable(value) {\n      return isArrayLikeObject(value) && (isArray(value) || isArguments(value));\n    }\n\n    /**\n     * Checks if `value` is a flattenable array and not a `_.matchesProperty`\n     * iteratee shorthand.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.\n     */\n    function isFlattenableIteratee(value) {\n      return isArray(value) && !(value.length == 2 && !isFunction(value[0]));\n    }\n\n    /**\n     * Checks if the given arguments are from an iteratee call.\n     *\n     * @private\n     * @param {*} value The potential iteratee value argument.\n     * @param {*} index The potential iteratee index or key argument.\n     * @param {*} object The potential iteratee object argument.\n     * @returns {boolean} Returns `true` if the arguments are from an iteratee call,\n     *  else `false`.\n     */\n    function isIterateeCall(value, index, object) {\n      if (!isObject(object)) {\n        return false;\n      }\n      var type = typeof index;\n      if (type == 'number'\n            ? (isArrayLike(object) && isIndex(index, object.length))\n            : (type == 'string' && index in object)\n          ) {\n        return eq(object[index], value);\n      }\n      return false;\n    }\n\n    /**\n     * Checks if `value` is a property name and not a property path.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @param {Object} [object] The object to query keys on.\n     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.\n     */\n    function isKey(value, object) {\n      var type = typeof value;\n      if (type == 'number' || type == 'symbol') {\n        return true;\n      }\n      return !isArray(value) &&\n        (isSymbol(value) || reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||\n          (object != null && value in Object(object)));\n    }\n\n    /**\n     * Checks if `value` is suitable for use as unique object key.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n     */\n    function isKeyable(value) {\n      var type = typeof value;\n      return type == 'number' || type == 'boolean' ||\n        (type == 'string' && value != '__proto__') || value == null;\n    }\n\n    /**\n     * Checks if `func` has a lazy counterpart.\n     *\n     * @private\n     * @param {Function} func The function to check.\n     * @returns {boolean} Returns `true` if `func` has a lazy counterpart,\n     *  else `false`.\n     */\n    function isLaziable(func) {\n      var funcName = getFuncName(func),\n          other = lodash[funcName];\n\n      if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {\n        return false;\n      }\n      if (func === other) {\n        return true;\n      }\n      var data = getData(other);\n      return !!data && func === data[0];\n    }\n\n    /**\n     * Checks if `value` is likely a prototype object.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n     */\n    function isPrototype(value) {\n      var Ctor = value && value.constructor,\n          proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n      return value === proto;\n    }\n\n    /**\n     * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.\n     *\n     * @private\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` if suitable for strict\n     *  equality comparisons, else `false`.\n     */\n    function isStrictComparable(value) {\n      return value === value && !isObject(value);\n    }\n\n    /**\n     * A specialized version of `matchesProperty` for source values suitable\n     * for strict equality comparisons, i.e. `===`.\n     *\n     * @private\n     * @param {string} key The key of the property to get.\n     * @param {*} srcValue The value to match.\n     * @returns {Function} Returns the new function.\n     */\n    function matchesStrictComparable(key, srcValue) {\n      return function(object) {\n        if (object == null) {\n          return false;\n        }\n        return object[key] === srcValue &&\n          (srcValue !== undefined || (key in Object(object)));\n      };\n    }\n\n    /**\n     * Merges the function metadata of `source` into `data`.\n     *\n     * Merging metadata reduces the number of wrappers used to invoke a function.\n     * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`\n     * may be applied regardless of execution order. Methods like `_.ary` and\n     * `_.rearg` modify function arguments, making the order in which they are\n     * executed important, preventing the merging of metadata. However, we make\n     * an exception for a safe combined case where curried functions have `_.ary`\n     * and or `_.rearg` applied.\n     *\n     * @private\n     * @param {Array} data The destination metadata.\n     * @param {Array} source The source metadata.\n     * @returns {Array} Returns `data`.\n     */\n    function mergeData(data, source) {\n      var bitmask = data[1],\n          srcBitmask = source[1],\n          newBitmask = bitmask | srcBitmask,\n          isCommon = newBitmask < (BIND_FLAG | BIND_KEY_FLAG | ARY_FLAG);\n\n      var isCombo =\n        ((srcBitmask == ARY_FLAG) && (bitmask == CURRY_FLAG)) ||\n        ((srcBitmask == ARY_FLAG) && (bitmask == REARG_FLAG) && (data[7].length <= source[8])) ||\n        ((srcBitmask == (ARY_FLAG | REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == CURRY_FLAG));\n\n      // Exit early if metadata can't be merged.\n      if (!(isCommon || isCombo)) {\n        return data;\n      }\n      // Use source `thisArg` if available.\n      if (srcBitmask & BIND_FLAG) {\n        data[2] = source[2];\n        // Set when currying a bound function.\n        newBitmask |= bitmask & BIND_FLAG ? 0 : CURRY_BOUND_FLAG;\n      }\n      // Compose partial arguments.\n      var value = source[3];\n      if (value) {\n        var partials = data[3];\n        data[3] = partials ? composeArgs(partials, value, source[4]) : value;\n        data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];\n      }\n      // Compose partial right arguments.\n      value = source[5];\n      if (value) {\n        partials = data[5];\n        data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;\n        data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];\n      }\n      // Use source `argPos` if available.\n      value = source[7];\n      if (value) {\n        data[7] = value;\n      }\n      // Use source `ary` if it's smaller.\n      if (srcBitmask & ARY_FLAG) {\n        data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);\n      }\n      // Use source `arity` if one is not provided.\n      if (data[9] == null) {\n        data[9] = source[9];\n      }\n      // Use source `func` and merge bitmasks.\n      data[0] = source[0];\n      data[1] = newBitmask;\n\n      return data;\n    }\n\n    /**\n     * Used by `_.defaultsDeep` to customize its `_.merge` use.\n     *\n     * @private\n     * @param {*} objValue The destination value.\n     * @param {*} srcValue The source value.\n     * @param {string} key The key of the property to merge.\n     * @param {Object} object The parent object of `objValue`.\n     * @param {Object} source The parent object of `srcValue`.\n     * @param {Object} [stack] Tracks traversed source values and their merged\n     *  counterparts.\n     * @returns {*} Returns the value to assign.\n     */\n    function mergeDefaults(objValue, srcValue, key, object, source, stack) {\n      if (isObject(objValue) && isObject(srcValue)) {\n        baseMerge(objValue, srcValue, undefined, mergeDefaults, stack.set(srcValue, objValue));\n      }\n      return objValue;\n    }\n\n    /**\n     * Gets the parent value at `path` of `object`.\n     *\n     * @private\n     * @param {Object} object The object to query.\n     * @param {Array} path The path to get the parent value of.\n     * @returns {*} Returns the parent value.\n     */\n    function parent(object, path) {\n      return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1));\n    }\n\n    /**\n     * Reorder `array` according to the specified indexes where the element at\n     * the first index is assigned as the first element, the element at\n     * the second index is assigned as the second element, and so on.\n     *\n     * @private\n     * @param {Array} array The array to reorder.\n     * @param {Array} indexes The arranged array indexes.\n     * @returns {Array} Returns `array`.\n     */\n    function reorder(array, indexes) {\n      var arrLength = array.length,\n          length = nativeMin(indexes.length, arrLength),\n          oldArray = copyArray(array);\n\n      while (length--) {\n        var index = indexes[length];\n        array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;\n      }\n      return array;\n    }\n\n    /**\n     * Sets metadata for `func`.\n     *\n     * **Note:** If this function becomes hot, i.e. is invoked a lot in a short\n     * period of time, it will trip its breaker and transition to an identity\n     * function to avoid garbage collection pauses in V8. See\n     * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)\n     * for more details.\n     *\n     * @private\n     * @param {Function} func The function to associate metadata with.\n     * @param {*} data The metadata.\n     * @returns {Function} Returns `func`.\n     */\n    var setData = (function() {\n      var count = 0,\n          lastCalled = 0;\n\n      return function(key, value) {\n        var stamp = now(),\n            remaining = HOT_SPAN - (stamp - lastCalled);\n\n        lastCalled = stamp;\n        if (remaining > 0) {\n          if (++count >= HOT_COUNT) {\n            return key;\n          }\n        } else {\n          count = 0;\n        }\n        return baseSetData(key, value);\n      };\n    }());\n\n    /**\n     * Converts `string` to a property path array.\n     *\n     * @private\n     * @param {string} string The string to convert.\n     * @returns {Array} Returns the property path array.\n     */\n    var stringToPath = memoize(function(string) {\n      var result = [];\n      toString(string).replace(rePropName, function(match, number, quote, string) {\n        result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));\n      });\n      return result;\n    });\n\n    /**\n     * Converts `value` to a string key if it's not a string or symbol.\n     *\n     * @private\n     * @param {*} value The value to inspect.\n     * @returns {string|symbol} Returns the key.\n     */\n    function toKey(key) {\n      return (typeof key == 'string' || isSymbol(key)) ? key : (key + '');\n    }\n\n    /**\n     * Converts `func` to its source code.\n     *\n     * @private\n     * @param {Function} func The function to process.\n     * @returns {string} Returns the source code.\n     */\n    function toSource(func) {\n      if (func != null) {\n        try {\n          return funcToString.call(func);\n        } catch (e) {}\n        try {\n          return (func + '');\n        } catch (e) {}\n      }\n      return '';\n    }\n\n    /**\n     * Creates a clone of `wrapper`.\n     *\n     * @private\n     * @param {Object} wrapper The wrapper to clone.\n     * @returns {Object} Returns the cloned wrapper.\n     */\n    function wrapperClone(wrapper) {\n      if (wrapper instanceof LazyWrapper) {\n        return wrapper.clone();\n      }\n      var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);\n      result.__actions__ = copyArray(wrapper.__actions__);\n      result.__index__  = wrapper.__index__;\n      result.__values__ = wrapper.__values__;\n      return result;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates an array of elements split into groups the length of `size`.\n     * If `array` can't be split evenly, the final chunk will be the remaining\n     * elements.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to process.\n     * @param {number} [size=1] The length of each chunk\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the new array containing chunks.\n     * @example\n     *\n     * _.chunk(['a', 'b', 'c', 'd'], 2);\n     * // => [['a', 'b'], ['c', 'd']]\n     *\n     * _.chunk(['a', 'b', 'c', 'd'], 3);\n     * // => [['a', 'b', 'c'], ['d']]\n     */\n    function chunk(array, size, guard) {\n      if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {\n        size = 1;\n      } else {\n        size = nativeMax(toInteger(size), 0);\n      }\n      var length = array ? array.length : 0;\n      if (!length || size < 1) {\n        return [];\n      }\n      var index = 0,\n          resIndex = 0,\n          result = Array(nativeCeil(length / size));\n\n      while (index < length) {\n        result[resIndex++] = baseSlice(array, index, (index += size));\n      }\n      return result;\n    }\n\n    /**\n     * Creates an array with all falsey values removed. The values `false`, `null`,\n     * `0`, `\"\"`, `undefined`, and `NaN` are falsey.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to compact.\n     * @returns {Array} Returns the new array of filtered values.\n     * @example\n     *\n     * _.compact([0, 1, false, 2, '', 3]);\n     * // => [1, 2, 3]\n     */\n    function compact(array) {\n      var index = -1,\n          length = array ? array.length : 0,\n          resIndex = 0,\n          result = [];\n\n      while (++index < length) {\n        var value = array[index];\n        if (value) {\n          result[resIndex++] = value;\n        }\n      }\n      return result;\n    }\n\n    /**\n     * Creates a new array concatenating `array` with any additional arrays\n     * and/or values.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to concatenate.\n     * @param {...*} [values] The values to concatenate.\n     * @returns {Array} Returns the new concatenated array.\n     * @example\n     *\n     * var array = [1];\n     * var other = _.concat(array, 2, [3], [[4]]);\n     *\n     * console.log(other);\n     * // => [1, 2, 3, [4]]\n     *\n     * console.log(array);\n     * // => [1]\n     */\n    function concat() {\n      var length = arguments.length,\n          array = castArray(arguments[0]);\n\n      if (length < 2) {\n        return length ? copyArray(array) : [];\n      }\n      var args = Array(length - 1);\n      while (length--) {\n        args[length - 1] = arguments[length];\n      }\n      return arrayConcat(array, baseFlatten(args, 1));\n    }\n\n    /**\n     * Creates an array of unique `array` values not included in the other given\n     * arrays using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons. The order of result values is determined by the\n     * order they occur in the first array.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @param {...Array} [values] The values to exclude.\n     * @returns {Array} Returns the new array of filtered values.\n     * @example\n     *\n     * _.difference([3, 2, 1], [4, 2]);\n     * // => [3, 1]\n     */\n    var difference = rest(function(array, values) {\n      return isArrayLikeObject(array)\n        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))\n        : [];\n    });\n\n    /**\n     * This method is like `_.difference` except that it accepts `iteratee` which\n     * is invoked for each element of `array` and `values` to generate the criterion\n     * by which they're compared. Result values are chosen from the first array.\n     * The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @param {...Array} [values] The values to exclude.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Array} Returns the new array of filtered values.\n     * @example\n     *\n     * _.differenceBy([3.1, 2.2, 1.3], [4.4, 2.5], Math.floor);\n     * // => [3.1, 1.3]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');\n     * // => [{ 'x': 2 }]\n     */\n    var differenceBy = rest(function(array, values) {\n      var iteratee = last(values);\n      if (isArrayLikeObject(iteratee)) {\n        iteratee = undefined;\n      }\n      return isArrayLikeObject(array)\n        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee))\n        : [];\n    });\n\n    /**\n     * This method is like `_.difference` except that it accepts `comparator`\n     * which is invoked to compare elements of `array` to `values`. Result values\n     * are chosen from the first array. The comparator is invoked with two arguments:\n     * (arrVal, othVal).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @param {...Array} [values] The values to exclude.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of filtered values.\n     * @example\n     *\n     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n     *\n     * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);\n     * // => [{ 'x': 2, 'y': 1 }]\n     */\n    var differenceWith = rest(function(array, values) {\n      var comparator = last(values);\n      if (isArrayLikeObject(comparator)) {\n        comparator = undefined;\n      }\n      return isArrayLikeObject(array)\n        ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)\n        : [];\n    });\n\n    /**\n     * Creates a slice of `array` with `n` elements dropped from the beginning.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.5.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {number} [n=1] The number of elements to drop.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * _.drop([1, 2, 3]);\n     * // => [2, 3]\n     *\n     * _.drop([1, 2, 3], 2);\n     * // => [3]\n     *\n     * _.drop([1, 2, 3], 5);\n     * // => []\n     *\n     * _.drop([1, 2, 3], 0);\n     * // => [1, 2, 3]\n     */\n    function drop(array, n, guard) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return [];\n      }\n      n = (guard || n === undefined) ? 1 : toInteger(n);\n      return baseSlice(array, n < 0 ? 0 : n, length);\n    }\n\n    /**\n     * Creates a slice of `array` with `n` elements dropped from the end.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {number} [n=1] The number of elements to drop.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * _.dropRight([1, 2, 3]);\n     * // => [1, 2]\n     *\n     * _.dropRight([1, 2, 3], 2);\n     * // => [1]\n     *\n     * _.dropRight([1, 2, 3], 5);\n     * // => []\n     *\n     * _.dropRight([1, 2, 3], 0);\n     * // => [1, 2, 3]\n     */\n    function dropRight(array, n, guard) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return [];\n      }\n      n = (guard || n === undefined) ? 1 : toInteger(n);\n      n = length - n;\n      return baseSlice(array, 0, n < 0 ? 0 : n);\n    }\n\n    /**\n     * Creates a slice of `array` excluding elements dropped from the end.\n     * Elements are dropped until `predicate` returns falsey. The predicate is\n     * invoked with three arguments: (value, index, array).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'active': true },\n     *   { 'user': 'fred',    'active': false },\n     *   { 'user': 'pebbles', 'active': false }\n     * ];\n     *\n     * _.dropRightWhile(users, function(o) { return !o.active; });\n     * // => objects for ['barney']\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });\n     * // => objects for ['barney', 'fred']\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.dropRightWhile(users, ['active', false]);\n     * // => objects for ['barney']\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.dropRightWhile(users, 'active');\n     * // => objects for ['barney', 'fred', 'pebbles']\n     */\n    function dropRightWhile(array, predicate) {\n      return (array && array.length)\n        ? baseWhile(array, getIteratee(predicate, 3), true, true)\n        : [];\n    }\n\n    /**\n     * Creates a slice of `array` excluding elements dropped from the beginning.\n     * Elements are dropped until `predicate` returns falsey. The predicate is\n     * invoked with three arguments: (value, index, array).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'active': false },\n     *   { 'user': 'fred',    'active': false },\n     *   { 'user': 'pebbles', 'active': true }\n     * ];\n     *\n     * _.dropWhile(users, function(o) { return !o.active; });\n     * // => objects for ['pebbles']\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.dropWhile(users, { 'user': 'barney', 'active': false });\n     * // => objects for ['fred', 'pebbles']\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.dropWhile(users, ['active', false]);\n     * // => objects for ['pebbles']\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.dropWhile(users, 'active');\n     * // => objects for ['barney', 'fred', 'pebbles']\n     */\n    function dropWhile(array, predicate) {\n      return (array && array.length)\n        ? baseWhile(array, getIteratee(predicate, 3), true)\n        : [];\n    }\n\n    /**\n     * Fills elements of `array` with `value` from `start` up to, but not\n     * including, `end`.\n     *\n     * **Note:** This method mutates `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.2.0\n     * @category Array\n     * @param {Array} array The array to fill.\n     * @param {*} value The value to fill `array` with.\n     * @param {number} [start=0] The start position.\n     * @param {number} [end=array.length] The end position.\n     * @returns {Array} Returns `array`.\n     * @example\n     *\n     * var array = [1, 2, 3];\n     *\n     * _.fill(array, 'a');\n     * console.log(array);\n     * // => ['a', 'a', 'a']\n     *\n     * _.fill(Array(3), 2);\n     * // => [2, 2, 2]\n     *\n     * _.fill([4, 6, 8, 10], '*', 1, 3);\n     * // => [4, '*', '*', 10]\n     */\n    function fill(array, value, start, end) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return [];\n      }\n      if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {\n        start = 0;\n        end = length;\n      }\n      return baseFill(array, value, start, end);\n    }\n\n    /**\n     * This method is like `_.find` except that it returns the index of the first\n     * element `predicate` returns truthy for instead of the element itself.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.1.0\n     * @category Array\n     * @param {Array} array The array to search.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {number} Returns the index of the found element, else `-1`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'active': false },\n     *   { 'user': 'fred',    'active': false },\n     *   { 'user': 'pebbles', 'active': true }\n     * ];\n     *\n     * _.findIndex(users, function(o) { return o.user == 'barney'; });\n     * // => 0\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.findIndex(users, { 'user': 'fred', 'active': false });\n     * // => 1\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.findIndex(users, ['active', false]);\n     * // => 0\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.findIndex(users, 'active');\n     * // => 2\n     */\n    function findIndex(array, predicate) {\n      return (array && array.length)\n        ? baseFindIndex(array, getIteratee(predicate, 3))\n        : -1;\n    }\n\n    /**\n     * This method is like `_.findIndex` except that it iterates over elements\n     * of `collection` from right to left.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Array\n     * @param {Array} array The array to search.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {number} Returns the index of the found element, else `-1`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'active': true },\n     *   { 'user': 'fred',    'active': false },\n     *   { 'user': 'pebbles', 'active': false }\n     * ];\n     *\n     * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });\n     * // => 2\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.findLastIndex(users, { 'user': 'barney', 'active': true });\n     * // => 0\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.findLastIndex(users, ['active', false]);\n     * // => 2\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.findLastIndex(users, 'active');\n     * // => 0\n     */\n    function findLastIndex(array, predicate) {\n      return (array && array.length)\n        ? baseFindIndex(array, getIteratee(predicate, 3), true)\n        : -1;\n    }\n\n    /**\n     * Flattens `array` a single level deep.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to flatten.\n     * @returns {Array} Returns the new flattened array.\n     * @example\n     *\n     * _.flatten([1, [2, [3, [4]], 5]]);\n     * // => [1, 2, [3, [4]], 5]\n     */\n    function flatten(array) {\n      var length = array ? array.length : 0;\n      return length ? baseFlatten(array, 1) : [];\n    }\n\n    /**\n     * Recursively flattens `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to flatten.\n     * @returns {Array} Returns the new flattened array.\n     * @example\n     *\n     * _.flattenDeep([1, [2, [3, [4]], 5]]);\n     * // => [1, 2, 3, 4, 5]\n     */\n    function flattenDeep(array) {\n      var length = array ? array.length : 0;\n      return length ? baseFlatten(array, INFINITY) : [];\n    }\n\n    /**\n     * Recursively flatten `array` up to `depth` times.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.4.0\n     * @category Array\n     * @param {Array} array The array to flatten.\n     * @param {number} [depth=1] The maximum recursion depth.\n     * @returns {Array} Returns the new flattened array.\n     * @example\n     *\n     * var array = [1, [2, [3, [4]], 5]];\n     *\n     * _.flattenDepth(array, 1);\n     * // => [1, 2, [3, [4]], 5]\n     *\n     * _.flattenDepth(array, 2);\n     * // => [1, 2, 3, [4], 5]\n     */\n    function flattenDepth(array, depth) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return [];\n      }\n      depth = depth === undefined ? 1 : toInteger(depth);\n      return baseFlatten(array, depth);\n    }\n\n    /**\n     * The inverse of `_.toPairs`; this method returns an object composed\n     * from key-value `pairs`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} pairs The key-value pairs.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * _.fromPairs([['fred', 30], ['barney', 40]]);\n     * // => { 'fred': 30, 'barney': 40 }\n     */\n    function fromPairs(pairs) {\n      var index = -1,\n          length = pairs ? pairs.length : 0,\n          result = {};\n\n      while (++index < length) {\n        var pair = pairs[index];\n        result[pair[0]] = pair[1];\n      }\n      return result;\n    }\n\n    /**\n     * Gets the first element of `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @alias first\n     * @category Array\n     * @param {Array} array The array to query.\n     * @returns {*} Returns the first element of `array`.\n     * @example\n     *\n     * _.head([1, 2, 3]);\n     * // => 1\n     *\n     * _.head([]);\n     * // => undefined\n     */\n    function head(array) {\n      return (array && array.length) ? array[0] : undefined;\n    }\n\n    /**\n     * Gets the index at which the first occurrence of `value` is found in `array`\n     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons. If `fromIndex` is negative, it's used as the\n     * offset from the end of `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to search.\n     * @param {*} value The value to search for.\n     * @param {number} [fromIndex=0] The index to search from.\n     * @returns {number} Returns the index of the matched value, else `-1`.\n     * @example\n     *\n     * _.indexOf([1, 2, 1, 2], 2);\n     * // => 1\n     *\n     * // Search from the `fromIndex`.\n     * _.indexOf([1, 2, 1, 2], 2, 2);\n     * // => 3\n     */\n    function indexOf(array, value, fromIndex) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return -1;\n      }\n      fromIndex = toInteger(fromIndex);\n      if (fromIndex < 0) {\n        fromIndex = nativeMax(length + fromIndex, 0);\n      }\n      return baseIndexOf(array, value, fromIndex);\n    }\n\n    /**\n     * Gets all but the last element of `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * _.initial([1, 2, 3]);\n     * // => [1, 2]\n     */\n    function initial(array) {\n      return dropRight(array, 1);\n    }\n\n    /**\n     * Creates an array of unique values that are included in all given arrays\n     * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons. The order of result values is determined by the\n     * order they occur in the first array.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @returns {Array} Returns the new array of intersecting values.\n     * @example\n     *\n     * _.intersection([2, 1], [4, 2], [1, 2]);\n     * // => [2]\n     */\n    var intersection = rest(function(arrays) {\n      var mapped = arrayMap(arrays, castArrayLikeObject);\n      return (mapped.length && mapped[0] === arrays[0])\n        ? baseIntersection(mapped)\n        : [];\n    });\n\n    /**\n     * This method is like `_.intersection` except that it accepts `iteratee`\n     * which is invoked for each element of each `arrays` to generate the criterion\n     * by which they're compared. Result values are chosen from the first array.\n     * The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Array} Returns the new array of intersecting values.\n     * @example\n     *\n     * _.intersectionBy([2.1, 1.2], [4.3, 2.4], Math.floor);\n     * // => [2.1]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n     * // => [{ 'x': 1 }]\n     */\n    var intersectionBy = rest(function(arrays) {\n      var iteratee = last(arrays),\n          mapped = arrayMap(arrays, castArrayLikeObject);\n\n      if (iteratee === last(mapped)) {\n        iteratee = undefined;\n      } else {\n        mapped.pop();\n      }\n      return (mapped.length && mapped[0] === arrays[0])\n        ? baseIntersection(mapped, getIteratee(iteratee))\n        : [];\n    });\n\n    /**\n     * This method is like `_.intersection` except that it accepts `comparator`\n     * which is invoked to compare elements of `arrays`. Result values are chosen\n     * from the first array. The comparator is invoked with two arguments:\n     * (arrVal, othVal).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of intersecting values.\n     * @example\n     *\n     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n     *\n     * _.intersectionWith(objects, others, _.isEqual);\n     * // => [{ 'x': 1, 'y': 2 }]\n     */\n    var intersectionWith = rest(function(arrays) {\n      var comparator = last(arrays),\n          mapped = arrayMap(arrays, castArrayLikeObject);\n\n      if (comparator === last(mapped)) {\n        comparator = undefined;\n      } else {\n        mapped.pop();\n      }\n      return (mapped.length && mapped[0] === arrays[0])\n        ? baseIntersection(mapped, undefined, comparator)\n        : [];\n    });\n\n    /**\n     * Converts all elements in `array` into a string separated by `separator`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to convert.\n     * @param {string} [separator=','] The element separator.\n     * @returns {string} Returns the joined string.\n     * @example\n     *\n     * _.join(['a', 'b', 'c'], '~');\n     * // => 'a~b~c'\n     */\n    function join(array, separator) {\n      return array ? nativeJoin.call(array, separator) : '';\n    }\n\n    /**\n     * Gets the last element of `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @returns {*} Returns the last element of `array`.\n     * @example\n     *\n     * _.last([1, 2, 3]);\n     * // => 3\n     */\n    function last(array) {\n      var length = array ? array.length : 0;\n      return length ? array[length - 1] : undefined;\n    }\n\n    /**\n     * This method is like `_.indexOf` except that it iterates over elements of\n     * `array` from right to left.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to search.\n     * @param {*} value The value to search for.\n     * @param {number} [fromIndex=array.length-1] The index to search from.\n     * @returns {number} Returns the index of the matched value, else `-1`.\n     * @example\n     *\n     * _.lastIndexOf([1, 2, 1, 2], 2);\n     * // => 3\n     *\n     * // Search from the `fromIndex`.\n     * _.lastIndexOf([1, 2, 1, 2], 2, 2);\n     * // => 1\n     */\n    function lastIndexOf(array, value, fromIndex) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return -1;\n      }\n      var index = length;\n      if (fromIndex !== undefined) {\n        index = toInteger(fromIndex);\n        index = (\n          index < 0\n            ? nativeMax(length + index, 0)\n            : nativeMin(index, length - 1)\n        ) + 1;\n      }\n      if (value !== value) {\n        return indexOfNaN(array, index, true);\n      }\n      while (index--) {\n        if (array[index] === value) {\n          return index;\n        }\n      }\n      return -1;\n    }\n\n    /**\n     * Gets the nth element of `array`. If `n` is negative, the nth element\n     * from the end is returned.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.11.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {number} [n=0] The index of the element to return.\n     * @returns {*} Returns the nth element of `array`.\n     * @example\n     *\n     * var array = ['a', 'b', 'c', 'd'];\n     *\n     * _.nth(array, 1);\n     * // => 'b'\n     *\n     * _.nth(array, -2);\n     * // => 'c';\n     */\n    function nth(array, n) {\n      return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;\n    }\n\n    /**\n     * Removes all given values from `array` using\n     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons.\n     *\n     * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`\n     * to remove elements from an array by predicate.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @param {...*} [values] The values to remove.\n     * @returns {Array} Returns `array`.\n     * @example\n     *\n     * var array = [1, 2, 3, 1, 2, 3];\n     *\n     * _.pull(array, 2, 3);\n     * console.log(array);\n     * // => [1, 1]\n     */\n    var pull = rest(pullAll);\n\n    /**\n     * This method is like `_.pull` except that it accepts an array of values to remove.\n     *\n     * **Note:** Unlike `_.difference`, this method mutates `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @param {Array} values The values to remove.\n     * @returns {Array} Returns `array`.\n     * @example\n     *\n     * var array = [1, 2, 3, 1, 2, 3];\n     *\n     * _.pullAll(array, [2, 3]);\n     * console.log(array);\n     * // => [1, 1]\n     */\n    function pullAll(array, values) {\n      return (array && array.length && values && values.length)\n        ? basePullAll(array, values)\n        : array;\n    }\n\n    /**\n     * This method is like `_.pullAll` except that it accepts `iteratee` which is\n     * invoked for each element of `array` and `values` to generate the criterion\n     * by which they're compared. The iteratee is invoked with one argument: (value).\n     *\n     * **Note:** Unlike `_.differenceBy`, this method mutates `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @param {Array} values The values to remove.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Array} Returns `array`.\n     * @example\n     *\n     * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];\n     *\n     * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');\n     * console.log(array);\n     * // => [{ 'x': 2 }]\n     */\n    function pullAllBy(array, values, iteratee) {\n      return (array && array.length && values && values.length)\n        ? basePullAll(array, values, getIteratee(iteratee))\n        : array;\n    }\n\n    /**\n     * This method is like `_.pullAll` except that it accepts `comparator` which\n     * is invoked to compare elements of `array` to `values`. The comparator is\n     * invoked with two arguments: (arrVal, othVal).\n     *\n     * **Note:** Unlike `_.differenceWith`, this method mutates `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.6.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @param {Array} values The values to remove.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns `array`.\n     * @example\n     *\n     * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];\n     *\n     * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);\n     * console.log(array);\n     * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]\n     */\n    function pullAllWith(array, values, comparator) {\n      return (array && array.length && values && values.length)\n        ? basePullAll(array, values, undefined, comparator)\n        : array;\n    }\n\n    /**\n     * Removes elements from `array` corresponding to `indexes` and returns an\n     * array of removed elements.\n     *\n     * **Note:** Unlike `_.at`, this method mutates `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @param {...(number|number[])} [indexes] The indexes of elements to remove.\n     * @returns {Array} Returns the new array of removed elements.\n     * @example\n     *\n     * var array = [5, 10, 15, 20];\n     * var evens = _.pullAt(array, 1, 3);\n     *\n     * console.log(array);\n     * // => [5, 15]\n     *\n     * console.log(evens);\n     * // => [10, 20]\n     */\n    var pullAt = rest(function(array, indexes) {\n      indexes = arrayMap(baseFlatten(indexes, 1), String);\n\n      var result = baseAt(array, indexes);\n      basePullAt(array, indexes.sort(compareAscending));\n      return result;\n    });\n\n    /**\n     * Removes all elements from `array` that `predicate` returns truthy for\n     * and returns an array of the removed elements. The predicate is invoked\n     * with three arguments: (value, index, array).\n     *\n     * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`\n     * to pull elements from an array by value.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the new array of removed elements.\n     * @example\n     *\n     * var array = [1, 2, 3, 4];\n     * var evens = _.remove(array, function(n) {\n     *   return n % 2 == 0;\n     * });\n     *\n     * console.log(array);\n     * // => [1, 3]\n     *\n     * console.log(evens);\n     * // => [2, 4]\n     */\n    function remove(array, predicate) {\n      var result = [];\n      if (!(array && array.length)) {\n        return result;\n      }\n      var index = -1,\n          indexes = [],\n          length = array.length;\n\n      predicate = getIteratee(predicate, 3);\n      while (++index < length) {\n        var value = array[index];\n        if (predicate(value, index, array)) {\n          result.push(value);\n          indexes.push(index);\n        }\n      }\n      basePullAt(array, indexes);\n      return result;\n    }\n\n    /**\n     * Reverses `array` so that the first element becomes the last, the second\n     * element becomes the second to last, and so on.\n     *\n     * **Note:** This method mutates `array` and is based on\n     * [`Array#reverse`](https://mdn.io/Array/reverse).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to modify.\n     * @returns {Array} Returns `array`.\n     * @example\n     *\n     * var array = [1, 2, 3];\n     *\n     * _.reverse(array);\n     * // => [3, 2, 1]\n     *\n     * console.log(array);\n     * // => [3, 2, 1]\n     */\n    function reverse(array) {\n      return array ? nativeReverse.call(array) : array;\n    }\n\n    /**\n     * Creates a slice of `array` from `start` up to, but not including, `end`.\n     *\n     * **Note:** This method is used instead of\n     * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are\n     * returned.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to slice.\n     * @param {number} [start=0] The start position.\n     * @param {number} [end=array.length] The end position.\n     * @returns {Array} Returns the slice of `array`.\n     */\n    function slice(array, start, end) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return [];\n      }\n      if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {\n        start = 0;\n        end = length;\n      }\n      else {\n        start = start == null ? 0 : toInteger(start);\n        end = end === undefined ? length : toInteger(end);\n      }\n      return baseSlice(array, start, end);\n    }\n\n    /**\n     * Uses a binary search to determine the lowest index at which `value`\n     * should be inserted into `array` in order to maintain its sort order.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The sorted array to inspect.\n     * @param {*} value The value to evaluate.\n     * @returns {number} Returns the index at which `value` should be inserted\n     *  into `array`.\n     * @example\n     *\n     * _.sortedIndex([30, 50], 40);\n     * // => 1\n     *\n     * _.sortedIndex([4, 5], 4);\n     * // => 0\n     */\n    function sortedIndex(array, value) {\n      return baseSortedIndex(array, value);\n    }\n\n    /**\n     * This method is like `_.sortedIndex` except that it accepts `iteratee`\n     * which is invoked for `value` and each element of `array` to compute their\n     * sort ranking. The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The sorted array to inspect.\n     * @param {*} value The value to evaluate.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {number} Returns the index at which `value` should be inserted\n     *  into `array`.\n     * @example\n     *\n     * var dict = { 'thirty': 30, 'forty': 40, 'fifty': 50 };\n     *\n     * _.sortedIndexBy(['thirty', 'fifty'], 'forty', _.propertyOf(dict));\n     * // => 1\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.sortedIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');\n     * // => 0\n     */\n    function sortedIndexBy(array, value, iteratee) {\n      return baseSortedIndexBy(array, value, getIteratee(iteratee));\n    }\n\n    /**\n     * This method is like `_.indexOf` except that it performs a binary\n     * search on a sorted `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to search.\n     * @param {*} value The value to search for.\n     * @returns {number} Returns the index of the matched value, else `-1`.\n     * @example\n     *\n     * _.sortedIndexOf([1, 1, 2, 2], 2);\n     * // => 2\n     */\n    function sortedIndexOf(array, value) {\n      var length = array ? array.length : 0;\n      if (length) {\n        var index = baseSortedIndex(array, value);\n        if (index < length && eq(array[index], value)) {\n          return index;\n        }\n      }\n      return -1;\n    }\n\n    /**\n     * This method is like `_.sortedIndex` except that it returns the highest\n     * index at which `value` should be inserted into `array` in order to\n     * maintain its sort order.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The sorted array to inspect.\n     * @param {*} value The value to evaluate.\n     * @returns {number} Returns the index at which `value` should be inserted\n     *  into `array`.\n     * @example\n     *\n     * _.sortedLastIndex([4, 5], 4);\n     * // => 1\n     */\n    function sortedLastIndex(array, value) {\n      return baseSortedIndex(array, value, true);\n    }\n\n    /**\n     * This method is like `_.sortedLastIndex` except that it accepts `iteratee`\n     * which is invoked for `value` and each element of `array` to compute their\n     * sort ranking. The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The sorted array to inspect.\n     * @param {*} value The value to evaluate.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {number} Returns the index at which `value` should be inserted\n     *  into `array`.\n     * @example\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.sortedLastIndexBy([{ 'x': 4 }, { 'x': 5 }], { 'x': 4 }, 'x');\n     * // => 1\n     */\n    function sortedLastIndexBy(array, value, iteratee) {\n      return baseSortedIndexBy(array, value, getIteratee(iteratee), true);\n    }\n\n    /**\n     * This method is like `_.lastIndexOf` except that it performs a binary\n     * search on a sorted `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to search.\n     * @param {*} value The value to search for.\n     * @returns {number} Returns the index of the matched value, else `-1`.\n     * @example\n     *\n     * _.sortedLastIndexOf([1, 1, 2, 2], 2);\n     * // => 3\n     */\n    function sortedLastIndexOf(array, value) {\n      var length = array ? array.length : 0;\n      if (length) {\n        var index = baseSortedIndex(array, value, true) - 1;\n        if (eq(array[index], value)) {\n          return index;\n        }\n      }\n      return -1;\n    }\n\n    /**\n     * This method is like `_.uniq` except that it's designed and optimized\n     * for sorted arrays.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @returns {Array} Returns the new duplicate free array.\n     * @example\n     *\n     * _.sortedUniq([1, 1, 2]);\n     * // => [1, 2]\n     */\n    function sortedUniq(array) {\n      return (array && array.length)\n        ? baseSortedUniq(array)\n        : [];\n    }\n\n    /**\n     * This method is like `_.uniqBy` except that it's designed and optimized\n     * for sorted arrays.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @param {Function} [iteratee] The iteratee invoked per element.\n     * @returns {Array} Returns the new duplicate free array.\n     * @example\n     *\n     * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);\n     * // => [1.1, 2.3]\n     */\n    function sortedUniqBy(array, iteratee) {\n      return (array && array.length)\n        ? baseSortedUniqBy(array, getIteratee(iteratee))\n        : [];\n    }\n\n    /**\n     * Gets all but the first element of `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * _.tail([1, 2, 3]);\n     * // => [2, 3]\n     */\n    function tail(array) {\n      return drop(array, 1);\n    }\n\n    /**\n     * Creates a slice of `array` with `n` elements taken from the beginning.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {number} [n=1] The number of elements to take.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * _.take([1, 2, 3]);\n     * // => [1]\n     *\n     * _.take([1, 2, 3], 2);\n     * // => [1, 2]\n     *\n     * _.take([1, 2, 3], 5);\n     * // => [1, 2, 3]\n     *\n     * _.take([1, 2, 3], 0);\n     * // => []\n     */\n    function take(array, n, guard) {\n      if (!(array && array.length)) {\n        return [];\n      }\n      n = (guard || n === undefined) ? 1 : toInteger(n);\n      return baseSlice(array, 0, n < 0 ? 0 : n);\n    }\n\n    /**\n     * Creates a slice of `array` with `n` elements taken from the end.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {number} [n=1] The number of elements to take.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * _.takeRight([1, 2, 3]);\n     * // => [3]\n     *\n     * _.takeRight([1, 2, 3], 2);\n     * // => [2, 3]\n     *\n     * _.takeRight([1, 2, 3], 5);\n     * // => [1, 2, 3]\n     *\n     * _.takeRight([1, 2, 3], 0);\n     * // => []\n     */\n    function takeRight(array, n, guard) {\n      var length = array ? array.length : 0;\n      if (!length) {\n        return [];\n      }\n      n = (guard || n === undefined) ? 1 : toInteger(n);\n      n = length - n;\n      return baseSlice(array, n < 0 ? 0 : n, length);\n    }\n\n    /**\n     * Creates a slice of `array` with elements taken from the end. Elements are\n     * taken until `predicate` returns falsey. The predicate is invoked with\n     * three arguments: (value, index, array).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'active': true },\n     *   { 'user': 'fred',    'active': false },\n     *   { 'user': 'pebbles', 'active': false }\n     * ];\n     *\n     * _.takeRightWhile(users, function(o) { return !o.active; });\n     * // => objects for ['fred', 'pebbles']\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });\n     * // => objects for ['pebbles']\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.takeRightWhile(users, ['active', false]);\n     * // => objects for ['fred', 'pebbles']\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.takeRightWhile(users, 'active');\n     * // => []\n     */\n    function takeRightWhile(array, predicate) {\n      return (array && array.length)\n        ? baseWhile(array, getIteratee(predicate, 3), false, true)\n        : [];\n    }\n\n    /**\n     * Creates a slice of `array` with elements taken from the beginning. Elements\n     * are taken until `predicate` returns falsey. The predicate is invoked with\n     * three arguments: (value, index, array).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Array\n     * @param {Array} array The array to query.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the slice of `array`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'active': false },\n     *   { 'user': 'fred',    'active': false},\n     *   { 'user': 'pebbles', 'active': true }\n     * ];\n     *\n     * _.takeWhile(users, function(o) { return !o.active; });\n     * // => objects for ['barney', 'fred']\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.takeWhile(users, { 'user': 'barney', 'active': false });\n     * // => objects for ['barney']\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.takeWhile(users, ['active', false]);\n     * // => objects for ['barney', 'fred']\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.takeWhile(users, 'active');\n     * // => []\n     */\n    function takeWhile(array, predicate) {\n      return (array && array.length)\n        ? baseWhile(array, getIteratee(predicate, 3))\n        : [];\n    }\n\n    /**\n     * Creates an array of unique values, in order, from all given arrays using\n     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @returns {Array} Returns the new array of combined values.\n     * @example\n     *\n     * _.union([2, 1], [4, 2], [1, 2]);\n     * // => [2, 1, 4]\n     */\n    var union = rest(function(arrays) {\n      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));\n    });\n\n    /**\n     * This method is like `_.union` except that it accepts `iteratee` which is\n     * invoked for each element of each `arrays` to generate the criterion by\n     * which uniqueness is computed. The iteratee is invoked with one argument:\n     * (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Array} Returns the new array of combined values.\n     * @example\n     *\n     * _.unionBy([2.1, 1.2], [4.3, 2.4], Math.floor);\n     * // => [2.1, 1.2, 4.3]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n     * // => [{ 'x': 1 }, { 'x': 2 }]\n     */\n    var unionBy = rest(function(arrays) {\n      var iteratee = last(arrays);\n      if (isArrayLikeObject(iteratee)) {\n        iteratee = undefined;\n      }\n      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee));\n    });\n\n    /**\n     * This method is like `_.union` except that it accepts `comparator` which\n     * is invoked to compare elements of `arrays`. The comparator is invoked\n     * with two arguments: (arrVal, othVal).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of combined values.\n     * @example\n     *\n     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n     *\n     * _.unionWith(objects, others, _.isEqual);\n     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n     */\n    var unionWith = rest(function(arrays) {\n      var comparator = last(arrays);\n      if (isArrayLikeObject(comparator)) {\n        comparator = undefined;\n      }\n      return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);\n    });\n\n    /**\n     * Creates a duplicate-free version of an array, using\n     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons, in which only the first occurrence of each\n     * element is kept.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @returns {Array} Returns the new duplicate free array.\n     * @example\n     *\n     * _.uniq([2, 1, 2]);\n     * // => [2, 1]\n     */\n    function uniq(array) {\n      return (array && array.length)\n        ? baseUniq(array)\n        : [];\n    }\n\n    /**\n     * This method is like `_.uniq` except that it accepts `iteratee` which is\n     * invoked for each element in `array` to generate the criterion by which\n     * uniqueness is computed. The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Array} Returns the new duplicate free array.\n     * @example\n     *\n     * _.uniqBy([2.1, 1.2, 2.3], Math.floor);\n     * // => [2.1, 1.2]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');\n     * // => [{ 'x': 1 }, { 'x': 2 }]\n     */\n    function uniqBy(array, iteratee) {\n      return (array && array.length)\n        ? baseUniq(array, getIteratee(iteratee))\n        : [];\n    }\n\n    /**\n     * This method is like `_.uniq` except that it accepts `comparator` which\n     * is invoked to compare elements of `array`. The comparator is invoked with\n     * two arguments: (arrVal, othVal).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {Array} array The array to inspect.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new duplicate free array.\n     * @example\n     *\n     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 },  { 'x': 1, 'y': 2 }];\n     *\n     * _.uniqWith(objects, _.isEqual);\n     * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]\n     */\n    function uniqWith(array, comparator) {\n      return (array && array.length)\n        ? baseUniq(array, undefined, comparator)\n        : [];\n    }\n\n    /**\n     * This method is like `_.zip` except that it accepts an array of grouped\n     * elements and creates an array regrouping the elements to their pre-zip\n     * configuration.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.2.0\n     * @category Array\n     * @param {Array} array The array of grouped elements to process.\n     * @returns {Array} Returns the new array of regrouped elements.\n     * @example\n     *\n     * var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]);\n     * // => [['fred', 30, true], ['barney', 40, false]]\n     *\n     * _.unzip(zipped);\n     * // => [['fred', 'barney'], [30, 40], [true, false]]\n     */\n    function unzip(array) {\n      if (!(array && array.length)) {\n        return [];\n      }\n      var length = 0;\n      array = arrayFilter(array, function(group) {\n        if (isArrayLikeObject(group)) {\n          length = nativeMax(group.length, length);\n          return true;\n        }\n      });\n      return baseTimes(length, function(index) {\n        return arrayMap(array, baseProperty(index));\n      });\n    }\n\n    /**\n     * This method is like `_.unzip` except that it accepts `iteratee` to specify\n     * how regrouped values should be combined. The iteratee is invoked with the\n     * elements of each group: (...group).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.8.0\n     * @category Array\n     * @param {Array} array The array of grouped elements to process.\n     * @param {Function} [iteratee=_.identity] The function to combine\n     *  regrouped values.\n     * @returns {Array} Returns the new array of regrouped elements.\n     * @example\n     *\n     * var zipped = _.zip([1, 2], [10, 20], [100, 200]);\n     * // => [[1, 10, 100], [2, 20, 200]]\n     *\n     * _.unzipWith(zipped, _.add);\n     * // => [3, 30, 300]\n     */\n    function unzipWith(array, iteratee) {\n      if (!(array && array.length)) {\n        return [];\n      }\n      var result = unzip(array);\n      if (iteratee == null) {\n        return result;\n      }\n      return arrayMap(result, function(group) {\n        return apply(iteratee, undefined, group);\n      });\n    }\n\n    /**\n     * Creates an array excluding all given values using\n     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * for equality comparisons.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {Array} array The array to filter.\n     * @param {...*} [values] The values to exclude.\n     * @returns {Array} Returns the new array of filtered values.\n     * @example\n     *\n     * _.without([1, 2, 1, 3], 1, 2);\n     * // => [3]\n     */\n    var without = rest(function(array, values) {\n      return isArrayLikeObject(array)\n        ? baseDifference(array, values)\n        : [];\n    });\n\n    /**\n     * Creates an array of unique values that is the\n     * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)\n     * of the given arrays. The order of result values is determined by the order\n     * they occur in the arrays.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.4.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @returns {Array} Returns the new array of values.\n     * @example\n     *\n     * _.xor([2, 1], [4, 2]);\n     * // => [1, 4]\n     */\n    var xor = rest(function(arrays) {\n      return baseXor(arrayFilter(arrays, isArrayLikeObject));\n    });\n\n    /**\n     * This method is like `_.xor` except that it accepts `iteratee` which is\n     * invoked for each element of each `arrays` to generate the criterion by\n     * which by which they're compared. The iteratee is invoked with one argument:\n     * (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Array} Returns the new array of values.\n     * @example\n     *\n     * _.xorBy([2.1, 1.2], [4.3, 2.4], Math.floor);\n     * // => [1.2, 4.3]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');\n     * // => [{ 'x': 2 }]\n     */\n    var xorBy = rest(function(arrays) {\n      var iteratee = last(arrays);\n      if (isArrayLikeObject(iteratee)) {\n        iteratee = undefined;\n      }\n      return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee));\n    });\n\n    /**\n     * This method is like `_.xor` except that it accepts `comparator` which is\n     * invoked to compare elements of `arrays`. The comparator is invoked with\n     * two arguments: (arrVal, othVal).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to inspect.\n     * @param {Function} [comparator] The comparator invoked per element.\n     * @returns {Array} Returns the new array of values.\n     * @example\n     *\n     * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];\n     * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];\n     *\n     * _.xorWith(objects, others, _.isEqual);\n     * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]\n     */\n    var xorWith = rest(function(arrays) {\n      var comparator = last(arrays);\n      if (isArrayLikeObject(comparator)) {\n        comparator = undefined;\n      }\n      return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);\n    });\n\n    /**\n     * Creates an array of grouped elements, the first of which contains the\n     * first elements of the given arrays, the second of which contains the\n     * second elements of the given arrays, and so on.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to process.\n     * @returns {Array} Returns the new array of grouped elements.\n     * @example\n     *\n     * _.zip(['fred', 'barney'], [30, 40], [true, false]);\n     * // => [['fred', 30, true], ['barney', 40, false]]\n     */\n    var zip = rest(unzip);\n\n    /**\n     * This method is like `_.fromPairs` except that it accepts two arrays,\n     * one of property identifiers and one of corresponding values.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.4.0\n     * @category Array\n     * @param {Array} [props=[]] The property identifiers.\n     * @param {Array} [values=[]] The property values.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * _.zipObject(['a', 'b'], [1, 2]);\n     * // => { 'a': 1, 'b': 2 }\n     */\n    function zipObject(props, values) {\n      return baseZipObject(props || [], values || [], assignValue);\n    }\n\n    /**\n     * This method is like `_.zipObject` except that it supports property paths.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.1.0\n     * @category Array\n     * @param {Array} [props=[]] The property identifiers.\n     * @param {Array} [values=[]] The property values.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);\n     * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }\n     */\n    function zipObjectDeep(props, values) {\n      return baseZipObject(props || [], values || [], baseSet);\n    }\n\n    /**\n     * This method is like `_.zip` except that it accepts `iteratee` to specify\n     * how grouped values should be combined. The iteratee is invoked with the\n     * elements of each group: (...group).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.8.0\n     * @category Array\n     * @param {...Array} [arrays] The arrays to process.\n     * @param {Function} [iteratee=_.identity] The function to combine grouped values.\n     * @returns {Array} Returns the new array of grouped elements.\n     * @example\n     *\n     * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {\n     *   return a + b + c;\n     * });\n     * // => [111, 222]\n     */\n    var zipWith = rest(function(arrays) {\n      var length = arrays.length,\n          iteratee = length > 1 ? arrays[length - 1] : undefined;\n\n      iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;\n      return unzipWith(arrays, iteratee);\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates a `lodash` wrapper instance that wraps `value` with explicit method\n     * chain sequences enabled. The result of such sequences must be unwrapped\n     * with `_#value`.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.3.0\n     * @category Seq\n     * @param {*} value The value to wrap.\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'age': 36 },\n     *   { 'user': 'fred',    'age': 40 },\n     *   { 'user': 'pebbles', 'age': 1 }\n     * ];\n     *\n     * var youngest = _\n     *   .chain(users)\n     *   .sortBy('age')\n     *   .map(function(o) {\n     *     return o.user + ' is ' + o.age;\n     *   })\n     *   .head()\n     *   .value();\n     * // => 'pebbles is 1'\n     */\n    function chain(value) {\n      var result = lodash(value);\n      result.__chain__ = true;\n      return result;\n    }\n\n    /**\n     * This method invokes `interceptor` and returns `value`. The interceptor\n     * is invoked with one argument; (value). The purpose of this method is to\n     * \"tap into\" a method chain sequence in order to modify intermediate results.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Seq\n     * @param {*} value The value to provide to `interceptor`.\n     * @param {Function} interceptor The function to invoke.\n     * @returns {*} Returns `value`.\n     * @example\n     *\n     * _([1, 2, 3])\n     *  .tap(function(array) {\n     *    // Mutate input array.\n     *    array.pop();\n     *  })\n     *  .reverse()\n     *  .value();\n     * // => [2, 1]\n     */\n    function tap(value, interceptor) {\n      interceptor(value);\n      return value;\n    }\n\n    /**\n     * This method is like `_.tap` except that it returns the result of `interceptor`.\n     * The purpose of this method is to \"pass thru\" values replacing intermediate\n     * results in a method chain sequence.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Seq\n     * @param {*} value The value to provide to `interceptor`.\n     * @param {Function} interceptor The function to invoke.\n     * @returns {*} Returns the result of `interceptor`.\n     * @example\n     *\n     * _('  abc  ')\n     *  .chain()\n     *  .trim()\n     *  .thru(function(value) {\n     *    return [value];\n     *  })\n     *  .value();\n     * // => ['abc']\n     */\n    function thru(value, interceptor) {\n      return interceptor(value);\n    }\n\n    /**\n     * This method is the wrapper version of `_.at`.\n     *\n     * @name at\n     * @memberOf _\n     * @since 1.0.0\n     * @category Seq\n     * @param {...(string|string[])} [paths] The property paths of elements to pick.\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n     *\n     * _(object).at(['a[0].b.c', 'a[1]']).value();\n     * // => [3, 4]\n     *\n     * _(['a', 'b', 'c']).at(0, 2).value();\n     * // => ['a', 'c']\n     */\n    var wrapperAt = rest(function(paths) {\n      paths = baseFlatten(paths, 1);\n      var length = paths.length,\n          start = length ? paths[0] : 0,\n          value = this.__wrapped__,\n          interceptor = function(object) { return baseAt(object, paths); };\n\n      if (length > 1 || this.__actions__.length ||\n          !(value instanceof LazyWrapper) || !isIndex(start)) {\n        return this.thru(interceptor);\n      }\n      value = value.slice(start, +start + (length ? 1 : 0));\n      value.__actions__.push({\n        'func': thru,\n        'args': [interceptor],\n        'thisArg': undefined\n      });\n      return new LodashWrapper(value, this.__chain__).thru(function(array) {\n        if (length && !array.length) {\n          array.push(undefined);\n        }\n        return array;\n      });\n    });\n\n    /**\n     * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.\n     *\n     * @name chain\n     * @memberOf _\n     * @since 0.1.0\n     * @category Seq\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36 },\n     *   { 'user': 'fred',   'age': 40 }\n     * ];\n     *\n     * // A sequence without explicit chaining.\n     * _(users).head();\n     * // => { 'user': 'barney', 'age': 36 }\n     *\n     * // A sequence with explicit chaining.\n     * _(users)\n     *   .chain()\n     *   .head()\n     *   .pick('user')\n     *   .value();\n     * // => { 'user': 'barney' }\n     */\n    function wrapperChain() {\n      return chain(this);\n    }\n\n    /**\n     * Executes the chain sequence and returns the wrapped result.\n     *\n     * @name commit\n     * @memberOf _\n     * @since 3.2.0\n     * @category Seq\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * var array = [1, 2];\n     * var wrapped = _(array).push(3);\n     *\n     * console.log(array);\n     * // => [1, 2]\n     *\n     * wrapped = wrapped.commit();\n     * console.log(array);\n     * // => [1, 2, 3]\n     *\n     * wrapped.last();\n     * // => 3\n     *\n     * console.log(array);\n     * // => [1, 2, 3]\n     */\n    function wrapperCommit() {\n      return new LodashWrapper(this.value(), this.__chain__);\n    }\n\n    /**\n     * Gets the next value on a wrapped object following the\n     * [iterator protocol](https://mdn.io/iteration_protocols#iterator).\n     *\n     * @name next\n     * @memberOf _\n     * @since 4.0.0\n     * @category Seq\n     * @returns {Object} Returns the next iterator value.\n     * @example\n     *\n     * var wrapped = _([1, 2]);\n     *\n     * wrapped.next();\n     * // => { 'done': false, 'value': 1 }\n     *\n     * wrapped.next();\n     * // => { 'done': false, 'value': 2 }\n     *\n     * wrapped.next();\n     * // => { 'done': true, 'value': undefined }\n     */\n    function wrapperNext() {\n      if (this.__values__ === undefined) {\n        this.__values__ = toArray(this.value());\n      }\n      var done = this.__index__ >= this.__values__.length,\n          value = done ? undefined : this.__values__[this.__index__++];\n\n      return { 'done': done, 'value': value };\n    }\n\n    /**\n     * Enables the wrapper to be iterable.\n     *\n     * @name Symbol.iterator\n     * @memberOf _\n     * @since 4.0.0\n     * @category Seq\n     * @returns {Object} Returns the wrapper object.\n     * @example\n     *\n     * var wrapped = _([1, 2]);\n     *\n     * wrapped[Symbol.iterator]() === wrapped;\n     * // => true\n     *\n     * Array.from(wrapped);\n     * // => [1, 2]\n     */\n    function wrapperToIterator() {\n      return this;\n    }\n\n    /**\n     * Creates a clone of the chain sequence planting `value` as the wrapped value.\n     *\n     * @name plant\n     * @memberOf _\n     * @since 3.2.0\n     * @category Seq\n     * @param {*} value The value to plant.\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * function square(n) {\n     *   return n * n;\n     * }\n     *\n     * var wrapped = _([1, 2]).map(square);\n     * var other = wrapped.plant([3, 4]);\n     *\n     * other.value();\n     * // => [9, 16]\n     *\n     * wrapped.value();\n     * // => [1, 4]\n     */\n    function wrapperPlant(value) {\n      var result,\n          parent = this;\n\n      while (parent instanceof baseLodash) {\n        var clone = wrapperClone(parent);\n        clone.__index__ = 0;\n        clone.__values__ = undefined;\n        if (result) {\n          previous.__wrapped__ = clone;\n        } else {\n          result = clone;\n        }\n        var previous = clone;\n        parent = parent.__wrapped__;\n      }\n      previous.__wrapped__ = value;\n      return result;\n    }\n\n    /**\n     * This method is the wrapper version of `_.reverse`.\n     *\n     * **Note:** This method mutates the wrapped array.\n     *\n     * @name reverse\n     * @memberOf _\n     * @since 0.1.0\n     * @category Seq\n     * @returns {Object} Returns the new `lodash` wrapper instance.\n     * @example\n     *\n     * var array = [1, 2, 3];\n     *\n     * _(array).reverse().value()\n     * // => [3, 2, 1]\n     *\n     * console.log(array);\n     * // => [3, 2, 1]\n     */\n    function wrapperReverse() {\n      var value = this.__wrapped__;\n      if (value instanceof LazyWrapper) {\n        var wrapped = value;\n        if (this.__actions__.length) {\n          wrapped = new LazyWrapper(this);\n        }\n        wrapped = wrapped.reverse();\n        wrapped.__actions__.push({\n          'func': thru,\n          'args': [reverse],\n          'thisArg': undefined\n        });\n        return new LodashWrapper(wrapped, this.__chain__);\n      }\n      return this.thru(reverse);\n    }\n\n    /**\n     * Executes the chain sequence to resolve the unwrapped value.\n     *\n     * @name value\n     * @memberOf _\n     * @since 0.1.0\n     * @alias toJSON, valueOf\n     * @category Seq\n     * @returns {*} Returns the resolved unwrapped value.\n     * @example\n     *\n     * _([1, 2, 3]).value();\n     * // => [1, 2, 3]\n     */\n    function wrapperValue() {\n      return baseWrapperValue(this.__wrapped__, this.__actions__);\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Creates an object composed of keys generated from the results of running\n     * each element of `collection` thru `iteratee`. The corresponding value of\n     * each key is the number of times the key was returned by `iteratee`. The\n     * iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.5.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee to transform keys.\n     * @returns {Object} Returns the composed aggregate object.\n     * @example\n     *\n     * _.countBy([6.1, 4.2, 6.3], Math.floor);\n     * // => { '4': 1, '6': 2 }\n     *\n     * _.countBy(['one', 'two', 'three'], 'length');\n     * // => { '3': 2, '5': 1 }\n     */\n    var countBy = createAggregator(function(result, value, key) {\n      hasOwnProperty.call(result, key) ? ++result[key] : (result[key] = 1);\n    });\n\n    /**\n     * Checks if `predicate` returns truthy for **all** elements of `collection`.\n     * Iteration is stopped once `predicate` returns falsey. The predicate is\n     * invoked with three arguments: (value, index|key, collection).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {boolean} Returns `true` if all elements pass the predicate check,\n     *  else `false`.\n     * @example\n     *\n     * _.every([true, 1, null, 'yes'], Boolean);\n     * // => false\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36, 'active': false },\n     *   { 'user': 'fred',   'age': 40, 'active': false }\n     * ];\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.every(users, { 'user': 'barney', 'active': false });\n     * // => false\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.every(users, ['active', false]);\n     * // => true\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.every(users, 'active');\n     * // => false\n     */\n    function every(collection, predicate, guard) {\n      var func = isArray(collection) ? arrayEvery : baseEvery;\n      if (guard && isIterateeCall(collection, predicate, guard)) {\n        predicate = undefined;\n      }\n      return func(collection, getIteratee(predicate, 3));\n    }\n\n    /**\n     * Iterates over elements of `collection`, returning an array of all elements\n     * `predicate` returns truthy for. The predicate is invoked with three\n     * arguments: (value, index|key, collection).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the new filtered array.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36, 'active': true },\n     *   { 'user': 'fred',   'age': 40, 'active': false }\n     * ];\n     *\n     * _.filter(users, function(o) { return !o.active; });\n     * // => objects for ['fred']\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.filter(users, { 'age': 36, 'active': true });\n     * // => objects for ['barney']\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.filter(users, ['active', false]);\n     * // => objects for ['fred']\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.filter(users, 'active');\n     * // => objects for ['barney']\n     */\n    function filter(collection, predicate) {\n      var func = isArray(collection) ? arrayFilter : baseFilter;\n      return func(collection, getIteratee(predicate, 3));\n    }\n\n    /**\n     * Iterates over elements of `collection`, returning the first element\n     * `predicate` returns truthy for. The predicate is invoked with three\n     * arguments: (value, index|key, collection).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to search.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {*} Returns the matched element, else `undefined`.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'age': 36, 'active': true },\n     *   { 'user': 'fred',    'age': 40, 'active': false },\n     *   { 'user': 'pebbles', 'age': 1,  'active': true }\n     * ];\n     *\n     * _.find(users, function(o) { return o.age < 40; });\n     * // => object for 'barney'\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.find(users, { 'age': 1, 'active': true });\n     * // => object for 'pebbles'\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.find(users, ['active', false]);\n     * // => object for 'fred'\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.find(users, 'active');\n     * // => object for 'barney'\n     */\n    function find(collection, predicate) {\n      predicate = getIteratee(predicate, 3);\n      if (isArray(collection)) {\n        var index = baseFindIndex(collection, predicate);\n        return index > -1 ? collection[index] : undefined;\n      }\n      return baseFind(collection, predicate, baseEach);\n    }\n\n    /**\n     * This method is like `_.find` except that it iterates over elements of\n     * `collection` from right to left.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to search.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {*} Returns the matched element, else `undefined`.\n     * @example\n     *\n     * _.findLast([1, 2, 3, 4], function(n) {\n     *   return n % 2 == 1;\n     * });\n     * // => 3\n     */\n    function findLast(collection, predicate) {\n      predicate = getIteratee(predicate, 3);\n      if (isArray(collection)) {\n        var index = baseFindIndex(collection, predicate, true);\n        return index > -1 ? collection[index] : undefined;\n      }\n      return baseFind(collection, predicate, baseEachRight);\n    }\n\n    /**\n     * Creates a flattened array of values by running each element in `collection`\n     * thru `iteratee` and flattening the mapped results. The iteratee is invoked\n     * with three arguments: (value, index|key, collection).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the new flattened array.\n     * @example\n     *\n     * function duplicate(n) {\n     *   return [n, n];\n     * }\n     *\n     * _.flatMap([1, 2], duplicate);\n     * // => [1, 1, 2, 2]\n     */\n    function flatMap(collection, iteratee) {\n      return baseFlatten(map(collection, iteratee), 1);\n    }\n\n    /**\n     * This method is like `_.flatMap` except that it recursively flattens the\n     * mapped results.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.7.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the new flattened array.\n     * @example\n     *\n     * function duplicate(n) {\n     *   return [[[n, n]]];\n     * }\n     *\n     * _.flatMapDeep([1, 2], duplicate);\n     * // => [1, 1, 2, 2]\n     */\n    function flatMapDeep(collection, iteratee) {\n      return baseFlatten(map(collection, iteratee), INFINITY);\n    }\n\n    /**\n     * This method is like `_.flatMap` except that it recursively flattens the\n     * mapped results up to `depth` times.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.7.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The function invoked per iteration.\n     * @param {number} [depth=1] The maximum recursion depth.\n     * @returns {Array} Returns the new flattened array.\n     * @example\n     *\n     * function duplicate(n) {\n     *   return [[[n, n]]];\n     * }\n     *\n     * _.flatMapDepth([1, 2], duplicate, 2);\n     * // => [[1, 1], [2, 2]]\n     */\n    function flatMapDepth(collection, iteratee, depth) {\n      depth = depth === undefined ? 1 : toInteger(depth);\n      return baseFlatten(map(collection, iteratee), depth);\n    }\n\n    /**\n     * Iterates over elements of `collection` and invokes `iteratee` for each element.\n     * The iteratee is invoked with three arguments: (value, index|key, collection).\n     * Iteratee functions may exit iteration early by explicitly returning `false`.\n     *\n     * **Note:** As with other \"Collections\" methods, objects with a \"length\"\n     * property are iterated like arrays. To avoid this behavior use `_.forIn`\n     * or `_.forOwn` for object iteration.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @alias each\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Array|Object} Returns `collection`.\n     * @example\n     *\n     * _([1, 2]).forEach(function(value) {\n     *   console.log(value);\n     * });\n     * // => Logs `1` then `2`.\n     *\n     * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {\n     *   console.log(key);\n     * });\n     * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n     */\n    function forEach(collection, iteratee) {\n      return (typeof iteratee == 'function' && isArray(collection))\n        ? arrayEach(collection, iteratee)\n        : baseEach(collection, getIteratee(iteratee));\n    }\n\n    /**\n     * This method is like `_.forEach` except that it iterates over elements of\n     * `collection` from right to left.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @alias eachRight\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Array|Object} Returns `collection`.\n     * @example\n     *\n     * _.forEachRight([1, 2], function(value) {\n     *   console.log(value);\n     * });\n     * // => Logs `2` then `1`.\n     */\n    function forEachRight(collection, iteratee) {\n      return (typeof iteratee == 'function' && isArray(collection))\n        ? arrayEachRight(collection, iteratee)\n        : baseEachRight(collection, getIteratee(iteratee));\n    }\n\n    /**\n     * Creates an object composed of keys generated from the results of running\n     * each element of `collection` thru `iteratee`. The order of grouped values\n     * is determined by the order they occur in `collection`. The corresponding\n     * value of each key is an array of elements responsible for generating the\n     * key. The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee to transform keys.\n     * @returns {Object} Returns the composed aggregate object.\n     * @example\n     *\n     * _.groupBy([6.1, 4.2, 6.3], Math.floor);\n     * // => { '4': [4.2], '6': [6.1, 6.3] }\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.groupBy(['one', 'two', 'three'], 'length');\n     * // => { '3': ['one', 'two'], '5': ['three'] }\n     */\n    var groupBy = createAggregator(function(result, value, key) {\n      if (hasOwnProperty.call(result, key)) {\n        result[key].push(value);\n      } else {\n        result[key] = [value];\n      }\n    });\n\n    /**\n     * Checks if `value` is in `collection`. If `collection` is a string, it's\n     * checked for a substring of `value`, otherwise\n     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * is used for equality comparisons. If `fromIndex` is negative, it's used as\n     * the offset from the end of `collection`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object|string} collection The collection to search.\n     * @param {*} value The value to search for.\n     * @param {number} [fromIndex=0] The index to search from.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n     * @returns {boolean} Returns `true` if `value` is found, else `false`.\n     * @example\n     *\n     * _.includes([1, 2, 3], 1);\n     * // => true\n     *\n     * _.includes([1, 2, 3], 1, 2);\n     * // => false\n     *\n     * _.includes({ 'user': 'fred', 'age': 40 }, 'fred');\n     * // => true\n     *\n     * _.includes('pebbles', 'eb');\n     * // => true\n     */\n    function includes(collection, value, fromIndex, guard) {\n      collection = isArrayLike(collection) ? collection : values(collection);\n      fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;\n\n      var length = collection.length;\n      if (fromIndex < 0) {\n        fromIndex = nativeMax(length + fromIndex, 0);\n      }\n      return isString(collection)\n        ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)\n        : (!!length && baseIndexOf(collection, value, fromIndex) > -1);\n    }\n\n    /**\n     * Invokes the method at `path` of each element in `collection`, returning\n     * an array of the results of each invoked method. Any additional arguments\n     * are provided to each invoked method. If `methodName` is a function, it's\n     * invoked for and `this` bound to, each element in `collection`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|string} path The path of the method to invoke or\n     *  the function invoked per iteration.\n     * @param {...*} [args] The arguments to invoke each method with.\n     * @returns {Array} Returns the array of results.\n     * @example\n     *\n     * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');\n     * // => [[1, 5, 7], [1, 2, 3]]\n     *\n     * _.invokeMap([123, 456], String.prototype.split, '');\n     * // => [['1', '2', '3'], ['4', '5', '6']]\n     */\n    var invokeMap = rest(function(collection, path, args) {\n      var index = -1,\n          isFunc = typeof path == 'function',\n          isProp = isKey(path),\n          result = isArrayLike(collection) ? Array(collection.length) : [];\n\n      baseEach(collection, function(value) {\n        var func = isFunc ? path : ((isProp && value != null) ? value[path] : undefined);\n        result[++index] = func ? apply(func, value, args) : baseInvoke(value, path, args);\n      });\n      return result;\n    });\n\n    /**\n     * Creates an object composed of keys generated from the results of running\n     * each element of `collection` thru `iteratee`. The corresponding value of\n     * each key is the last element responsible for generating the key. The\n     * iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee to transform keys.\n     * @returns {Object} Returns the composed aggregate object.\n     * @example\n     *\n     * var array = [\n     *   { 'dir': 'left', 'code': 97 },\n     *   { 'dir': 'right', 'code': 100 }\n     * ];\n     *\n     * _.keyBy(array, function(o) {\n     *   return String.fromCharCode(o.code);\n     * });\n     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }\n     *\n     * _.keyBy(array, 'dir');\n     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }\n     */\n    var keyBy = createAggregator(function(result, value, key) {\n      result[key] = value;\n    });\n\n    /**\n     * Creates an array of values by running each element in `collection` thru\n     * `iteratee`. The iteratee is invoked with three arguments:\n     * (value, index|key, collection).\n     *\n     * Many lodash methods are guarded to work as iteratees for methods like\n     * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.\n     *\n     * The guarded methods are:\n     * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,\n     * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,\n     * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,\n     * `template`, `trim`, `trimEnd`, `trimStart`, and `words`\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the new mapped array.\n     * @example\n     *\n     * function square(n) {\n     *   return n * n;\n     * }\n     *\n     * _.map([4, 8], square);\n     * // => [16, 64]\n     *\n     * _.map({ 'a': 4, 'b': 8 }, square);\n     * // => [16, 64] (iteration order is not guaranteed)\n     *\n     * var users = [\n     *   { 'user': 'barney' },\n     *   { 'user': 'fred' }\n     * ];\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.map(users, 'user');\n     * // => ['barney', 'fred']\n     */\n    function map(collection, iteratee) {\n      var func = isArray(collection) ? arrayMap : baseMap;\n      return func(collection, getIteratee(iteratee, 3));\n    }\n\n    /**\n     * This method is like `_.sortBy` except that it allows specifying the sort\n     * orders of the iteratees to sort by. If `orders` is unspecified, all values\n     * are sorted in ascending order. Otherwise, specify an order of \"desc\" for\n     * descending or \"asc\" for ascending sort order of corresponding values.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]\n     *  The iteratees to sort by.\n     * @param {string[]} [orders] The sort orders of `iteratees`.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.\n     * @returns {Array} Returns the new sorted array.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'fred',   'age': 48 },\n     *   { 'user': 'barney', 'age': 34 },\n     *   { 'user': 'fred',   'age': 40 },\n     *   { 'user': 'barney', 'age': 36 }\n     * ];\n     *\n     * // Sort by `user` in ascending order and by `age` in descending order.\n     * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);\n     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n     */\n    function orderBy(collection, iteratees, orders, guard) {\n      if (collection == null) {\n        return [];\n      }\n      if (!isArray(iteratees)) {\n        iteratees = iteratees == null ? [] : [iteratees];\n      }\n      orders = guard ? undefined : orders;\n      if (!isArray(orders)) {\n        orders = orders == null ? [] : [orders];\n      }\n      return baseOrderBy(collection, iteratees, orders);\n    }\n\n    /**\n     * Creates an array of elements split into two groups, the first of which\n     * contains elements `predicate` returns truthy for, the second of which\n     * contains elements `predicate` returns falsey for. The predicate is\n     * invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the array of grouped elements.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney',  'age': 36, 'active': false },\n     *   { 'user': 'fred',    'age': 40, 'active': true },\n     *   { 'user': 'pebbles', 'age': 1,  'active': false }\n     * ];\n     *\n     * _.partition(users, function(o) { return o.active; });\n     * // => objects for [['fred'], ['barney', 'pebbles']]\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.partition(users, { 'age': 1, 'active': false });\n     * // => objects for [['pebbles'], ['barney', 'fred']]\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.partition(users, ['active', false]);\n     * // => objects for [['barney', 'pebbles'], ['fred']]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.partition(users, 'active');\n     * // => objects for [['fred'], ['barney', 'pebbles']]\n     */\n    var partition = createAggregator(function(result, value, key) {\n      result[key ? 0 : 1].push(value);\n    }, function() { return [[], []]; });\n\n    /**\n     * Reduces `collection` to a value which is the accumulated result of running\n     * each element in `collection` thru `iteratee`, where each successive\n     * invocation is supplied the return value of the previous. If `accumulator`\n     * is not given, the first element of `collection` is used as the initial\n     * value. The iteratee is invoked with four arguments:\n     * (accumulator, value, index|key, collection).\n     *\n     * Many lodash methods are guarded to work as iteratees for methods like\n     * `_.reduce`, `_.reduceRight`, and `_.transform`.\n     *\n     * The guarded methods are:\n     * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,\n     * and `sortBy`\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @param {*} [accumulator] The initial value.\n     * @returns {*} Returns the accumulated value.\n     * @example\n     *\n     * _.reduce([1, 2], function(sum, n) {\n     *   return sum + n;\n     * }, 0);\n     * // => 3\n     *\n     * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n     *   (result[value] || (result[value] = [])).push(key);\n     *   return result;\n     * }, {});\n     * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)\n     */\n    function reduce(collection, iteratee, accumulator) {\n      var func = isArray(collection) ? arrayReduce : baseReduce,\n          initAccum = arguments.length < 3;\n\n      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);\n    }\n\n    /**\n     * This method is like `_.reduce` except that it iterates over elements of\n     * `collection` from right to left.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @param {*} [accumulator] The initial value.\n     * @returns {*} Returns the accumulated value.\n     * @example\n     *\n     * var array = [[0, 1], [2, 3], [4, 5]];\n     *\n     * _.reduceRight(array, function(flattened, other) {\n     *   return flattened.concat(other);\n     * }, []);\n     * // => [4, 5, 2, 3, 0, 1]\n     */\n    function reduceRight(collection, iteratee, accumulator) {\n      var func = isArray(collection) ? arrayReduceRight : baseReduce,\n          initAccum = arguments.length < 3;\n\n      return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);\n    }\n\n    /**\n     * The opposite of `_.filter`; this method returns the elements of `collection`\n     * that `predicate` does **not** return truthy for.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Array} Returns the new filtered array.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36, 'active': false },\n     *   { 'user': 'fred',   'age': 40, 'active': true }\n     * ];\n     *\n     * _.reject(users, function(o) { return !o.active; });\n     * // => objects for ['fred']\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.reject(users, { 'age': 40, 'active': true });\n     * // => objects for ['barney']\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.reject(users, ['active', false]);\n     * // => objects for ['fred']\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.reject(users, 'active');\n     * // => objects for ['barney']\n     */\n    function reject(collection, predicate) {\n      var func = isArray(collection) ? arrayFilter : baseFilter;\n      predicate = getIteratee(predicate, 3);\n      return func(collection, function(value, index, collection) {\n        return !predicate(value, index, collection);\n      });\n    }\n\n    /**\n     * Gets a random element from `collection`.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to sample.\n     * @returns {*} Returns the random element.\n     * @example\n     *\n     * _.sample([1, 2, 3, 4]);\n     * // => 2\n     */\n    function sample(collection) {\n      var array = isArrayLike(collection) ? collection : values(collection),\n          length = array.length;\n\n      return length > 0 ? array[baseRandom(0, length - 1)] : undefined;\n    }\n\n    /**\n     * Gets `n` random elements at unique keys from `collection` up to the\n     * size of `collection`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to sample.\n     * @param {number} [n=1] The number of elements to sample.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the random elements.\n     * @example\n     *\n     * _.sampleSize([1, 2, 3], 2);\n     * // => [3, 1]\n     *\n     * _.sampleSize([1, 2, 3], 4);\n     * // => [2, 3, 1]\n     */\n    function sampleSize(collection, n, guard) {\n      var index = -1,\n          result = toArray(collection),\n          length = result.length,\n          lastIndex = length - 1;\n\n      if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {\n        n = 1;\n      } else {\n        n = baseClamp(toInteger(n), 0, length);\n      }\n      while (++index < n) {\n        var rand = baseRandom(index, lastIndex),\n            value = result[rand];\n\n        result[rand] = result[index];\n        result[index] = value;\n      }\n      result.length = n;\n      return result;\n    }\n\n    /**\n     * Creates an array of shuffled values, using a version of the\n     * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to shuffle.\n     * @returns {Array} Returns the new shuffled array.\n     * @example\n     *\n     * _.shuffle([1, 2, 3, 4]);\n     * // => [4, 1, 3, 2]\n     */\n    function shuffle(collection) {\n      return sampleSize(collection, MAX_ARRAY_LENGTH);\n    }\n\n    /**\n     * Gets the size of `collection` by returning its length for array-like\n     * values or the number of own enumerable string keyed properties for objects.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to inspect.\n     * @returns {number} Returns the collection size.\n     * @example\n     *\n     * _.size([1, 2, 3]);\n     * // => 3\n     *\n     * _.size({ 'a': 1, 'b': 2 });\n     * // => 2\n     *\n     * _.size('pebbles');\n     * // => 7\n     */\n    function size(collection) {\n      if (collection == null) {\n        return 0;\n      }\n      if (isArrayLike(collection)) {\n        var result = collection.length;\n        return (result && isString(collection)) ? stringSize(collection) : result;\n      }\n      if (isObjectLike(collection)) {\n        var tag = getTag(collection);\n        if (tag == mapTag || tag == setTag) {\n          return collection.size;\n        }\n      }\n      return keys(collection).length;\n    }\n\n    /**\n     * Checks if `predicate` returns truthy for **any** element of `collection`.\n     * Iteration is stopped once `predicate` returns truthy. The predicate is\n     * invoked with three arguments: (value, index|key, collection).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {boolean} Returns `true` if any element passes the predicate check,\n     *  else `false`.\n     * @example\n     *\n     * _.some([null, 0, 'yes', false], Boolean);\n     * // => true\n     *\n     * var users = [\n     *   { 'user': 'barney', 'active': true },\n     *   { 'user': 'fred',   'active': false }\n     * ];\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.some(users, { 'user': 'barney', 'active': false });\n     * // => false\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.some(users, ['active', false]);\n     * // => true\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.some(users, 'active');\n     * // => true\n     */\n    function some(collection, predicate, guard) {\n      var func = isArray(collection) ? arraySome : baseSome;\n      if (guard && isIterateeCall(collection, predicate, guard)) {\n        predicate = undefined;\n      }\n      return func(collection, getIteratee(predicate, 3));\n    }\n\n    /**\n     * Creates an array of elements, sorted in ascending order by the results of\n     * running each element in a collection thru each iteratee. This method\n     * performs a stable sort, that is, it preserves the original sort order of\n     * equal elements. The iteratees are invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Collection\n     * @param {Array|Object} collection The collection to iterate over.\n     * @param {...(Array|Array[]|Function|Function[]|Object|Object[]|string|string[])}\n     *  [iteratees=[_.identity]] The iteratees to sort by.\n     * @returns {Array} Returns the new sorted array.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'fred',   'age': 48 },\n     *   { 'user': 'barney', 'age': 36 },\n     *   { 'user': 'fred',   'age': 40 },\n     *   { 'user': 'barney', 'age': 34 }\n     * ];\n     *\n     * _.sortBy(users, function(o) { return o.user; });\n     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n     *\n     * _.sortBy(users, ['user', 'age']);\n     * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]\n     *\n     * _.sortBy(users, 'user', function(o) {\n     *   return Math.floor(o.age / 10);\n     * });\n     * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]\n     */\n    var sortBy = rest(function(collection, iteratees) {\n      if (collection == null) {\n        return [];\n      }\n      var length = iteratees.length;\n      if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {\n        iteratees = [];\n      } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {\n        iteratees = [iteratees[0]];\n      }\n      iteratees = (iteratees.length == 1 && isArray(iteratees[0]))\n        ? iteratees[0]\n        : baseFlatten(iteratees, 1, isFlattenableIteratee);\n\n      return baseOrderBy(collection, iteratees, []);\n    });\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Gets the timestamp of the number of milliseconds that have elapsed since\n     * the Unix epoch (1 January 1970 00:00:00 UTC).\n     *\n     * @static\n     * @memberOf _\n     * @since 2.4.0\n     * @type {Function}\n     * @category Date\n     * @returns {number} Returns the timestamp.\n     * @example\n     *\n     * _.defer(function(stamp) {\n     *   console.log(_.now() - stamp);\n     * }, _.now());\n     * // => Logs the number of milliseconds it took for the deferred function to be invoked.\n     */\n    var now = Date.now;\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * The opposite of `_.before`; this method creates a function that invokes\n     * `func` once it's called `n` or more times.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {number} n The number of calls before `func` is invoked.\n     * @param {Function} func The function to restrict.\n     * @returns {Function} Returns the new restricted function.\n     * @example\n     *\n     * var saves = ['profile', 'settings'];\n     *\n     * var done = _.after(saves.length, function() {\n     *   console.log('done saving!');\n     * });\n     *\n     * _.forEach(saves, function(type) {\n     *   asyncSave({ 'type': type, 'complete': done });\n     * });\n     * // => Logs 'done saving!' after the two async saves have completed.\n     */\n    function after(n, func) {\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      n = toInteger(n);\n      return function() {\n        if (--n < 1) {\n          return func.apply(this, arguments);\n        }\n      };\n    }\n\n    /**\n     * Creates a function that invokes `func`, with up to `n` arguments,\n     * ignoring any additional arguments.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Function\n     * @param {Function} func The function to cap arguments for.\n     * @param {number} [n=func.length] The arity cap.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * _.map(['6', '8', '10'], _.ary(parseInt, 1));\n     * // => [6, 8, 10]\n     */\n    function ary(func, n, guard) {\n      n = guard ? undefined : n;\n      n = (func && n == null) ? func.length : n;\n      return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n);\n    }\n\n    /**\n     * Creates a function that invokes `func`, with the `this` binding and arguments\n     * of the created function, while it's called less than `n` times. Subsequent\n     * calls to the created function return the result of the last `func` invocation.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Function\n     * @param {number} n The number of calls at which `func` is no longer invoked.\n     * @param {Function} func The function to restrict.\n     * @returns {Function} Returns the new restricted function.\n     * @example\n     *\n     * jQuery(element).on('click', _.before(5, addContactToList));\n     * // => allows adding up to 4 contacts to the list\n     */\n    function before(n, func) {\n      var result;\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      n = toInteger(n);\n      return function() {\n        if (--n > 0) {\n          result = func.apply(this, arguments);\n        }\n        if (n <= 1) {\n          func = undefined;\n        }\n        return result;\n      };\n    }\n\n    /**\n     * Creates a function that invokes `func` with the `this` binding of `thisArg`\n     * and `partials` prepended to the arguments it receives.\n     *\n     * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,\n     * may be used as a placeholder for partially applied arguments.\n     *\n     * **Note:** Unlike native `Function#bind` this method doesn't set the \"length\"\n     * property of bound functions.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to bind.\n     * @param {*} thisArg The `this` binding of `func`.\n     * @param {...*} [partials] The arguments to be partially applied.\n     * @returns {Function} Returns the new bound function.\n     * @example\n     *\n     * var greet = function(greeting, punctuation) {\n     *   return greeting + ' ' + this.user + punctuation;\n     * };\n     *\n     * var object = { 'user': 'fred' };\n     *\n     * var bound = _.bind(greet, object, 'hi');\n     * bound('!');\n     * // => 'hi fred!'\n     *\n     * // Bound with placeholders.\n     * var bound = _.bind(greet, object, _, '!');\n     * bound('hi');\n     * // => 'hi fred!'\n     */\n    var bind = rest(function(func, thisArg, partials) {\n      var bitmask = BIND_FLAG;\n      if (partials.length) {\n        var holders = replaceHolders(partials, getPlaceholder(bind));\n        bitmask |= PARTIAL_FLAG;\n      }\n      return createWrapper(func, bitmask, thisArg, partials, holders);\n    });\n\n    /**\n     * Creates a function that invokes the method at `object[key]` with `partials`\n     * prepended to the arguments it receives.\n     *\n     * This method differs from `_.bind` by allowing bound functions to reference\n     * methods that may be redefined or don't yet exist. See\n     * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)\n     * for more details.\n     *\n     * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic\n     * builds, may be used as a placeholder for partially applied arguments.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.10.0\n     * @category Function\n     * @param {Object} object The object to invoke the method on.\n     * @param {string} key The key of the method.\n     * @param {...*} [partials] The arguments to be partially applied.\n     * @returns {Function} Returns the new bound function.\n     * @example\n     *\n     * var object = {\n     *   'user': 'fred',\n     *   'greet': function(greeting, punctuation) {\n     *     return greeting + ' ' + this.user + punctuation;\n     *   }\n     * };\n     *\n     * var bound = _.bindKey(object, 'greet', 'hi');\n     * bound('!');\n     * // => 'hi fred!'\n     *\n     * object.greet = function(greeting, punctuation) {\n     *   return greeting + 'ya ' + this.user + punctuation;\n     * };\n     *\n     * bound('!');\n     * // => 'hiya fred!'\n     *\n     * // Bound with placeholders.\n     * var bound = _.bindKey(object, 'greet', _, '!');\n     * bound('hi');\n     * // => 'hiya fred!'\n     */\n    var bindKey = rest(function(object, key, partials) {\n      var bitmask = BIND_FLAG | BIND_KEY_FLAG;\n      if (partials.length) {\n        var holders = replaceHolders(partials, getPlaceholder(bindKey));\n        bitmask |= PARTIAL_FLAG;\n      }\n      return createWrapper(key, bitmask, object, partials, holders);\n    });\n\n    /**\n     * Creates a function that accepts arguments of `func` and either invokes\n     * `func` returning its result, if at least `arity` number of arguments have\n     * been provided, or returns a function that accepts the remaining `func`\n     * arguments, and so on. The arity of `func` may be specified if `func.length`\n     * is not sufficient.\n     *\n     * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,\n     * may be used as a placeholder for provided arguments.\n     *\n     * **Note:** This method doesn't set the \"length\" property of curried functions.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Function\n     * @param {Function} func The function to curry.\n     * @param {number} [arity=func.length] The arity of `func`.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Function} Returns the new curried function.\n     * @example\n     *\n     * var abc = function(a, b, c) {\n     *   return [a, b, c];\n     * };\n     *\n     * var curried = _.curry(abc);\n     *\n     * curried(1)(2)(3);\n     * // => [1, 2, 3]\n     *\n     * curried(1, 2)(3);\n     * // => [1, 2, 3]\n     *\n     * curried(1, 2, 3);\n     * // => [1, 2, 3]\n     *\n     * // Curried with placeholders.\n     * curried(1)(_, 3)(2);\n     * // => [1, 2, 3]\n     */\n    function curry(func, arity, guard) {\n      arity = guard ? undefined : arity;\n      var result = createWrapper(func, CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n      result.placeholder = curry.placeholder;\n      return result;\n    }\n\n    /**\n     * This method is like `_.curry` except that arguments are applied to `func`\n     * in the manner of `_.partialRight` instead of `_.partial`.\n     *\n     * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic\n     * builds, may be used as a placeholder for provided arguments.\n     *\n     * **Note:** This method doesn't set the \"length\" property of curried functions.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Function\n     * @param {Function} func The function to curry.\n     * @param {number} [arity=func.length] The arity of `func`.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Function} Returns the new curried function.\n     * @example\n     *\n     * var abc = function(a, b, c) {\n     *   return [a, b, c];\n     * };\n     *\n     * var curried = _.curryRight(abc);\n     *\n     * curried(3)(2)(1);\n     * // => [1, 2, 3]\n     *\n     * curried(2, 3)(1);\n     * // => [1, 2, 3]\n     *\n     * curried(1, 2, 3);\n     * // => [1, 2, 3]\n     *\n     * // Curried with placeholders.\n     * curried(3)(1, _)(2);\n     * // => [1, 2, 3]\n     */\n    function curryRight(func, arity, guard) {\n      arity = guard ? undefined : arity;\n      var result = createWrapper(func, CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);\n      result.placeholder = curryRight.placeholder;\n      return result;\n    }\n\n    /**\n     * Creates a debounced function that delays invoking `func` until after `wait`\n     * milliseconds have elapsed since the last time the debounced function was\n     * invoked. The debounced function comes with a `cancel` method to cancel\n     * delayed `func` invocations and a `flush` method to immediately invoke them.\n     * Provide an options object to indicate whether `func` should be invoked on\n     * the leading and/or trailing edge of the `wait` timeout. The `func` is invoked\n     * with the last arguments provided to the debounced function. Subsequent calls\n     * to the debounced function return the result of the last `func` invocation.\n     *\n     * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked\n     * on the trailing edge of the timeout only if the debounced function is\n     * invoked more than once during the `wait` timeout.\n     *\n     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n     * for details over the differences between `_.debounce` and `_.throttle`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to debounce.\n     * @param {number} [wait=0] The number of milliseconds to delay.\n     * @param {Object} [options={}] The options object.\n     * @param {boolean} [options.leading=false]\n     *  Specify invoking on the leading edge of the timeout.\n     * @param {number} [options.maxWait]\n     *  The maximum time `func` is allowed to be delayed before it's invoked.\n     * @param {boolean} [options.trailing=true]\n     *  Specify invoking on the trailing edge of the timeout.\n     * @returns {Function} Returns the new debounced function.\n     * @example\n     *\n     * // Avoid costly calculations while the window size is in flux.\n     * jQuery(window).on('resize', _.debounce(calculateLayout, 150));\n     *\n     * // Invoke `sendMail` when clicked, debouncing subsequent calls.\n     * jQuery(element).on('click', _.debounce(sendMail, 300, {\n     *   'leading': true,\n     *   'trailing': false\n     * }));\n     *\n     * // Ensure `batchLog` is invoked once after 1 second of debounced calls.\n     * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });\n     * var source = new EventSource('/stream');\n     * jQuery(source).on('message', debounced);\n     *\n     * // Cancel the trailing debounced invocation.\n     * jQuery(window).on('popstate', debounced.cancel);\n     */\n    function debounce(func, wait, options) {\n      var lastArgs,\n          lastThis,\n          maxWait,\n          result,\n          timerId,\n          lastCallTime = 0,\n          lastInvokeTime = 0,\n          leading = false,\n          maxing = false,\n          trailing = true;\n\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      wait = toNumber(wait) || 0;\n      if (isObject(options)) {\n        leading = !!options.leading;\n        maxing = 'maxWait' in options;\n        maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;\n        trailing = 'trailing' in options ? !!options.trailing : trailing;\n      }\n\n      function invokeFunc(time) {\n        var args = lastArgs,\n            thisArg = lastThis;\n\n        lastArgs = lastThis = undefined;\n        lastInvokeTime = time;\n        result = func.apply(thisArg, args);\n        return result;\n      }\n\n      function leadingEdge(time) {\n        // Reset any `maxWait` timer.\n        lastInvokeTime = time;\n        // Start the timer for the trailing edge.\n        timerId = setTimeout(timerExpired, wait);\n        // Invoke the leading edge.\n        return leading ? invokeFunc(time) : result;\n      }\n\n      function remainingWait(time) {\n        var timeSinceLastCall = time - lastCallTime,\n            timeSinceLastInvoke = time - lastInvokeTime,\n            result = wait - timeSinceLastCall;\n\n        return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;\n      }\n\n      function shouldInvoke(time) {\n        var timeSinceLastCall = time - lastCallTime,\n            timeSinceLastInvoke = time - lastInvokeTime;\n\n        // Either this is the first call, activity has stopped and we're at the\n        // trailing edge, the system time has gone backwards and we're treating\n        // it as the trailing edge, or we've hit the `maxWait` limit.\n        return (!lastCallTime || (timeSinceLastCall >= wait) ||\n          (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));\n      }\n\n      function timerExpired() {\n        var time = now();\n        if (shouldInvoke(time)) {\n          return trailingEdge(time);\n        }\n        // Restart the timer.\n        timerId = setTimeout(timerExpired, remainingWait(time));\n      }\n\n      function trailingEdge(time) {\n        clearTimeout(timerId);\n        timerId = undefined;\n\n        // Only invoke if we have `lastArgs` which means `func` has been\n        // debounced at least once.\n        if (trailing && lastArgs) {\n          return invokeFunc(time);\n        }\n        lastArgs = lastThis = undefined;\n        return result;\n      }\n\n      function cancel() {\n        if (timerId !== undefined) {\n          clearTimeout(timerId);\n        }\n        lastCallTime = lastInvokeTime = 0;\n        lastArgs = lastThis = timerId = undefined;\n      }\n\n      function flush() {\n        return timerId === undefined ? result : trailingEdge(now());\n      }\n\n      function debounced() {\n        var time = now(),\n            isInvoking = shouldInvoke(time);\n\n        lastArgs = arguments;\n        lastThis = this;\n        lastCallTime = time;\n\n        if (isInvoking) {\n          if (timerId === undefined) {\n            return leadingEdge(lastCallTime);\n          }\n          if (maxing) {\n            // Handle invocations in a tight loop.\n            clearTimeout(timerId);\n            timerId = setTimeout(timerExpired, wait);\n            return invokeFunc(lastCallTime);\n          }\n        }\n        if (timerId === undefined) {\n          timerId = setTimeout(timerExpired, wait);\n        }\n        return result;\n      }\n      debounced.cancel = cancel;\n      debounced.flush = flush;\n      return debounced;\n    }\n\n    /**\n     * Defers invoking the `func` until the current call stack has cleared. Any\n     * additional arguments are provided to `func` when it's invoked.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to defer.\n     * @param {...*} [args] The arguments to invoke `func` with.\n     * @returns {number} Returns the timer id.\n     * @example\n     *\n     * _.defer(function(text) {\n     *   console.log(text);\n     * }, 'deferred');\n     * // => Logs 'deferred' after one or more milliseconds.\n     */\n    var defer = rest(function(func, args) {\n      return baseDelay(func, 1, args);\n    });\n\n    /**\n     * Invokes `func` after `wait` milliseconds. Any additional arguments are\n     * provided to `func` when it's invoked.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to delay.\n     * @param {number} wait The number of milliseconds to delay invocation.\n     * @param {...*} [args] The arguments to invoke `func` with.\n     * @returns {number} Returns the timer id.\n     * @example\n     *\n     * _.delay(function(text) {\n     *   console.log(text);\n     * }, 1000, 'later');\n     * // => Logs 'later' after one second.\n     */\n    var delay = rest(function(func, wait, args) {\n      return baseDelay(func, toNumber(wait) || 0, args);\n    });\n\n    /**\n     * Creates a function that invokes `func` with arguments reversed.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Function\n     * @param {Function} func The function to flip arguments for.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var flipped = _.flip(function() {\n     *   return _.toArray(arguments);\n     * });\n     *\n     * flipped('a', 'b', 'c', 'd');\n     * // => ['d', 'c', 'b', 'a']\n     */\n    function flip(func) {\n      return createWrapper(func, FLIP_FLAG);\n    }\n\n    /**\n     * Creates a function that memoizes the result of `func`. If `resolver` is\n     * provided, it determines the cache key for storing the result based on the\n     * arguments provided to the memoized function. By default, the first argument\n     * provided to the memoized function is used as the map cache key. The `func`\n     * is invoked with the `this` binding of the memoized function.\n     *\n     * **Note:** The cache is exposed as the `cache` property on the memoized\n     * function. Its creation may be customized by replacing the `_.memoize.Cache`\n     * constructor with one whose instances implement the\n     * [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object)\n     * method interface of `delete`, `get`, `has`, and `set`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to have its output memoized.\n     * @param {Function} [resolver] The function to resolve the cache key.\n     * @returns {Function} Returns the new memoizing function.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': 2 };\n     * var other = { 'c': 3, 'd': 4 };\n     *\n     * var values = _.memoize(_.values);\n     * values(object);\n     * // => [1, 2]\n     *\n     * values(other);\n     * // => [3, 4]\n     *\n     * object.a = 2;\n     * values(object);\n     * // => [1, 2]\n     *\n     * // Modify the result cache.\n     * values.cache.set(object, ['a', 'b']);\n     * values(object);\n     * // => ['a', 'b']\n     *\n     * // Replace `_.memoize.Cache`.\n     * _.memoize.Cache = WeakMap;\n     */\n    function memoize(func, resolver) {\n      if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      var memoized = function() {\n        var args = arguments,\n            key = resolver ? resolver.apply(this, args) : args[0],\n            cache = memoized.cache;\n\n        if (cache.has(key)) {\n          return cache.get(key);\n        }\n        var result = func.apply(this, args);\n        memoized.cache = cache.set(key, result);\n        return result;\n      };\n      memoized.cache = new (memoize.Cache || MapCache);\n      return memoized;\n    }\n\n    // Assign cache to `_.memoize`.\n    memoize.Cache = MapCache;\n\n    /**\n     * Creates a function that negates the result of the predicate `func`. The\n     * `func` predicate is invoked with the `this` binding and arguments of the\n     * created function.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Function\n     * @param {Function} predicate The predicate to negate.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * function isEven(n) {\n     *   return n % 2 == 0;\n     * }\n     *\n     * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));\n     * // => [1, 3, 5]\n     */\n    function negate(predicate) {\n      if (typeof predicate != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      return function() {\n        return !predicate.apply(this, arguments);\n      };\n    }\n\n    /**\n     * Creates a function that is restricted to invoking `func` once. Repeat calls\n     * to the function return the value of the first invocation. The `func` is\n     * invoked with the `this` binding and arguments of the created function.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to restrict.\n     * @returns {Function} Returns the new restricted function.\n     * @example\n     *\n     * var initialize = _.once(createApplication);\n     * initialize();\n     * initialize();\n     * // `initialize` invokes `createApplication` once\n     */\n    function once(func) {\n      return before(2, func);\n    }\n\n    /**\n     * Creates a function that invokes `func` with arguments transformed by\n     * corresponding `transforms`.\n     *\n     * @static\n     * @since 4.0.0\n     * @memberOf _\n     * @category Function\n     * @param {Function} func The function to wrap.\n     * @param {...(Array|Array[]|Function|Function[]|Object|Object[]|string|string[])}\n     *  [transforms[_.identity]] The functions to transform.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * function doubled(n) {\n     *   return n * 2;\n     * }\n     *\n     * function square(n) {\n     *   return n * n;\n     * }\n     *\n     * var func = _.overArgs(function(x, y) {\n     *   return [x, y];\n     * }, square, doubled);\n     *\n     * func(9, 3);\n     * // => [81, 6]\n     *\n     * func(10, 5);\n     * // => [100, 10]\n     */\n    var overArgs = rest(function(func, transforms) {\n      transforms = (transforms.length == 1 && isArray(transforms[0]))\n        ? arrayMap(transforms[0], baseUnary(getIteratee()))\n        : arrayMap(baseFlatten(transforms, 1, isFlattenableIteratee), baseUnary(getIteratee()));\n\n      var funcsLength = transforms.length;\n      return rest(function(args) {\n        var index = -1,\n            length = nativeMin(args.length, funcsLength);\n\n        while (++index < length) {\n          args[index] = transforms[index].call(this, args[index]);\n        }\n        return apply(func, this, args);\n      });\n    });\n\n    /**\n     * Creates a function that invokes `func` with `partials` prepended to the\n     * arguments it receives. This method is like `_.bind` except it does **not**\n     * alter the `this` binding.\n     *\n     * The `_.partial.placeholder` value, which defaults to `_` in monolithic\n     * builds, may be used as a placeholder for partially applied arguments.\n     *\n     * **Note:** This method doesn't set the \"length\" property of partially\n     * applied functions.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.2.0\n     * @category Function\n     * @param {Function} func The function to partially apply arguments to.\n     * @param {...*} [partials] The arguments to be partially applied.\n     * @returns {Function} Returns the new partially applied function.\n     * @example\n     *\n     * var greet = function(greeting, name) {\n     *   return greeting + ' ' + name;\n     * };\n     *\n     * var sayHelloTo = _.partial(greet, 'hello');\n     * sayHelloTo('fred');\n     * // => 'hello fred'\n     *\n     * // Partially applied with placeholders.\n     * var greetFred = _.partial(greet, _, 'fred');\n     * greetFred('hi');\n     * // => 'hi fred'\n     */\n    var partial = rest(function(func, partials) {\n      var holders = replaceHolders(partials, getPlaceholder(partial));\n      return createWrapper(func, PARTIAL_FLAG, undefined, partials, holders);\n    });\n\n    /**\n     * This method is like `_.partial` except that partially applied arguments\n     * are appended to the arguments it receives.\n     *\n     * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic\n     * builds, may be used as a placeholder for partially applied arguments.\n     *\n     * **Note:** This method doesn't set the \"length\" property of partially\n     * applied functions.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.0.0\n     * @category Function\n     * @param {Function} func The function to partially apply arguments to.\n     * @param {...*} [partials] The arguments to be partially applied.\n     * @returns {Function} Returns the new partially applied function.\n     * @example\n     *\n     * var greet = function(greeting, name) {\n     *   return greeting + ' ' + name;\n     * };\n     *\n     * var greetFred = _.partialRight(greet, 'fred');\n     * greetFred('hi');\n     * // => 'hi fred'\n     *\n     * // Partially applied with placeholders.\n     * var sayHelloTo = _.partialRight(greet, 'hello', _);\n     * sayHelloTo('fred');\n     * // => 'hello fred'\n     */\n    var partialRight = rest(function(func, partials) {\n      var holders = replaceHolders(partials, getPlaceholder(partialRight));\n      return createWrapper(func, PARTIAL_RIGHT_FLAG, undefined, partials, holders);\n    });\n\n    /**\n     * Creates a function that invokes `func` with arguments arranged according\n     * to the specified `indexes` where the argument value at the first index is\n     * provided as the first argument, the argument value at the second index is\n     * provided as the second argument, and so on.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Function\n     * @param {Function} func The function to rearrange arguments for.\n     * @param {...(number|number[])} indexes The arranged argument indexes.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var rearged = _.rearg(function(a, b, c) {\n     *   return [a, b, c];\n     * }, 2, 0, 1);\n     *\n     * rearged('b', 'c', 'a')\n     * // => ['a', 'b', 'c']\n     */\n    var rearg = rest(function(func, indexes) {\n      return createWrapper(func, REARG_FLAG, undefined, undefined, undefined, baseFlatten(indexes, 1));\n    });\n\n    /**\n     * Creates a function that invokes `func` with the `this` binding of the\n     * created function and arguments from `start` and beyond provided as\n     * an array.\n     *\n     * **Note:** This method is based on the\n     * [rest parameter](https://mdn.io/rest_parameters).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Function\n     * @param {Function} func The function to apply a rest parameter to.\n     * @param {number} [start=func.length-1] The start position of the rest parameter.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var say = _.rest(function(what, names) {\n     *   return what + ' ' + _.initial(names).join(', ') +\n     *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);\n     * });\n     *\n     * say('hello', 'fred', 'barney', 'pebbles');\n     * // => 'hello fred, barney, & pebbles'\n     */\n    function rest(func, start) {\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0);\n      return function() {\n        var args = arguments,\n            index = -1,\n            length = nativeMax(args.length - start, 0),\n            array = Array(length);\n\n        while (++index < length) {\n          array[index] = args[start + index];\n        }\n        switch (start) {\n          case 0: return func.call(this, array);\n          case 1: return func.call(this, args[0], array);\n          case 2: return func.call(this, args[0], args[1], array);\n        }\n        var otherArgs = Array(start + 1);\n        index = -1;\n        while (++index < start) {\n          otherArgs[index] = args[index];\n        }\n        otherArgs[start] = array;\n        return apply(func, this, otherArgs);\n      };\n    }\n\n    /**\n     * Creates a function that invokes `func` with the `this` binding of the\n     * create function and an array of arguments much like\n     * [`Function#apply`](http://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply).\n     *\n     * **Note:** This method is based on the\n     * [spread operator](https://mdn.io/spread_operator).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.2.0\n     * @category Function\n     * @param {Function} func The function to spread arguments over.\n     * @param {number} [start=0] The start position of the spread.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var say = _.spread(function(who, what) {\n     *   return who + ' says ' + what;\n     * });\n     *\n     * say(['fred', 'hello']);\n     * // => 'fred says hello'\n     *\n     * var numbers = Promise.all([\n     *   Promise.resolve(40),\n     *   Promise.resolve(36)\n     * ]);\n     *\n     * numbers.then(_.spread(function(x, y) {\n     *   return x + y;\n     * }));\n     * // => a Promise of 76\n     */\n    function spread(func, start) {\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      start = start === undefined ? 0 : nativeMax(toInteger(start), 0);\n      return rest(function(args) {\n        var array = args[start],\n            otherArgs = castSlice(args, 0, start);\n\n        if (array) {\n          arrayPush(otherArgs, array);\n        }\n        return apply(func, this, otherArgs);\n      });\n    }\n\n    /**\n     * Creates a throttled function that only invokes `func` at most once per\n     * every `wait` milliseconds. The throttled function comes with a `cancel`\n     * method to cancel delayed `func` invocations and a `flush` method to\n     * immediately invoke them. Provide an options object to indicate whether\n     * `func` should be invoked on the leading and/or trailing edge of the `wait`\n     * timeout. The `func` is invoked with the last arguments provided to the\n     * throttled function. Subsequent calls to the throttled function return the\n     * result of the last `func` invocation.\n     *\n     * **Note:** If `leading` and `trailing` options are `true`, `func` is\n     * invoked on the trailing edge of the timeout only if the throttled function\n     * is invoked more than once during the `wait` timeout.\n     *\n     * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)\n     * for details over the differences between `_.throttle` and `_.debounce`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {Function} func The function to throttle.\n     * @param {number} [wait=0] The number of milliseconds to throttle invocations to.\n     * @param {Object} [options={}] The options object.\n     * @param {boolean} [options.leading=true]\n     *  Specify invoking on the leading edge of the timeout.\n     * @param {boolean} [options.trailing=true]\n     *  Specify invoking on the trailing edge of the timeout.\n     * @returns {Function} Returns the new throttled function.\n     * @example\n     *\n     * // Avoid excessively updating the position while scrolling.\n     * jQuery(window).on('scroll', _.throttle(updatePosition, 100));\n     *\n     * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.\n     * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });\n     * jQuery(element).on('click', throttled);\n     *\n     * // Cancel the trailing throttled invocation.\n     * jQuery(window).on('popstate', throttled.cancel);\n     */\n    function throttle(func, wait, options) {\n      var leading = true,\n          trailing = true;\n\n      if (typeof func != 'function') {\n        throw new TypeError(FUNC_ERROR_TEXT);\n      }\n      if (isObject(options)) {\n        leading = 'leading' in options ? !!options.leading : leading;\n        trailing = 'trailing' in options ? !!options.trailing : trailing;\n      }\n      return debounce(func, wait, {\n        'leading': leading,\n        'maxWait': wait,\n        'trailing': trailing\n      });\n    }\n\n    /**\n     * Creates a function that accepts up to one argument, ignoring any\n     * additional arguments.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Function\n     * @param {Function} func The function to cap arguments for.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * _.map(['6', '8', '10'], _.unary(parseInt));\n     * // => [6, 8, 10]\n     */\n    function unary(func) {\n      return ary(func, 1);\n    }\n\n    /**\n     * Creates a function that provides `value` to the wrapper function as its\n     * first argument. Any additional arguments provided to the function are\n     * appended to those provided to the wrapper function. The wrapper is invoked\n     * with the `this` binding of the created function.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Function\n     * @param {*} value The value to wrap.\n     * @param {Function} [wrapper=identity] The wrapper function.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var p = _.wrap(_.escape, function(func, text) {\n     *   return '<p>' + func(text) + '</p>';\n     * });\n     *\n     * p('fred, barney, & pebbles');\n     * // => '<p>fred, barney, &amp; pebbles</p>'\n     */\n    function wrap(value, wrapper) {\n      wrapper = wrapper == null ? identity : wrapper;\n      return partial(wrapper, value);\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Casts `value` as an array if it's not one.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.4.0\n     * @category Lang\n     * @param {*} value The value to inspect.\n     * @returns {Array} Returns the cast array.\n     * @example\n     *\n     * _.castArray(1);\n     * // => [1]\n     *\n     * _.castArray({ 'a': 1 });\n     * // => [{ 'a': 1 }]\n     *\n     * _.castArray('abc');\n     * // => ['abc']\n     *\n     * _.castArray(null);\n     * // => [null]\n     *\n     * _.castArray(undefined);\n     * // => [undefined]\n     *\n     * _.castArray();\n     * // => []\n     *\n     * var array = [1, 2, 3];\n     * console.log(_.castArray(array) === array);\n     * // => true\n     */\n    function castArray() {\n      if (!arguments.length) {\n        return [];\n      }\n      var value = arguments[0];\n      return isArray(value) ? value : [value];\n    }\n\n    /**\n     * Creates a shallow clone of `value`.\n     *\n     * **Note:** This method is loosely based on the\n     * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)\n     * and supports cloning arrays, array buffers, booleans, date objects, maps,\n     * numbers, `Object` objects, regexes, sets, strings, symbols, and typed\n     * arrays. The own enumerable properties of `arguments` objects are cloned\n     * as plain objects. An empty object is returned for uncloneable values such\n     * as error objects, functions, DOM nodes, and WeakMaps.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to clone.\n     * @returns {*} Returns the cloned value.\n     * @example\n     *\n     * var objects = [{ 'a': 1 }, { 'b': 2 }];\n     *\n     * var shallow = _.clone(objects);\n     * console.log(shallow[0] === objects[0]);\n     * // => true\n     */\n    function clone(value) {\n      return baseClone(value, false, true);\n    }\n\n    /**\n     * This method is like `_.clone` except that it accepts `customizer` which\n     * is invoked to produce the cloned value. If `customizer` returns `undefined`,\n     * cloning is handled by the method instead. The `customizer` is invoked with\n     * up to four arguments; (value [, index|key, object, stack]).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to clone.\n     * @param {Function} [customizer] The function to customize cloning.\n     * @returns {*} Returns the cloned value.\n     * @example\n     *\n     * function customizer(value) {\n     *   if (_.isElement(value)) {\n     *     return value.cloneNode(false);\n     *   }\n     * }\n     *\n     * var el = _.cloneWith(document.body, customizer);\n     *\n     * console.log(el === document.body);\n     * // => false\n     * console.log(el.nodeName);\n     * // => 'BODY'\n     * console.log(el.childNodes.length);\n     * // => 0\n     */\n    function cloneWith(value, customizer) {\n      return baseClone(value, false, true, customizer);\n    }\n\n    /**\n     * This method is like `_.clone` except that it recursively clones `value`.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.0.0\n     * @category Lang\n     * @param {*} value The value to recursively clone.\n     * @returns {*} Returns the deep cloned value.\n     * @example\n     *\n     * var objects = [{ 'a': 1 }, { 'b': 2 }];\n     *\n     * var deep = _.cloneDeep(objects);\n     * console.log(deep[0] === objects[0]);\n     * // => false\n     */\n    function cloneDeep(value) {\n      return baseClone(value, true, true);\n    }\n\n    /**\n     * This method is like `_.cloneWith` except that it recursively clones `value`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to recursively clone.\n     * @param {Function} [customizer] The function to customize cloning.\n     * @returns {*} Returns the deep cloned value.\n     * @example\n     *\n     * function customizer(value) {\n     *   if (_.isElement(value)) {\n     *     return value.cloneNode(true);\n     *   }\n     * }\n     *\n     * var el = _.cloneDeepWith(document.body, customizer);\n     *\n     * console.log(el === document.body);\n     * // => false\n     * console.log(el.nodeName);\n     * // => 'BODY'\n     * console.log(el.childNodes.length);\n     * // => 20\n     */\n    function cloneDeepWith(value, customizer) {\n      return baseClone(value, true, true, customizer);\n    }\n\n    /**\n     * Performs a\n     * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero)\n     * comparison between two values to determine if they are equivalent.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n     * @example\n     *\n     * var object = { 'user': 'fred' };\n     * var other = { 'user': 'fred' };\n     *\n     * _.eq(object, object);\n     * // => true\n     *\n     * _.eq(object, other);\n     * // => false\n     *\n     * _.eq('a', 'a');\n     * // => true\n     *\n     * _.eq('a', Object('a'));\n     * // => false\n     *\n     * _.eq(NaN, NaN);\n     * // => true\n     */\n    function eq(value, other) {\n      return value === other || (value !== value && other !== other);\n    }\n\n    /**\n     * Checks if `value` is greater than `other`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.9.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @returns {boolean} Returns `true` if `value` is greater than `other`,\n     *  else `false`.\n     * @example\n     *\n     * _.gt(3, 1);\n     * // => true\n     *\n     * _.gt(3, 3);\n     * // => false\n     *\n     * _.gt(1, 3);\n     * // => false\n     */\n    function gt(value, other) {\n      return value > other;\n    }\n\n    /**\n     * Checks if `value` is greater than or equal to `other`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.9.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @returns {boolean} Returns `true` if `value` is greater than or equal to\n     *  `other`, else `false`.\n     * @example\n     *\n     * _.gte(3, 1);\n     * // => true\n     *\n     * _.gte(3, 3);\n     * // => true\n     *\n     * _.gte(1, 3);\n     * // => false\n     */\n    function gte(value, other) {\n      return value >= other;\n    }\n\n    /**\n     * Checks if `value` is likely an `arguments` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isArguments(function() { return arguments; }());\n     * // => true\n     *\n     * _.isArguments([1, 2, 3]);\n     * // => false\n     */\n    function isArguments(value) {\n      // Safari 8.1 incorrectly makes `arguments.callee` enumerable in strict mode.\n      return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n        (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n    }\n\n    /**\n     * Checks if `value` is classified as an `Array` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @type {Function}\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isArray([1, 2, 3]);\n     * // => true\n     *\n     * _.isArray(document.body.children);\n     * // => false\n     *\n     * _.isArray('abc');\n     * // => false\n     *\n     * _.isArray(_.noop);\n     * // => false\n     */\n    var isArray = Array.isArray;\n\n    /**\n     * Checks if `value` is classified as an `ArrayBuffer` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.3.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isArrayBuffer(new ArrayBuffer(2));\n     * // => true\n     *\n     * _.isArrayBuffer(new Array(2));\n     * // => false\n     */\n    function isArrayBuffer(value) {\n      return isObjectLike(value) && objectToString.call(value) == arrayBufferTag;\n    }\n\n    /**\n     * Checks if `value` is array-like. A value is considered array-like if it's\n     * not a function and has a `value.length` that's an integer greater than or\n     * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n     * @example\n     *\n     * _.isArrayLike([1, 2, 3]);\n     * // => true\n     *\n     * _.isArrayLike(document.body.children);\n     * // => true\n     *\n     * _.isArrayLike('abc');\n     * // => true\n     *\n     * _.isArrayLike(_.noop);\n     * // => false\n     */\n    function isArrayLike(value) {\n      return value != null && isLength(getLength(value)) && !isFunction(value);\n    }\n\n    /**\n     * This method is like `_.isArrayLike` except that it also checks if `value`\n     * is an object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is an array-like object,\n     *  else `false`.\n     * @example\n     *\n     * _.isArrayLikeObject([1, 2, 3]);\n     * // => true\n     *\n     * _.isArrayLikeObject(document.body.children);\n     * // => true\n     *\n     * _.isArrayLikeObject('abc');\n     * // => false\n     *\n     * _.isArrayLikeObject(_.noop);\n     * // => false\n     */\n    function isArrayLikeObject(value) {\n      return isObjectLike(value) && isArrayLike(value);\n    }\n\n    /**\n     * Checks if `value` is classified as a boolean primitive or object.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isBoolean(false);\n     * // => true\n     *\n     * _.isBoolean(null);\n     * // => false\n     */\n    function isBoolean(value) {\n      return value === true || value === false ||\n        (isObjectLike(value) && objectToString.call(value) == boolTag);\n    }\n\n    /**\n     * Checks if `value` is a buffer.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.3.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.\n     * @example\n     *\n     * _.isBuffer(new Buffer(2));\n     * // => true\n     *\n     * _.isBuffer(new Uint8Array(2));\n     * // => false\n     */\n    var isBuffer = !Buffer ? constant(false) : function(value) {\n      return value instanceof Buffer;\n    };\n\n    /**\n     * Checks if `value` is classified as a `Date` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isDate(new Date);\n     * // => true\n     *\n     * _.isDate('Mon April 23 2012');\n     * // => false\n     */\n    function isDate(value) {\n      return isObjectLike(value) && objectToString.call(value) == dateTag;\n    }\n\n    /**\n     * Checks if `value` is likely a DOM element.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a DOM element,\n     *  else `false`.\n     * @example\n     *\n     * _.isElement(document.body);\n     * // => true\n     *\n     * _.isElement('<body>');\n     * // => false\n     */\n    function isElement(value) {\n      return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value);\n    }\n\n    /**\n     * Checks if `value` is an empty object, collection, map, or set.\n     *\n     * Objects are considered empty if they have no own enumerable string keyed\n     * properties.\n     *\n     * Array-like values such as `arguments` objects, arrays, buffers, strings, or\n     * jQuery-like collections are considered empty if they have a `length` of `0`.\n     * Similarly, maps and sets are considered empty if they have a `size` of `0`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is empty, else `false`.\n     * @example\n     *\n     * _.isEmpty(null);\n     * // => true\n     *\n     * _.isEmpty(true);\n     * // => true\n     *\n     * _.isEmpty(1);\n     * // => true\n     *\n     * _.isEmpty([1, 2, 3]);\n     * // => false\n     *\n     * _.isEmpty({ 'a': 1 });\n     * // => false\n     */\n    function isEmpty(value) {\n      if (isArrayLike(value) &&\n          (isArray(value) || isString(value) || isFunction(value.splice) ||\n            isArguments(value) || isBuffer(value))) {\n        return !value.length;\n      }\n      if (isObjectLike(value)) {\n        var tag = getTag(value);\n        if (tag == mapTag || tag == setTag) {\n          return !value.size;\n        }\n      }\n      for (var key in value) {\n        if (hasOwnProperty.call(value, key)) {\n          return false;\n        }\n      }\n      return !(nonEnumShadows && keys(value).length);\n    }\n\n    /**\n     * Performs a deep comparison between two values to determine if they are\n     * equivalent.\n     *\n     * **Note:** This method supports comparing arrays, array buffers, booleans,\n     * date objects, error objects, maps, numbers, `Object` objects, regexes,\n     * sets, strings, symbols, and typed arrays. `Object` objects are compared\n     * by their own, not inherited, enumerable properties. Functions and DOM\n     * nodes are **not** supported.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @returns {boolean} Returns `true` if the values are equivalent,\n     *  else `false`.\n     * @example\n     *\n     * var object = { 'user': 'fred' };\n     * var other = { 'user': 'fred' };\n     *\n     * _.isEqual(object, other);\n     * // => true\n     *\n     * object === other;\n     * // => false\n     */\n    function isEqual(value, other) {\n      return baseIsEqual(value, other);\n    }\n\n    /**\n     * This method is like `_.isEqual` except that it accepts `customizer` which\n     * is invoked to compare values. If `customizer` returns `undefined`, comparisons\n     * are handled by the method instead. The `customizer` is invoked with up to\n     * six arguments: (objValue, othValue [, index|key, object, other, stack]).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @param {Function} [customizer] The function to customize comparisons.\n     * @returns {boolean} Returns `true` if the values are equivalent,\n     *  else `false`.\n     * @example\n     *\n     * function isGreeting(value) {\n     *   return /^h(?:i|ello)$/.test(value);\n     * }\n     *\n     * function customizer(objValue, othValue) {\n     *   if (isGreeting(objValue) && isGreeting(othValue)) {\n     *     return true;\n     *   }\n     * }\n     *\n     * var array = ['hello', 'goodbye'];\n     * var other = ['hi', 'goodbye'];\n     *\n     * _.isEqualWith(array, other, customizer);\n     * // => true\n     */\n    function isEqualWith(value, other, customizer) {\n      customizer = typeof customizer == 'function' ? customizer : undefined;\n      var result = customizer ? customizer(value, other) : undefined;\n      return result === undefined ? baseIsEqual(value, other, customizer) : !!result;\n    }\n\n    /**\n     * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,\n     * `SyntaxError`, `TypeError`, or `URIError` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is an error object,\n     *  else `false`.\n     * @example\n     *\n     * _.isError(new Error);\n     * // => true\n     *\n     * _.isError(Error);\n     * // => false\n     */\n    function isError(value) {\n      if (!isObjectLike(value)) {\n        return false;\n      }\n      return (objectToString.call(value) == errorTag) ||\n        (typeof value.message == 'string' && typeof value.name == 'string');\n    }\n\n    /**\n     * Checks if `value` is a finite primitive number.\n     *\n     * **Note:** This method is based on\n     * [`Number.isFinite`](https://mdn.io/Number/isFinite).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a finite number,\n     *  else `false`.\n     * @example\n     *\n     * _.isFinite(3);\n     * // => true\n     *\n     * _.isFinite(Number.MAX_VALUE);\n     * // => true\n     *\n     * _.isFinite(3.14);\n     * // => true\n     *\n     * _.isFinite(Infinity);\n     * // => false\n     */\n    function isFinite(value) {\n      return typeof value == 'number' && nativeIsFinite(value);\n    }\n\n    /**\n     * Checks if `value` is classified as a `Function` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isFunction(_);\n     * // => true\n     *\n     * _.isFunction(/abc/);\n     * // => false\n     */\n    function isFunction(value) {\n      // The use of `Object#toString` avoids issues with the `typeof` operator\n      // in Safari 8 which returns 'object' for typed array and weak map constructors,\n      // and PhantomJS 1.9 which returns 'function' for `NodeList` instances.\n      var tag = isObject(value) ? objectToString.call(value) : '';\n      return tag == funcTag || tag == genTag;\n    }\n\n    /**\n     * Checks if `value` is an integer.\n     *\n     * **Note:** This method is based on\n     * [`Number.isInteger`](https://mdn.io/Number/isInteger).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is an integer, else `false`.\n     * @example\n     *\n     * _.isInteger(3);\n     * // => true\n     *\n     * _.isInteger(Number.MIN_VALUE);\n     * // => false\n     *\n     * _.isInteger(Infinity);\n     * // => false\n     *\n     * _.isInteger('3');\n     * // => false\n     */\n    function isInteger(value) {\n      return typeof value == 'number' && value == toInteger(value);\n    }\n\n    /**\n     * Checks if `value` is a valid array-like length.\n     *\n     * **Note:** This function is loosely based on\n     * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a valid length,\n     *  else `false`.\n     * @example\n     *\n     * _.isLength(3);\n     * // => true\n     *\n     * _.isLength(Number.MIN_VALUE);\n     * // => false\n     *\n     * _.isLength(Infinity);\n     * // => false\n     *\n     * _.isLength('3');\n     * // => false\n     */\n    function isLength(value) {\n      return typeof value == 'number' &&\n        value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n    }\n\n    /**\n     * Checks if `value` is the\n     * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types)\n     * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n     * @example\n     *\n     * _.isObject({});\n     * // => true\n     *\n     * _.isObject([1, 2, 3]);\n     * // => true\n     *\n     * _.isObject(_.noop);\n     * // => true\n     *\n     * _.isObject(null);\n     * // => false\n     */\n    function isObject(value) {\n      var type = typeof value;\n      return !!value && (type == 'object' || type == 'function');\n    }\n\n    /**\n     * Checks if `value` is object-like. A value is object-like if it's not `null`\n     * and has a `typeof` result of \"object\".\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n     * @example\n     *\n     * _.isObjectLike({});\n     * // => true\n     *\n     * _.isObjectLike([1, 2, 3]);\n     * // => true\n     *\n     * _.isObjectLike(_.noop);\n     * // => false\n     *\n     * _.isObjectLike(null);\n     * // => false\n     */\n    function isObjectLike(value) {\n      return !!value && typeof value == 'object';\n    }\n\n    /**\n     * Checks if `value` is classified as a `Map` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.3.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isMap(new Map);\n     * // => true\n     *\n     * _.isMap(new WeakMap);\n     * // => false\n     */\n    function isMap(value) {\n      return isObjectLike(value) && getTag(value) == mapTag;\n    }\n\n    /**\n     * Performs a partial deep comparison between `object` and `source` to\n     * determine if `object` contains equivalent property values. This method is\n     * equivalent to a `_.matches` function when `source` is partially applied.\n     *\n     * **Note:** This method supports comparing the same values as `_.isEqual`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Lang\n     * @param {Object} object The object to inspect.\n     * @param {Object} source The object of property values to match.\n     * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n     * @example\n     *\n     * var object = { 'user': 'fred', 'age': 40 };\n     *\n     * _.isMatch(object, { 'age': 40 });\n     * // => true\n     *\n     * _.isMatch(object, { 'age': 36 });\n     * // => false\n     */\n    function isMatch(object, source) {\n      return object === source || baseIsMatch(object, source, getMatchData(source));\n    }\n\n    /**\n     * This method is like `_.isMatch` except that it accepts `customizer` which\n     * is invoked to compare values. If `customizer` returns `undefined`, comparisons\n     * are handled by the method instead. The `customizer` is invoked with five\n     * arguments: (objValue, srcValue, index|key, object, source).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {Object} object The object to inspect.\n     * @param {Object} source The object of property values to match.\n     * @param {Function} [customizer] The function to customize comparisons.\n     * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n     * @example\n     *\n     * function isGreeting(value) {\n     *   return /^h(?:i|ello)$/.test(value);\n     * }\n     *\n     * function customizer(objValue, srcValue) {\n     *   if (isGreeting(objValue) && isGreeting(srcValue)) {\n     *     return true;\n     *   }\n     * }\n     *\n     * var object = { 'greeting': 'hello' };\n     * var source = { 'greeting': 'hi' };\n     *\n     * _.isMatchWith(object, source, customizer);\n     * // => true\n     */\n    function isMatchWith(object, source, customizer) {\n      customizer = typeof customizer == 'function' ? customizer : undefined;\n      return baseIsMatch(object, source, getMatchData(source), customizer);\n    }\n\n    /**\n     * Checks if `value` is `NaN`.\n     *\n     * **Note:** This method is based on\n     * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as\n     * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for\n     * `undefined` and other non-number values.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.\n     * @example\n     *\n     * _.isNaN(NaN);\n     * // => true\n     *\n     * _.isNaN(new Number(NaN));\n     * // => true\n     *\n     * isNaN(undefined);\n     * // => true\n     *\n     * _.isNaN(undefined);\n     * // => false\n     */\n    function isNaN(value) {\n      // An `NaN` primitive is the only value that is not equal to itself.\n      // Perform the `toStringTag` check first to avoid errors with some\n      // ActiveX objects in IE.\n      return isNumber(value) && value != +value;\n    }\n\n    /**\n     * Checks if `value` is a native function.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a native function,\n     *  else `false`.\n     * @example\n     *\n     * _.isNative(Array.prototype.push);\n     * // => true\n     *\n     * _.isNative(_);\n     * // => false\n     */\n    function isNative(value) {\n      if (!isObject(value)) {\n        return false;\n      }\n      var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n      return pattern.test(toSource(value));\n    }\n\n    /**\n     * Checks if `value` is `null`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is `null`, else `false`.\n     * @example\n     *\n     * _.isNull(null);\n     * // => true\n     *\n     * _.isNull(void 0);\n     * // => false\n     */\n    function isNull(value) {\n      return value === null;\n    }\n\n    /**\n     * Checks if `value` is `null` or `undefined`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is nullish, else `false`.\n     * @example\n     *\n     * _.isNil(null);\n     * // => true\n     *\n     * _.isNil(void 0);\n     * // => true\n     *\n     * _.isNil(NaN);\n     * // => false\n     */\n    function isNil(value) {\n      return value == null;\n    }\n\n    /**\n     * Checks if `value` is classified as a `Number` primitive or object.\n     *\n     * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are\n     * classified as numbers, use the `_.isFinite` method.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isNumber(3);\n     * // => true\n     *\n     * _.isNumber(Number.MIN_VALUE);\n     * // => true\n     *\n     * _.isNumber(Infinity);\n     * // => true\n     *\n     * _.isNumber('3');\n     * // => false\n     */\n    function isNumber(value) {\n      return typeof value == 'number' ||\n        (isObjectLike(value) && objectToString.call(value) == numberTag);\n    }\n\n    /**\n     * Checks if `value` is a plain object, that is, an object created by the\n     * `Object` constructor or one with a `[[Prototype]]` of `null`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.8.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a plain object,\n     *  else `false`.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     * }\n     *\n     * _.isPlainObject(new Foo);\n     * // => false\n     *\n     * _.isPlainObject([1, 2, 3]);\n     * // => false\n     *\n     * _.isPlainObject({ 'x': 0, 'y': 0 });\n     * // => true\n     *\n     * _.isPlainObject(Object.create(null));\n     * // => true\n     */\n    function isPlainObject(value) {\n      if (!isObjectLike(value) ||\n          objectToString.call(value) != objectTag || isHostObject(value)) {\n        return false;\n      }\n      var proto = getPrototype(value);\n      if (proto === null) {\n        return true;\n      }\n      var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;\n      return (typeof Ctor == 'function' &&\n        Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);\n    }\n\n    /**\n     * Checks if `value` is classified as a `RegExp` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.1.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isRegExp(/abc/);\n     * // => true\n     *\n     * _.isRegExp('/abc/');\n     * // => false\n     */\n    function isRegExp(value) {\n      return isObject(value) && objectToString.call(value) == regexpTag;\n    }\n\n    /**\n     * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754\n     * double precision number which isn't the result of a rounded unsafe integer.\n     *\n     * **Note:** This method is based on\n     * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is a safe integer,\n     *  else `false`.\n     * @example\n     *\n     * _.isSafeInteger(3);\n     * // => true\n     *\n     * _.isSafeInteger(Number.MIN_VALUE);\n     * // => false\n     *\n     * _.isSafeInteger(Infinity);\n     * // => false\n     *\n     * _.isSafeInteger('3');\n     * // => false\n     */\n    function isSafeInteger(value) {\n      return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;\n    }\n\n    /**\n     * Checks if `value` is classified as a `Set` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.3.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isSet(new Set);\n     * // => true\n     *\n     * _.isSet(new WeakSet);\n     * // => false\n     */\n    function isSet(value) {\n      return isObjectLike(value) && getTag(value) == setTag;\n    }\n\n    /**\n     * Checks if `value` is classified as a `String` primitive or object.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isString('abc');\n     * // => true\n     *\n     * _.isString(1);\n     * // => false\n     */\n    function isString(value) {\n      return typeof value == 'string' ||\n        (!isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag);\n    }\n\n    /**\n     * Checks if `value` is classified as a `Symbol` primitive or object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isSymbol(Symbol.iterator);\n     * // => true\n     *\n     * _.isSymbol('abc');\n     * // => false\n     */\n    function isSymbol(value) {\n      return typeof value == 'symbol' ||\n        (isObjectLike(value) && objectToString.call(value) == symbolTag);\n    }\n\n    /**\n     * Checks if `value` is classified as a typed array.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isTypedArray(new Uint8Array);\n     * // => true\n     *\n     * _.isTypedArray([]);\n     * // => false\n     */\n    function isTypedArray(value) {\n      return isObjectLike(value) &&\n        isLength(value.length) && !!typedArrayTags[objectToString.call(value)];\n    }\n\n    /**\n     * Checks if `value` is `undefined`.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.\n     * @example\n     *\n     * _.isUndefined(void 0);\n     * // => true\n     *\n     * _.isUndefined(null);\n     * // => false\n     */\n    function isUndefined(value) {\n      return value === undefined;\n    }\n\n    /**\n     * Checks if `value` is classified as a `WeakMap` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.3.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isWeakMap(new WeakMap);\n     * // => true\n     *\n     * _.isWeakMap(new Map);\n     * // => false\n     */\n    function isWeakMap(value) {\n      return isObjectLike(value) && getTag(value) == weakMapTag;\n    }\n\n    /**\n     * Checks if `value` is classified as a `WeakSet` object.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.3.0\n     * @category Lang\n     * @param {*} value The value to check.\n     * @returns {boolean} Returns `true` if `value` is correctly classified,\n     *  else `false`.\n     * @example\n     *\n     * _.isWeakSet(new WeakSet);\n     * // => true\n     *\n     * _.isWeakSet(new Set);\n     * // => false\n     */\n    function isWeakSet(value) {\n      return isObjectLike(value) && objectToString.call(value) == weakSetTag;\n    }\n\n    /**\n     * Checks if `value` is less than `other`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.9.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @returns {boolean} Returns `true` if `value` is less than `other`,\n     *  else `false`.\n     * @example\n     *\n     * _.lt(1, 3);\n     * // => true\n     *\n     * _.lt(3, 3);\n     * // => false\n     *\n     * _.lt(3, 1);\n     * // => false\n     */\n    function lt(value, other) {\n      return value < other;\n    }\n\n    /**\n     * Checks if `value` is less than or equal to `other`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.9.0\n     * @category Lang\n     * @param {*} value The value to compare.\n     * @param {*} other The other value to compare.\n     * @returns {boolean} Returns `true` if `value` is less than or equal to\n     *  `other`, else `false`.\n     * @example\n     *\n     * _.lte(1, 3);\n     * // => true\n     *\n     * _.lte(3, 3);\n     * // => true\n     *\n     * _.lte(3, 1);\n     * // => false\n     */\n    function lte(value, other) {\n      return value <= other;\n    }\n\n    /**\n     * Converts `value` to an array.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Lang\n     * @param {*} value The value to convert.\n     * @returns {Array} Returns the converted array.\n     * @example\n     *\n     * _.toArray({ 'a': 1, 'b': 2 });\n     * // => [1, 2]\n     *\n     * _.toArray('abc');\n     * // => ['a', 'b', 'c']\n     *\n     * _.toArray(1);\n     * // => []\n     *\n     * _.toArray(null);\n     * // => []\n     */\n    function toArray(value) {\n      if (!value) {\n        return [];\n      }\n      if (isArrayLike(value)) {\n        return isString(value) ? stringToArray(value) : copyArray(value);\n      }\n      if (iteratorSymbol && value[iteratorSymbol]) {\n        return iteratorToArray(value[iteratorSymbol]());\n      }\n      var tag = getTag(value),\n          func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);\n\n      return func(value);\n    }\n\n    /**\n     * Converts `value` to an integer.\n     *\n     * **Note:** This function is loosely based on\n     * [`ToInteger`](http://www.ecma-international.org/ecma-262/6.0/#sec-tointeger).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to convert.\n     * @returns {number} Returns the converted integer.\n     * @example\n     *\n     * _.toInteger(3);\n     * // => 3\n     *\n     * _.toInteger(Number.MIN_VALUE);\n     * // => 0\n     *\n     * _.toInteger(Infinity);\n     * // => 1.7976931348623157e+308\n     *\n     * _.toInteger('3');\n     * // => 3\n     */\n    function toInteger(value) {\n      if (!value) {\n        return value === 0 ? value : 0;\n      }\n      value = toNumber(value);\n      if (value === INFINITY || value === -INFINITY) {\n        var sign = (value < 0 ? -1 : 1);\n        return sign * MAX_INTEGER;\n      }\n      var remainder = value % 1;\n      return value === value ? (remainder ? value - remainder : value) : 0;\n    }\n\n    /**\n     * Converts `value` to an integer suitable for use as the length of an\n     * array-like object.\n     *\n     * **Note:** This method is based on\n     * [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to convert.\n     * @returns {number} Returns the converted integer.\n     * @example\n     *\n     * _.toLength(3);\n     * // => 3\n     *\n     * _.toLength(Number.MIN_VALUE);\n     * // => 0\n     *\n     * _.toLength(Infinity);\n     * // => 4294967295\n     *\n     * _.toLength('3');\n     * // => 3\n     */\n    function toLength(value) {\n      return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;\n    }\n\n    /**\n     * Converts `value` to a number.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to process.\n     * @returns {number} Returns the number.\n     * @example\n     *\n     * _.toNumber(3);\n     * // => 3\n     *\n     * _.toNumber(Number.MIN_VALUE);\n     * // => 5e-324\n     *\n     * _.toNumber(Infinity);\n     * // => Infinity\n     *\n     * _.toNumber('3');\n     * // => 3\n     */\n    function toNumber(value) {\n      if (typeof value == 'number') {\n        return value;\n      }\n      if (isSymbol(value)) {\n        return NAN;\n      }\n      if (isObject(value)) {\n        var other = isFunction(value.valueOf) ? value.valueOf() : value;\n        value = isObject(other) ? (other + '') : other;\n      }\n      if (typeof value != 'string') {\n        return value === 0 ? value : +value;\n      }\n      value = value.replace(reTrim, '');\n      var isBinary = reIsBinary.test(value);\n      return (isBinary || reIsOctal.test(value))\n        ? freeParseInt(value.slice(2), isBinary ? 2 : 8)\n        : (reIsBadHex.test(value) ? NAN : +value);\n    }\n\n    /**\n     * Converts `value` to a plain object flattening inherited enumerable string\n     * keyed properties of `value` to own properties of the plain object.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Lang\n     * @param {*} value The value to convert.\n     * @returns {Object} Returns the converted plain object.\n     * @example\n     *\n     * function Foo() {\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.assign({ 'a': 1 }, new Foo);\n     * // => { 'a': 1, 'b': 2 }\n     *\n     * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));\n     * // => { 'a': 1, 'b': 2, 'c': 3 }\n     */\n    function toPlainObject(value) {\n      return copyObject(value, keysIn(value));\n    }\n\n    /**\n     * Converts `value` to a safe integer. A safe integer can be compared and\n     * represented correctly.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to convert.\n     * @returns {number} Returns the converted integer.\n     * @example\n     *\n     * _.toSafeInteger(3);\n     * // => 3\n     *\n     * _.toSafeInteger(Number.MIN_VALUE);\n     * // => 0\n     *\n     * _.toSafeInteger(Infinity);\n     * // => 9007199254740991\n     *\n     * _.toSafeInteger('3');\n     * // => 3\n     */\n    function toSafeInteger(value) {\n      return baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER);\n    }\n\n    /**\n     * Converts `value` to a string. An empty string is returned for `null`\n     * and `undefined` values. The sign of `-0` is preserved.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Lang\n     * @param {*} value The value to process.\n     * @returns {string} Returns the string.\n     * @example\n     *\n     * _.toString(null);\n     * // => ''\n     *\n     * _.toString(-0);\n     * // => '-0'\n     *\n     * _.toString([1, 2, 3]);\n     * // => '1,2,3'\n     */\n    function toString(value) {\n      // Exit early for strings to avoid a performance hit in some environments.\n      if (typeof value == 'string') {\n        return value;\n      }\n      if (value == null) {\n        return '';\n      }\n      if (isSymbol(value)) {\n        return symbolToString ? symbolToString.call(value) : '';\n      }\n      var result = (value + '');\n      return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Assigns own enumerable string keyed properties of source objects to the\n     * destination object. Source objects are applied from left to right.\n     * Subsequent sources overwrite property assignments of previous sources.\n     *\n     * **Note:** This method mutates `object` and is loosely based on\n     * [`Object.assign`](https://mdn.io/Object/assign).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.10.0\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} [sources] The source objects.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function Foo() {\n     *   this.c = 3;\n     * }\n     *\n     * function Bar() {\n     *   this.e = 5;\n     * }\n     *\n     * Foo.prototype.d = 4;\n     * Bar.prototype.f = 6;\n     *\n     * _.assign({ 'a': 1 }, new Foo, new Bar);\n     * // => { 'a': 1, 'c': 3, 'e': 5 }\n     */\n    var assign = createAssigner(function(object, source) {\n      if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {\n        copyObject(source, keys(source), object);\n        return;\n      }\n      for (var key in source) {\n        if (hasOwnProperty.call(source, key)) {\n          assignValue(object, key, source[key]);\n        }\n      }\n    });\n\n    /**\n     * This method is like `_.assign` except that it iterates over own and\n     * inherited source properties.\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @alias extend\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} [sources] The source objects.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function Foo() {\n     *   this.b = 2;\n     * }\n     *\n     * function Bar() {\n     *   this.d = 4;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     * Bar.prototype.e = 5;\n     *\n     * _.assignIn({ 'a': 1 }, new Foo, new Bar);\n     * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5 }\n     */\n    var assignIn = createAssigner(function(object, source) {\n      if (nonEnumShadows || isPrototype(source) || isArrayLike(source)) {\n        copyObject(source, keysIn(source), object);\n        return;\n      }\n      for (var key in source) {\n        assignValue(object, key, source[key]);\n      }\n    });\n\n    /**\n     * This method is like `_.assignIn` except that it accepts `customizer`\n     * which is invoked to produce the assigned values. If `customizer` returns\n     * `undefined`, assignment is handled by the method instead. The `customizer`\n     * is invoked with five arguments: (objValue, srcValue, key, object, source).\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @alias extendWith\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} sources The source objects.\n     * @param {Function} [customizer] The function to customize assigned values.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function customizer(objValue, srcValue) {\n     *   return _.isUndefined(objValue) ? srcValue : objValue;\n     * }\n     *\n     * var defaults = _.partialRight(_.assignInWith, customizer);\n     *\n     * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n     * // => { 'a': 1, 'b': 2 }\n     */\n    var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {\n      copyObject(source, keysIn(source), object, customizer);\n    });\n\n    /**\n     * This method is like `_.assign` except that it accepts `customizer`\n     * which is invoked to produce the assigned values. If `customizer` returns\n     * `undefined`, assignment is handled by the method instead. The `customizer`\n     * is invoked with five arguments: (objValue, srcValue, key, object, source).\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} sources The source objects.\n     * @param {Function} [customizer] The function to customize assigned values.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function customizer(objValue, srcValue) {\n     *   return _.isUndefined(objValue) ? srcValue : objValue;\n     * }\n     *\n     * var defaults = _.partialRight(_.assignWith, customizer);\n     *\n     * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });\n     * // => { 'a': 1, 'b': 2 }\n     */\n    var assignWith = createAssigner(function(object, source, srcIndex, customizer) {\n      copyObject(source, keys(source), object, customizer);\n    });\n\n    /**\n     * Creates an array of values corresponding to `paths` of `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.0.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {...(string|string[])} [paths] The property paths of elements to pick.\n     * @returns {Array} Returns the new array of picked elements.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };\n     *\n     * _.at(object, ['a[0].b.c', 'a[1]']);\n     * // => [3, 4]\n     *\n     * _.at(['a', 'b', 'c'], 0, 2);\n     * // => ['a', 'c']\n     */\n    var at = rest(function(object, paths) {\n      return baseAt(object, baseFlatten(paths, 1));\n    });\n\n    /**\n     * Creates an object that inherits from the `prototype` object. If a\n     * `properties` object is given, its own enumerable string keyed properties\n     * are assigned to the created object.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.3.0\n     * @category Object\n     * @param {Object} prototype The object to inherit from.\n     * @param {Object} [properties] The properties to assign to the object.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * function Shape() {\n     *   this.x = 0;\n     *   this.y = 0;\n     * }\n     *\n     * function Circle() {\n     *   Shape.call(this);\n     * }\n     *\n     * Circle.prototype = _.create(Shape.prototype, {\n     *   'constructor': Circle\n     * });\n     *\n     * var circle = new Circle;\n     * circle instanceof Circle;\n     * // => true\n     *\n     * circle instanceof Shape;\n     * // => true\n     */\n    function create(prototype, properties) {\n      var result = baseCreate(prototype);\n      return properties ? baseAssign(result, properties) : result;\n    }\n\n    /**\n     * Assigns own and inherited enumerable string keyed properties of source\n     * objects to the destination object for all destination properties that\n     * resolve to `undefined`. Source objects are applied from left to right.\n     * Once a property is set, additional values of the same property are ignored.\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} [sources] The source objects.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' });\n     * // => { 'user': 'barney', 'age': 36 }\n     */\n    var defaults = rest(function(args) {\n      args.push(undefined, assignInDefaults);\n      return apply(assignInWith, undefined, args);\n    });\n\n    /**\n     * This method is like `_.defaults` except that it recursively assigns\n     * default properties.\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.10.0\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} [sources] The source objects.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * _.defaultsDeep({ 'user': { 'name': 'barney' } }, { 'user': { 'name': 'fred', 'age': 36 } });\n     * // => { 'user': { 'name': 'barney', 'age': 36 } }\n     *\n     */\n    var defaultsDeep = rest(function(args) {\n      args.push(undefined, mergeDefaults);\n      return apply(mergeWith, undefined, args);\n    });\n\n    /**\n     * This method is like `_.find` except that it returns the key of the first\n     * element `predicate` returns truthy for instead of the element itself.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.1.0\n     * @category Object\n     * @param {Object} object The object to search.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {string|undefined} Returns the key of the matched element,\n     *  else `undefined`.\n     * @example\n     *\n     * var users = {\n     *   'barney':  { 'age': 36, 'active': true },\n     *   'fred':    { 'age': 40, 'active': false },\n     *   'pebbles': { 'age': 1,  'active': true }\n     * };\n     *\n     * _.findKey(users, function(o) { return o.age < 40; });\n     * // => 'barney' (iteration order is not guaranteed)\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.findKey(users, { 'age': 1, 'active': true });\n     * // => 'pebbles'\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.findKey(users, ['active', false]);\n     * // => 'fred'\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.findKey(users, 'active');\n     * // => 'barney'\n     */\n    function findKey(object, predicate) {\n      return baseFind(object, getIteratee(predicate, 3), baseForOwn, true);\n    }\n\n    /**\n     * This method is like `_.findKey` except that it iterates over elements of\n     * a collection in the opposite order.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Object\n     * @param {Object} object The object to search.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per iteration.\n     * @returns {string|undefined} Returns the key of the matched element,\n     *  else `undefined`.\n     * @example\n     *\n     * var users = {\n     *   'barney':  { 'age': 36, 'active': true },\n     *   'fred':    { 'age': 40, 'active': false },\n     *   'pebbles': { 'age': 1,  'active': true }\n     * };\n     *\n     * _.findLastKey(users, function(o) { return o.age < 40; });\n     * // => returns 'pebbles' assuming `_.findKey` returns 'barney'\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.findLastKey(users, { 'age': 36, 'active': true });\n     * // => 'barney'\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.findLastKey(users, ['active', false]);\n     * // => 'fred'\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.findLastKey(users, 'active');\n     * // => 'pebbles'\n     */\n    function findLastKey(object, predicate) {\n      return baseFind(object, getIteratee(predicate, 3), baseForOwnRight, true);\n    }\n\n    /**\n     * Iterates over own and inherited enumerable string keyed properties of an\n     * object and invokes `iteratee` for each property. The iteratee is invoked\n     * with three arguments: (value, key, object). Iteratee functions may exit\n     * iteration early by explicitly returning `false`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.3.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.forIn(new Foo, function(value, key) {\n     *   console.log(key);\n     * });\n     * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).\n     */\n    function forIn(object, iteratee) {\n      return object == null\n        ? object\n        : baseFor(object, getIteratee(iteratee), keysIn);\n    }\n\n    /**\n     * This method is like `_.forIn` except that it iterates over properties of\n     * `object` in the opposite order.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.forInRight(new Foo, function(value, key) {\n     *   console.log(key);\n     * });\n     * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'.\n     */\n    function forInRight(object, iteratee) {\n      return object == null\n        ? object\n        : baseForRight(object, getIteratee(iteratee), keysIn);\n    }\n\n    /**\n     * Iterates over own enumerable string keyed properties of an object and\n     * invokes `iteratee` for each property. The iteratee is invoked with three\n     * arguments: (value, key, object). Iteratee functions may exit iteration\n     * early by explicitly returning `false`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.3.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.forOwn(new Foo, function(value, key) {\n     *   console.log(key);\n     * });\n     * // => Logs 'a' then 'b' (iteration order is not guaranteed).\n     */\n    function forOwn(object, iteratee) {\n      return object && baseForOwn(object, getIteratee(iteratee));\n    }\n\n    /**\n     * This method is like `_.forOwn` except that it iterates over properties of\n     * `object` in the opposite order.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.0.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.forOwnRight(new Foo, function(value, key) {\n     *   console.log(key);\n     * });\n     * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.\n     */\n    function forOwnRight(object, iteratee) {\n      return object && baseForOwnRight(object, getIteratee(iteratee));\n    }\n\n    /**\n     * Creates an array of function property names from own enumerable properties\n     * of `object`.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The object to inspect.\n     * @returns {Array} Returns the new array of property names.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = _.constant('a');\n     *   this.b = _.constant('b');\n     * }\n     *\n     * Foo.prototype.c = _.constant('c');\n     *\n     * _.functions(new Foo);\n     * // => ['a', 'b']\n     */\n    function functions(object) {\n      return object == null ? [] : baseFunctions(object, keys(object));\n    }\n\n    /**\n     * Creates an array of function property names from own and inherited\n     * enumerable properties of `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The object to inspect.\n     * @returns {Array} Returns the new array of property names.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = _.constant('a');\n     *   this.b = _.constant('b');\n     * }\n     *\n     * Foo.prototype.c = _.constant('c');\n     *\n     * _.functionsIn(new Foo);\n     * // => ['a', 'b', 'c']\n     */\n    function functionsIn(object) {\n      return object == null ? [] : baseFunctions(object, keysIn(object));\n    }\n\n    /**\n     * Gets the value at `path` of `object`. If the resolved value is\n     * `undefined`, the `defaultValue` is used in its place.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.7.0\n     * @category Object\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the property to get.\n     * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n     * @returns {*} Returns the resolved value.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n     *\n     * _.get(object, 'a[0].b.c');\n     * // => 3\n     *\n     * _.get(object, ['a', '0', 'b', 'c']);\n     * // => 3\n     *\n     * _.get(object, 'a.b.c', 'default');\n     * // => 'default'\n     */\n    function get(object, path, defaultValue) {\n      var result = object == null ? undefined : baseGet(object, path);\n      return result === undefined ? defaultValue : result;\n    }\n\n    /**\n     * Checks if `path` is a direct property of `object`.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path to check.\n     * @returns {boolean} Returns `true` if `path` exists, else `false`.\n     * @example\n     *\n     * var object = { 'a': { 'b': 2 } };\n     * var other = _.create({ 'a': _.create({ 'b': 2 }) });\n     *\n     * _.has(object, 'a');\n     * // => true\n     *\n     * _.has(object, 'a.b');\n     * // => true\n     *\n     * _.has(object, ['a', 'b']);\n     * // => true\n     *\n     * _.has(other, 'a');\n     * // => false\n     */\n    function has(object, path) {\n      return object != null && hasPath(object, path, baseHas);\n    }\n\n    /**\n     * Checks if `path` is a direct or inherited property of `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path to check.\n     * @returns {boolean} Returns `true` if `path` exists, else `false`.\n     * @example\n     *\n     * var object = _.create({ 'a': _.create({ 'b': 2 }) });\n     *\n     * _.hasIn(object, 'a');\n     * // => true\n     *\n     * _.hasIn(object, 'a.b');\n     * // => true\n     *\n     * _.hasIn(object, ['a', 'b']);\n     * // => true\n     *\n     * _.hasIn(object, 'b');\n     * // => false\n     */\n    function hasIn(object, path) {\n      return object != null && hasPath(object, path, baseHasIn);\n    }\n\n    /**\n     * Creates an object composed of the inverted keys and values of `object`.\n     * If `object` contains duplicate values, subsequent values overwrite\n     * property assignments of previous values.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.7.0\n     * @category Object\n     * @param {Object} object The object to invert.\n     * @returns {Object} Returns the new inverted object.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': 2, 'c': 1 };\n     *\n     * _.invert(object);\n     * // => { '1': 'c', '2': 'b' }\n     */\n    var invert = createInverter(function(result, value, key) {\n      result[value] = key;\n    }, constant(identity));\n\n    /**\n     * This method is like `_.invert` except that the inverted object is generated\n     * from the results of running each element of `object` thru `iteratee`. The\n     * corresponding inverted value of each inverted key is an array of keys\n     * responsible for generating the inverted value. The iteratee is invoked\n     * with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.1.0\n     * @category Object\n     * @param {Object} object The object to invert.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {Object} Returns the new inverted object.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': 2, 'c': 1 };\n     *\n     * _.invertBy(object);\n     * // => { '1': ['a', 'c'], '2': ['b'] }\n     *\n     * _.invertBy(object, function(value) {\n     *   return 'group' + value;\n     * });\n     * // => { 'group1': ['a', 'c'], 'group2': ['b'] }\n     */\n    var invertBy = createInverter(function(result, value, key) {\n      if (hasOwnProperty.call(result, value)) {\n        result[value].push(key);\n      } else {\n        result[value] = [key];\n      }\n    }, getIteratee);\n\n    /**\n     * Invokes the method at `path` of `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the method to invoke.\n     * @param {...*} [args] The arguments to invoke the method with.\n     * @returns {*} Returns the result of the invoked method.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };\n     *\n     * _.invoke(object, 'a[0].b.c.slice', 1, 3);\n     * // => [2, 3]\n     */\n    var invoke = rest(baseInvoke);\n\n    /**\n     * Creates an array of the own enumerable property names of `object`.\n     *\n     * **Note:** Non-object values are coerced to objects. See the\n     * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)\n     * for more details.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property names.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.keys(new Foo);\n     * // => ['a', 'b'] (iteration order is not guaranteed)\n     *\n     * _.keys('hi');\n     * // => ['0', '1']\n     */\n    function keys(object) {\n      var isProto = isPrototype(object);\n      if (!(isProto || isArrayLike(object))) {\n        return baseKeys(object);\n      }\n      var indexes = indexKeys(object),\n          skipIndexes = !!indexes,\n          result = indexes || [],\n          length = result.length;\n\n      for (var key in object) {\n        if (baseHas(object, key) &&\n            !(skipIndexes && (key == 'length' || isIndex(key, length))) &&\n            !(isProto && key == 'constructor')) {\n          result.push(key);\n        }\n      }\n      return result;\n    }\n\n    /**\n     * Creates an array of the own and inherited enumerable property names of `object`.\n     *\n     * **Note:** Non-object values are coerced to objects.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Object\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property names.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.keysIn(new Foo);\n     * // => ['a', 'b', 'c'] (iteration order is not guaranteed)\n     */\n    function keysIn(object) {\n      var index = -1,\n          isProto = isPrototype(object),\n          props = baseKeysIn(object),\n          propsLength = props.length,\n          indexes = indexKeys(object),\n          skipIndexes = !!indexes,\n          result = indexes || [],\n          length = result.length;\n\n      while (++index < propsLength) {\n        var key = props[index];\n        if (!(skipIndexes && (key == 'length' || isIndex(key, length))) &&\n            !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {\n          result.push(key);\n        }\n      }\n      return result;\n    }\n\n    /**\n     * The opposite of `_.mapValues`; this method creates an object with the\n     * same values as `object` and keys generated by running each own enumerable\n     * string keyed property of `object` thru `iteratee`. The iteratee is invoked\n     * with three arguments: (value, key, object).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.8.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Object} Returns the new mapped object.\n     * @example\n     *\n     * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {\n     *   return key + value;\n     * });\n     * // => { 'a1': 1, 'b2': 2 }\n     */\n    function mapKeys(object, iteratee) {\n      var result = {};\n      iteratee = getIteratee(iteratee, 3);\n\n      baseForOwn(object, function(value, key, object) {\n        result[iteratee(value, key, object)] = value;\n      });\n      return result;\n    }\n\n    /**\n     * Creates an object with the same keys as `object` and values generated\n     * by running each own enumerable string keyed property of `object` thru\n     * `iteratee`. The iteratee is invoked with three arguments:\n     * (value, key, object).\n     *\n     * @static\n     * @memberOf _\n     * @since 2.4.0\n     * @category Object\n     * @param {Object} object The object to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The function invoked per iteration.\n     * @returns {Object} Returns the new mapped object.\n     * @example\n     *\n     * var users = {\n     *   'fred':    { 'user': 'fred',    'age': 40 },\n     *   'pebbles': { 'user': 'pebbles', 'age': 1 }\n     * };\n     *\n     * _.mapValues(users, function(o) { return o.age; });\n     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.mapValues(users, 'age');\n     * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)\n     */\n    function mapValues(object, iteratee) {\n      var result = {};\n      iteratee = getIteratee(iteratee, 3);\n\n      baseForOwn(object, function(value, key, object) {\n        result[key] = iteratee(value, key, object);\n      });\n      return result;\n    }\n\n    /**\n     * This method is like `_.assign` except that it recursively merges own and\n     * inherited enumerable string keyed properties of source objects into the\n     * destination object. Source properties that resolve to `undefined` are\n     * skipped if a destination value exists. Array and plain object properties\n     * are merged recursively.Other objects and value types are overridden by\n     * assignment. Source objects are applied from left to right. Subsequent\n     * sources overwrite property assignments of previous sources.\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.5.0\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} [sources] The source objects.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * var users = {\n     *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]\n     * };\n     *\n     * var ages = {\n     *   'data': [{ 'age': 36 }, { 'age': 40 }]\n     * };\n     *\n     * _.merge(users, ages);\n     * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }\n     */\n    var merge = createAssigner(function(object, source, srcIndex) {\n      baseMerge(object, source, srcIndex);\n    });\n\n    /**\n     * This method is like `_.merge` except that it accepts `customizer` which\n     * is invoked to produce the merged values of the destination and source\n     * properties. If `customizer` returns `undefined`, merging is handled by the\n     * method instead. The `customizer` is invoked with seven arguments:\n     * (objValue, srcValue, key, object, source, stack).\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The destination object.\n     * @param {...Object} sources The source objects.\n     * @param {Function} customizer The function to customize assigned values.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * function customizer(objValue, srcValue) {\n     *   if (_.isArray(objValue)) {\n     *     return objValue.concat(srcValue);\n     *   }\n     * }\n     *\n     * var object = {\n     *   'fruits': ['apple'],\n     *   'vegetables': ['beet']\n     * };\n     *\n     * var other = {\n     *   'fruits': ['banana'],\n     *   'vegetables': ['carrot']\n     * };\n     *\n     * _.mergeWith(object, other, customizer);\n     * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }\n     */\n    var mergeWith = createAssigner(function(object, source, srcIndex, customizer) {\n      baseMerge(object, source, srcIndex, customizer);\n    });\n\n    /**\n     * The opposite of `_.pick`; this method creates an object composed of the\n     * own and inherited enumerable string keyed properties of `object` that are\n     * not omitted.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The source object.\n     * @param {...(string|string[])} [props] The property identifiers to omit.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': '2', 'c': 3 };\n     *\n     * _.omit(object, ['a', 'c']);\n     * // => { 'b': '2' }\n     */\n    var omit = rest(function(object, props) {\n      if (object == null) {\n        return {};\n      }\n      props = arrayMap(baseFlatten(props, 1), toKey);\n      return basePick(object, baseDifference(getAllKeysIn(object), props));\n    });\n\n    /**\n     * The opposite of `_.pickBy`; this method creates an object composed of\n     * the own and inherited enumerable string keyed properties of `object` that\n     * `predicate` doesn't return truthy for. The predicate is invoked with two\n     * arguments: (value, key).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The source object.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per property.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': '2', 'c': 3 };\n     *\n     * _.omitBy(object, _.isNumber);\n     * // => { 'b': '2' }\n     */\n    function omitBy(object, predicate) {\n      predicate = getIteratee(predicate);\n      return basePickBy(object, function(value, key) {\n        return !predicate(value, key);\n      });\n    }\n\n    /**\n     * Creates an object composed of the picked `object` properties.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The source object.\n     * @param {...(string|string[])} [props] The property identifiers to pick.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': '2', 'c': 3 };\n     *\n     * _.pick(object, ['a', 'c']);\n     * // => { 'a': 1, 'c': 3 }\n     */\n    var pick = rest(function(object, props) {\n      return object == null ? {} : basePick(object, baseFlatten(props, 1));\n    });\n\n    /**\n     * Creates an object composed of the `object` properties `predicate` returns\n     * truthy for. The predicate is invoked with two arguments: (value, key).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The source object.\n     * @param {Array|Function|Object|string} [predicate=_.identity]\n     *  The function invoked per property.\n     * @returns {Object} Returns the new object.\n     * @example\n     *\n     * var object = { 'a': 1, 'b': '2', 'c': 3 };\n     *\n     * _.pickBy(object, _.isNumber);\n     * // => { 'a': 1, 'c': 3 }\n     */\n    function pickBy(object, predicate) {\n      return object == null ? {} : basePickBy(object, getIteratee(predicate));\n    }\n\n    /**\n     * This method is like `_.get` except that if the resolved value is a\n     * function it's invoked with the `this` binding of its parent object and\n     * its result is returned.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The object to query.\n     * @param {Array|string} path The path of the property to resolve.\n     * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n     * @returns {*} Returns the resolved value.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };\n     *\n     * _.result(object, 'a[0].b.c1');\n     * // => 3\n     *\n     * _.result(object, 'a[0].b.c2');\n     * // => 4\n     *\n     * _.result(object, 'a[0].b.c3', 'default');\n     * // => 'default'\n     *\n     * _.result(object, 'a[0].b.c3', _.constant('default'));\n     * // => 'default'\n     */\n    function result(object, path, defaultValue) {\n      path = isKey(path, object) ? [path] : castPath(path);\n\n      var index = -1,\n          length = path.length;\n\n      // Ensure the loop is entered when path is empty.\n      if (!length) {\n        object = undefined;\n        length = 1;\n      }\n      while (++index < length) {\n        var value = object == null ? undefined : object[path[index]];\n        if (value === undefined) {\n          index = length;\n          value = defaultValue;\n        }\n        object = isFunction(value) ? value.call(object) : value;\n      }\n      return object;\n    }\n\n    /**\n     * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,\n     * it's created. Arrays are created for missing index properties while objects\n     * are created for all other missing properties. Use `_.setWith` to customize\n     * `path` creation.\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.7.0\n     * @category Object\n     * @param {Object} object The object to modify.\n     * @param {Array|string} path The path of the property to set.\n     * @param {*} value The value to set.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n     *\n     * _.set(object, 'a[0].b.c', 4);\n     * console.log(object.a[0].b.c);\n     * // => 4\n     *\n     * _.set(object, ['x', '0', 'y', 'z'], 5);\n     * console.log(object.x[0].y.z);\n     * // => 5\n     */\n    function set(object, path, value) {\n      return object == null ? object : baseSet(object, path, value);\n    }\n\n    /**\n     * This method is like `_.set` except that it accepts `customizer` which is\n     * invoked to produce the objects of `path`.  If `customizer` returns `undefined`\n     * path creation is handled by the method instead. The `customizer` is invoked\n     * with three arguments: (nsValue, key, nsObject).\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The object to modify.\n     * @param {Array|string} path The path of the property to set.\n     * @param {*} value The value to set.\n     * @param {Function} [customizer] The function to customize assigned values.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * var object = {};\n     *\n     * _.setWith(object, '[0][1]', 'a', Object);\n     * // => { '0': { '1': 'a' } }\n     */\n    function setWith(object, path, value, customizer) {\n      customizer = typeof customizer == 'function' ? customizer : undefined;\n      return object == null ? object : baseSet(object, path, value, customizer);\n    }\n\n    /**\n     * Creates an array of own enumerable string keyed-value pairs for `object`\n     * which can be consumed by `_.fromPairs`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @alias entries\n     * @category Object\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the new array of key-value pairs.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.toPairs(new Foo);\n     * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)\n     */\n    function toPairs(object) {\n      return baseToPairs(object, keys(object));\n    }\n\n    /**\n     * Creates an array of own and inherited enumerable string keyed-value pairs\n     * for `object` which can be consumed by `_.fromPairs`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @alias entriesIn\n     * @category Object\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the new array of key-value pairs.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.toPairsIn(new Foo);\n     * // => [['a', 1], ['b', 2], ['c', 1]] (iteration order is not guaranteed)\n     */\n    function toPairsIn(object) {\n      return baseToPairs(object, keysIn(object));\n    }\n\n    /**\n     * An alternative to `_.reduce`; this method transforms `object` to a new\n     * `accumulator` object which is the result of running each of its own\n     * enumerable string keyed properties thru `iteratee`, with each invocation\n     * potentially mutating the `accumulator` object. The iteratee is invoked\n     * with four arguments: (accumulator, value, key, object). Iteratee functions\n     * may exit iteration early by explicitly returning `false`.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.3.0\n     * @category Object\n     * @param {Array|Object} object The object to iterate over.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @param {*} [accumulator] The custom accumulator value.\n     * @returns {*} Returns the accumulated value.\n     * @example\n     *\n     * _.transform([2, 3, 4], function(result, n) {\n     *   result.push(n *= n);\n     *   return n % 2 == 0;\n     * }, []);\n     * // => [4, 9]\n     *\n     * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {\n     *   (result[value] || (result[value] = [])).push(key);\n     * }, {});\n     * // => { '1': ['a', 'c'], '2': ['b'] }\n     */\n    function transform(object, iteratee, accumulator) {\n      var isArr = isArray(object) || isTypedArray(object);\n      iteratee = getIteratee(iteratee, 4);\n\n      if (accumulator == null) {\n        if (isArr || isObject(object)) {\n          var Ctor = object.constructor;\n          if (isArr) {\n            accumulator = isArray(object) ? new Ctor : [];\n          } else {\n            accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};\n          }\n        } else {\n          accumulator = {};\n        }\n      }\n      (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) {\n        return iteratee(accumulator, value, index, object);\n      });\n      return accumulator;\n    }\n\n    /**\n     * Removes the property at `path` of `object`.\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Object\n     * @param {Object} object The object to modify.\n     * @param {Array|string} path The path of the property to unset.\n     * @returns {boolean} Returns `true` if the property is deleted, else `false`.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': 7 } }] };\n     * _.unset(object, 'a[0].b.c');\n     * // => true\n     *\n     * console.log(object);\n     * // => { 'a': [{ 'b': {} }] };\n     *\n     * _.unset(object, ['a', '0', 'b', 'c']);\n     * // => true\n     *\n     * console.log(object);\n     * // => { 'a': [{ 'b': {} }] };\n     */\n    function unset(object, path) {\n      return object == null ? true : baseUnset(object, path);\n    }\n\n    /**\n     * This method is like `_.set` except that accepts `updater` to produce the\n     * value to set. Use `_.updateWith` to customize `path` creation. The `updater`\n     * is invoked with one argument: (value).\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.6.0\n     * @category Object\n     * @param {Object} object The object to modify.\n     * @param {Array|string} path The path of the property to set.\n     * @param {Function} updater The function to produce the updated value.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n     *\n     * _.update(object, 'a[0].b.c', function(n) { return n * n; });\n     * console.log(object.a[0].b.c);\n     * // => 9\n     *\n     * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });\n     * console.log(object.x[0].y.z);\n     * // => 0\n     */\n    function update(object, path, updater) {\n      return object == null ? object : baseUpdate(object, path, castFunction(updater));\n    }\n\n    /**\n     * This method is like `_.update` except that it accepts `customizer` which is\n     * invoked to produce the objects of `path`.  If `customizer` returns `undefined`\n     * path creation is handled by the method instead. The `customizer` is invoked\n     * with three arguments: (nsValue, key, nsObject).\n     *\n     * **Note:** This method mutates `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.6.0\n     * @category Object\n     * @param {Object} object The object to modify.\n     * @param {Array|string} path The path of the property to set.\n     * @param {Function} updater The function to produce the updated value.\n     * @param {Function} [customizer] The function to customize assigned values.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * var object = {};\n     *\n     * _.updateWith(object, '[0][1]', _.constant('a'), Object);\n     * // => { '0': { '1': 'a' } }\n     */\n    function updateWith(object, path, updater, customizer) {\n      customizer = typeof customizer == 'function' ? customizer : undefined;\n      return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer);\n    }\n\n    /**\n     * Creates an array of the own enumerable string keyed property values of `object`.\n     *\n     * **Note:** Non-object values are coerced to objects.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Object\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property values.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.values(new Foo);\n     * // => [1, 2] (iteration order is not guaranteed)\n     *\n     * _.values('hi');\n     * // => ['h', 'i']\n     */\n    function values(object) {\n      return object ? baseValues(object, keys(object)) : [];\n    }\n\n    /**\n     * Creates an array of the own and inherited enumerable string keyed property\n     * values of `object`.\n     *\n     * **Note:** Non-object values are coerced to objects.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Object\n     * @param {Object} object The object to query.\n     * @returns {Array} Returns the array of property values.\n     * @example\n     *\n     * function Foo() {\n     *   this.a = 1;\n     *   this.b = 2;\n     * }\n     *\n     * Foo.prototype.c = 3;\n     *\n     * _.valuesIn(new Foo);\n     * // => [1, 2, 3] (iteration order is not guaranteed)\n     */\n    function valuesIn(object) {\n      return object == null ? [] : baseValues(object, keysIn(object));\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Clamps `number` within the inclusive `lower` and `upper` bounds.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Number\n     * @param {number} number The number to clamp.\n     * @param {number} [lower] The lower bound.\n     * @param {number} upper The upper bound.\n     * @returns {number} Returns the clamped number.\n     * @example\n     *\n     * _.clamp(-10, -5, 5);\n     * // => -5\n     *\n     * _.clamp(10, -5, 5);\n     * // => 5\n     */\n    function clamp(number, lower, upper) {\n      if (upper === undefined) {\n        upper = lower;\n        lower = undefined;\n      }\n      if (upper !== undefined) {\n        upper = toNumber(upper);\n        upper = upper === upper ? upper : 0;\n      }\n      if (lower !== undefined) {\n        lower = toNumber(lower);\n        lower = lower === lower ? lower : 0;\n      }\n      return baseClamp(toNumber(number), lower, upper);\n    }\n\n    /**\n     * Checks if `n` is between `start` and up to but not including, `end`. If\n     * `end` is not specified, it's set to `start` with `start` then set to `0`.\n     * If `start` is greater than `end` the params are swapped to support\n     * negative ranges.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.3.0\n     * @category Number\n     * @param {number} number The number to check.\n     * @param {number} [start=0] The start of the range.\n     * @param {number} end The end of the range.\n     * @returns {boolean} Returns `true` if `number` is in the range, else `false`.\n     * @example\n     *\n     * _.inRange(3, 2, 4);\n     * // => true\n     *\n     * _.inRange(4, 8);\n     * // => true\n     *\n     * _.inRange(4, 2);\n     * // => false\n     *\n     * _.inRange(2, 2);\n     * // => false\n     *\n     * _.inRange(1.2, 2);\n     * // => true\n     *\n     * _.inRange(5.2, 4);\n     * // => false\n     *\n     * _.inRange(-3, -2, -6);\n     * // => true\n     */\n    function inRange(number, start, end) {\n      start = toNumber(start) || 0;\n      if (end === undefined) {\n        end = start;\n        start = 0;\n      } else {\n        end = toNumber(end) || 0;\n      }\n      number = toNumber(number);\n      return baseInRange(number, start, end);\n    }\n\n    /**\n     * Produces a random number between the inclusive `lower` and `upper` bounds.\n     * If only one argument is provided a number between `0` and the given number\n     * is returned. If `floating` is `true`, or either `lower` or `upper` are\n     * floats, a floating-point number is returned instead of an integer.\n     *\n     * **Note:** JavaScript follows the IEEE-754 standard for resolving\n     * floating-point values which can produce unexpected results.\n     *\n     * @static\n     * @memberOf _\n     * @since 0.7.0\n     * @category Number\n     * @param {number} [lower=0] The lower bound.\n     * @param {number} [upper=1] The upper bound.\n     * @param {boolean} [floating] Specify returning a floating-point number.\n     * @returns {number} Returns the random number.\n     * @example\n     *\n     * _.random(0, 5);\n     * // => an integer between 0 and 5\n     *\n     * _.random(5);\n     * // => also an integer between 0 and 5\n     *\n     * _.random(5, true);\n     * // => a floating-point number between 0 and 5\n     *\n     * _.random(1.2, 5.2);\n     * // => a floating-point number between 1.2 and 5.2\n     */\n    function random(lower, upper, floating) {\n      if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {\n        upper = floating = undefined;\n      }\n      if (floating === undefined) {\n        if (typeof upper == 'boolean') {\n          floating = upper;\n          upper = undefined;\n        }\n        else if (typeof lower == 'boolean') {\n          floating = lower;\n          lower = undefined;\n        }\n      }\n      if (lower === undefined && upper === undefined) {\n        lower = 0;\n        upper = 1;\n      }\n      else {\n        lower = toNumber(lower) || 0;\n        if (upper === undefined) {\n          upper = lower;\n          lower = 0;\n        } else {\n          upper = toNumber(upper) || 0;\n        }\n      }\n      if (lower > upper) {\n        var temp = lower;\n        lower = upper;\n        upper = temp;\n      }\n      if (floating || lower % 1 || upper % 1) {\n        var rand = nativeRandom();\n        return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);\n      }\n      return baseRandom(lower, upper);\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the camel cased string.\n     * @example\n     *\n     * _.camelCase('Foo Bar');\n     * // => 'fooBar'\n     *\n     * _.camelCase('--foo-bar--');\n     * // => 'fooBar'\n     *\n     * _.camelCase('__FOO_BAR__');\n     * // => 'fooBar'\n     */\n    var camelCase = createCompounder(function(result, word, index) {\n      word = word.toLowerCase();\n      return result + (index ? capitalize(word) : word);\n    });\n\n    /**\n     * Converts the first character of `string` to upper case and the remaining\n     * to lower case.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to capitalize.\n     * @returns {string} Returns the capitalized string.\n     * @example\n     *\n     * _.capitalize('FRED');\n     * // => 'Fred'\n     */\n    function capitalize(string) {\n      return upperFirst(toString(string).toLowerCase());\n    }\n\n    /**\n     * Deburrs `string` by converting\n     * [latin-1 supplementary letters](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)\n     * to basic latin letters and removing\n     * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to deburr.\n     * @returns {string} Returns the deburred string.\n     * @example\n     *\n     * _.deburr('déjà vu');\n     * // => 'deja vu'\n     */\n    function deburr(string) {\n      string = toString(string);\n      return string && string.replace(reLatin1, deburrLetter).replace(reComboMark, '');\n    }\n\n    /**\n     * Checks if `string` ends with the given target string.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to search.\n     * @param {string} [target] The string to search for.\n     * @param {number} [position=string.length] The position to search from.\n     * @returns {boolean} Returns `true` if `string` ends with `target`,\n     *  else `false`.\n     * @example\n     *\n     * _.endsWith('abc', 'c');\n     * // => true\n     *\n     * _.endsWith('abc', 'b');\n     * // => false\n     *\n     * _.endsWith('abc', 'b', 2);\n     * // => true\n     */\n    function endsWith(string, target, position) {\n      string = toString(string);\n      target = typeof target == 'string' ? target : (target + '');\n\n      var length = string.length;\n      position = position === undefined\n        ? length\n        : baseClamp(toInteger(position), 0, length);\n\n      position -= target.length;\n      return position >= 0 && string.indexOf(target, position) == position;\n    }\n\n    /**\n     * Converts the characters \"&\", \"<\", \">\", '\"', \"'\", and \"\\`\" in `string` to\n     * their corresponding HTML entities.\n     *\n     * **Note:** No other characters are escaped. To escape additional\n     * characters use a third-party library like [_he_](https://mths.be/he).\n     *\n     * Though the \">\" character is escaped for symmetry, characters like\n     * \">\" and \"/\" don't need escaping in HTML and have no special meaning\n     * unless they're part of a tag or unquoted attribute value. See\n     * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)\n     * (under \"semi-related fun fact\") for more details.\n     *\n     * Backticks are escaped because in IE < 9, they can break out of\n     * attribute values or HTML comments. See [#59](https://html5sec.org/#59),\n     * [#102](https://html5sec.org/#102), [#108](https://html5sec.org/#108), and\n     * [#133](https://html5sec.org/#133) of the\n     * [HTML5 Security Cheatsheet](https://html5sec.org/) for more details.\n     *\n     * When working with HTML you should always\n     * [quote attribute values](http://wonko.com/post/html-escaping) to reduce\n     * XSS vectors.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category String\n     * @param {string} [string=''] The string to escape.\n     * @returns {string} Returns the escaped string.\n     * @example\n     *\n     * _.escape('fred, barney, & pebbles');\n     * // => 'fred, barney, &amp; pebbles'\n     */\n    function escape(string) {\n      string = toString(string);\n      return (string && reHasUnescapedHtml.test(string))\n        ? string.replace(reUnescapedHtml, escapeHtmlChar)\n        : string;\n    }\n\n    /**\n     * Escapes the `RegExp` special characters \"^\", \"$\", \"\\\", \".\", \"*\", \"+\",\n     * \"?\", \"(\", \")\", \"[\", \"]\", \"{\", \"}\", and \"|\" in `string`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to escape.\n     * @returns {string} Returns the escaped string.\n     * @example\n     *\n     * _.escapeRegExp('[lodash](https://lodash.com/)');\n     * // => '\\[lodash\\]\\(https://lodash\\.com/\\)'\n     */\n    function escapeRegExp(string) {\n      string = toString(string);\n      return (string && reHasRegExpChar.test(string))\n        ? string.replace(reRegExpChar, '\\\\$&')\n        : string;\n    }\n\n    /**\n     * Converts `string` to\n     * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the kebab cased string.\n     * @example\n     *\n     * _.kebabCase('Foo Bar');\n     * // => 'foo-bar'\n     *\n     * _.kebabCase('fooBar');\n     * // => 'foo-bar'\n     *\n     * _.kebabCase('__FOO_BAR__');\n     * // => 'foo-bar'\n     */\n    var kebabCase = createCompounder(function(result, word, index) {\n      return result + (index ? '-' : '') + word.toLowerCase();\n    });\n\n    /**\n     * Converts `string`, as space separated words, to lower case.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the lower cased string.\n     * @example\n     *\n     * _.lowerCase('--Foo-Bar--');\n     * // => 'foo bar'\n     *\n     * _.lowerCase('fooBar');\n     * // => 'foo bar'\n     *\n     * _.lowerCase('__FOO_BAR__');\n     * // => 'foo bar'\n     */\n    var lowerCase = createCompounder(function(result, word, index) {\n      return result + (index ? ' ' : '') + word.toLowerCase();\n    });\n\n    /**\n     * Converts the first character of `string` to lower case.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the converted string.\n     * @example\n     *\n     * _.lowerFirst('Fred');\n     * // => 'fred'\n     *\n     * _.lowerFirst('FRED');\n     * // => 'fRED'\n     */\n    var lowerFirst = createCaseFirst('toLowerCase');\n\n    /**\n     * Pads `string` on the left and right sides if it's shorter than `length`.\n     * Padding characters are truncated if they can't be evenly divided by `length`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to pad.\n     * @param {number} [length=0] The padding length.\n     * @param {string} [chars=' '] The string used as padding.\n     * @returns {string} Returns the padded string.\n     * @example\n     *\n     * _.pad('abc', 8);\n     * // => '  abc   '\n     *\n     * _.pad('abc', 8, '_-');\n     * // => '_-abc_-_'\n     *\n     * _.pad('abc', 3);\n     * // => 'abc'\n     */\n    function pad(string, length, chars) {\n      string = toString(string);\n      length = toInteger(length);\n\n      var strLength = length ? stringSize(string) : 0;\n      if (!length || strLength >= length) {\n        return string;\n      }\n      var mid = (length - strLength) / 2;\n      return (\n        createPadding(nativeFloor(mid), chars) +\n        string +\n        createPadding(nativeCeil(mid), chars)\n      );\n    }\n\n    /**\n     * Pads `string` on the right side if it's shorter than `length`. Padding\n     * characters are truncated if they exceed `length`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to pad.\n     * @param {number} [length=0] The padding length.\n     * @param {string} [chars=' '] The string used as padding.\n     * @returns {string} Returns the padded string.\n     * @example\n     *\n     * _.padEnd('abc', 6);\n     * // => 'abc   '\n     *\n     * _.padEnd('abc', 6, '_-');\n     * // => 'abc_-_'\n     *\n     * _.padEnd('abc', 3);\n     * // => 'abc'\n     */\n    function padEnd(string, length, chars) {\n      string = toString(string);\n      length = toInteger(length);\n\n      var strLength = length ? stringSize(string) : 0;\n      return (length && strLength < length)\n        ? (string + createPadding(length - strLength, chars))\n        : string;\n    }\n\n    /**\n     * Pads `string` on the left side if it's shorter than `length`. Padding\n     * characters are truncated if they exceed `length`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to pad.\n     * @param {number} [length=0] The padding length.\n     * @param {string} [chars=' '] The string used as padding.\n     * @returns {string} Returns the padded string.\n     * @example\n     *\n     * _.padStart('abc', 6);\n     * // => '   abc'\n     *\n     * _.padStart('abc', 6, '_-');\n     * // => '_-_abc'\n     *\n     * _.padStart('abc', 3);\n     * // => 'abc'\n     */\n    function padStart(string, length, chars) {\n      string = toString(string);\n      length = toInteger(length);\n\n      var strLength = length ? stringSize(string) : 0;\n      return (length && strLength < length)\n        ? (createPadding(length - strLength, chars) + string)\n        : string;\n    }\n\n    /**\n     * Converts `string` to an integer of the specified radix. If `radix` is\n     * `undefined` or `0`, a `radix` of `10` is used unless `value` is a\n     * hexadecimal, in which case a `radix` of `16` is used.\n     *\n     * **Note:** This method aligns with the\n     * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`.\n     *\n     * @static\n     * @memberOf _\n     * @since 1.1.0\n     * @category String\n     * @param {string} string The string to convert.\n     * @param {number} [radix=10] The radix to interpret `value` by.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {number} Returns the converted integer.\n     * @example\n     *\n     * _.parseInt('08');\n     * // => 8\n     *\n     * _.map(['6', '08', '10'], _.parseInt);\n     * // => [6, 8, 10]\n     */\n    function parseInt(string, radix, guard) {\n      // Chrome fails to trim leading <BOM> whitespace characters.\n      // See https://bugs.chromium.org/p/v8/issues/detail?id=3109 for more details.\n      if (guard || radix == null) {\n        radix = 0;\n      } else if (radix) {\n        radix = +radix;\n      }\n      string = toString(string).replace(reTrim, '');\n      return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10));\n    }\n\n    /**\n     * Repeats the given string `n` times.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to repeat.\n     * @param {number} [n=1] The number of times to repeat the string.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {string} Returns the repeated string.\n     * @example\n     *\n     * _.repeat('*', 3);\n     * // => '***'\n     *\n     * _.repeat('abc', 2);\n     * // => 'abcabc'\n     *\n     * _.repeat('abc', 0);\n     * // => ''\n     */\n    function repeat(string, n, guard) {\n      if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {\n        n = 1;\n      } else {\n        n = toInteger(n);\n      }\n      return baseRepeat(toString(string), n);\n    }\n\n    /**\n     * Replaces matches for `pattern` in `string` with `replacement`.\n     *\n     * **Note:** This method is based on\n     * [`String#replace`](https://mdn.io/String/replace).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to modify.\n     * @param {RegExp|string} pattern The pattern to replace.\n     * @param {Function|string} replacement The match replacement.\n     * @returns {string} Returns the modified string.\n     * @example\n     *\n     * _.replace('Hi Fred', 'Fred', 'Barney');\n     * // => 'Hi Barney'\n     */\n    function replace() {\n      var args = arguments,\n          string = toString(args[0]);\n\n      return args.length < 3 ? string : nativeReplace.call(string, args[1], args[2]);\n    }\n\n    /**\n     * Converts `string` to\n     * [snake case](https://en.wikipedia.org/wiki/Snake_case).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the snake cased string.\n     * @example\n     *\n     * _.snakeCase('Foo Bar');\n     * // => 'foo_bar'\n     *\n     * _.snakeCase('fooBar');\n     * // => 'foo_bar'\n     *\n     * _.snakeCase('--FOO-BAR--');\n     * // => 'foo_bar'\n     */\n    var snakeCase = createCompounder(function(result, word, index) {\n      return result + (index ? '_' : '') + word.toLowerCase();\n    });\n\n    /**\n     * Splits `string` by `separator`.\n     *\n     * **Note:** This method is based on\n     * [`String#split`](https://mdn.io/String/split).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to split.\n     * @param {RegExp|string} separator The separator pattern to split by.\n     * @param {number} [limit] The length to truncate results to.\n     * @returns {Array} Returns the new array of string segments.\n     * @example\n     *\n     * _.split('a-b-c', '-', 2);\n     * // => ['a', 'b']\n     */\n    function split(string, separator, limit) {\n      if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) {\n        separator = limit = undefined;\n      }\n      limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0;\n      if (!limit) {\n        return [];\n      }\n      string = toString(string);\n      if (string && (\n            typeof separator == 'string' ||\n            (separator != null && !isRegExp(separator))\n          )) {\n        separator += '';\n        if (separator == '' && reHasComplexSymbol.test(string)) {\n          return castSlice(stringToArray(string), 0, limit);\n        }\n      }\n      return nativeSplit.call(string, separator, limit);\n    }\n\n    /**\n     * Converts `string` to\n     * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).\n     *\n     * @static\n     * @memberOf _\n     * @since 3.1.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the start cased string.\n     * @example\n     *\n     * _.startCase('--foo-bar--');\n     * // => 'Foo Bar'\n     *\n     * _.startCase('fooBar');\n     * // => 'Foo Bar'\n     *\n     * _.startCase('__FOO_BAR__');\n     * // => 'FOO BAR'\n     */\n    var startCase = createCompounder(function(result, word, index) {\n      return result + (index ? ' ' : '') + upperFirst(word);\n    });\n\n    /**\n     * Checks if `string` starts with the given target string.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to search.\n     * @param {string} [target] The string to search for.\n     * @param {number} [position=0] The position to search from.\n     * @returns {boolean} Returns `true` if `string` starts with `target`,\n     *  else `false`.\n     * @example\n     *\n     * _.startsWith('abc', 'a');\n     * // => true\n     *\n     * _.startsWith('abc', 'b');\n     * // => false\n     *\n     * _.startsWith('abc', 'b', 1);\n     * // => true\n     */\n    function startsWith(string, target, position) {\n      string = toString(string);\n      position = baseClamp(toInteger(position), 0, string.length);\n      return string.lastIndexOf(target, position) == position;\n    }\n\n    /**\n     * Creates a compiled template function that can interpolate data properties\n     * in \"interpolate\" delimiters, HTML-escape interpolated data properties in\n     * \"escape\" delimiters, and execute JavaScript in \"evaluate\" delimiters. Data\n     * properties may be accessed as free variables in the template. If a setting\n     * object is given, it takes precedence over `_.templateSettings` values.\n     *\n     * **Note:** In the development build `_.template` utilizes\n     * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)\n     * for easier debugging.\n     *\n     * For more information on precompiling templates see\n     * [lodash's custom builds documentation](https://lodash.com/custom-builds).\n     *\n     * For more information on Chrome extension sandboxes see\n     * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category String\n     * @param {string} [string=''] The template string.\n     * @param {Object} [options={}] The options object.\n     * @param {RegExp} [options.escape=_.templateSettings.escape]\n     *  The HTML \"escape\" delimiter.\n     * @param {RegExp} [options.evaluate=_.templateSettings.evaluate]\n     *  The \"evaluate\" delimiter.\n     * @param {Object} [options.imports=_.templateSettings.imports]\n     *  An object to import into the template as free variables.\n     * @param {RegExp} [options.interpolate=_.templateSettings.interpolate]\n     *  The \"interpolate\" delimiter.\n     * @param {string} [options.sourceURL='lodash.templateSources[n]']\n     *  The sourceURL of the compiled template.\n     * @param {string} [options.variable='obj']\n     *  The data object variable name.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Function} Returns the compiled template function.\n     * @example\n     *\n     * // Use the \"interpolate\" delimiter to create a compiled template.\n     * var compiled = _.template('hello <%= user %>!');\n     * compiled({ 'user': 'fred' });\n     * // => 'hello fred!'\n     *\n     * // Use the HTML \"escape\" delimiter to escape data property values.\n     * var compiled = _.template('<b><%- value %></b>');\n     * compiled({ 'value': '<script>' });\n     * // => '<b>&lt;script&gt;</b>'\n     *\n     * // Use the \"evaluate\" delimiter to execute JavaScript and generate HTML.\n     * var compiled = _.template('<% _.forEach(users, function(user) { %><li><%- user %></li><% }); %>');\n     * compiled({ 'users': ['fred', 'barney'] });\n     * // => '<li>fred</li><li>barney</li>'\n     *\n     * // Use the internal `print` function in \"evaluate\" delimiters.\n     * var compiled = _.template('<% print(\"hello \" + user); %>!');\n     * compiled({ 'user': 'barney' });\n     * // => 'hello barney!'\n     *\n     * // Use the ES delimiter as an alternative to the default \"interpolate\" delimiter.\n     * var compiled = _.template('hello ${ user }!');\n     * compiled({ 'user': 'pebbles' });\n     * // => 'hello pebbles!'\n     *\n     * // Use custom template delimiters.\n     * _.templateSettings.interpolate = /{{([\\s\\S]+?)}}/g;\n     * var compiled = _.template('hello {{ user }}!');\n     * compiled({ 'user': 'mustache' });\n     * // => 'hello mustache!'\n     *\n     * // Use backslashes to treat delimiters as plain text.\n     * var compiled = _.template('<%= \"\\\\<%- value %\\\\>\" %>');\n     * compiled({ 'value': 'ignored' });\n     * // => '<%- value %>'\n     *\n     * // Use the `imports` option to import `jQuery` as `jq`.\n     * var text = '<% jq.each(users, function(user) { %><li><%- user %></li><% }); %>';\n     * var compiled = _.template(text, { 'imports': { 'jq': jQuery } });\n     * compiled({ 'users': ['fred', 'barney'] });\n     * // => '<li>fred</li><li>barney</li>'\n     *\n     * // Use the `sourceURL` option to specify a custom sourceURL for the template.\n     * var compiled = _.template('hello <%= user %>!', { 'sourceURL': '/basic/greeting.jst' });\n     * compiled(data);\n     * // => Find the source of \"greeting.jst\" under the Sources tab or Resources panel of the web inspector.\n     *\n     * // Use the `variable` option to ensure a with-statement isn't used in the compiled template.\n     * var compiled = _.template('hi <%= data.user %>!', { 'variable': 'data' });\n     * compiled.source;\n     * // => function(data) {\n     * //   var __t, __p = '';\n     * //   __p += 'hi ' + ((__t = ( data.user )) == null ? '' : __t) + '!';\n     * //   return __p;\n     * // }\n     *\n     * // Use the `source` property to inline compiled templates for meaningful\n     * // line numbers in error messages and stack traces.\n     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\\\n     *   var JST = {\\\n     *     \"main\": ' + _.template(mainText).source + '\\\n     *   };\\\n     * ');\n     */\n    function template(string, options, guard) {\n      // Based on John Resig's `tmpl` implementation\n      // (http://ejohn.org/blog/javascript-micro-templating/)\n      // and Laura Doktorova's doT.js (https://github.com/olado/doT).\n      var settings = lodash.templateSettings;\n\n      if (guard && isIterateeCall(string, options, guard)) {\n        options = undefined;\n      }\n      string = toString(string);\n      options = assignInWith({}, options, settings, assignInDefaults);\n\n      var imports = assignInWith({}, options.imports, settings.imports, assignInDefaults),\n          importsKeys = keys(imports),\n          importsValues = baseValues(imports, importsKeys);\n\n      var isEscaping,\n          isEvaluating,\n          index = 0,\n          interpolate = options.interpolate || reNoMatch,\n          source = \"__p += '\";\n\n      // Compile the regexp to match each delimiter.\n      var reDelimiters = RegExp(\n        (options.escape || reNoMatch).source + '|' +\n        interpolate.source + '|' +\n        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +\n        (options.evaluate || reNoMatch).source + '|$'\n      , 'g');\n\n      // Use a sourceURL for easier debugging.\n      var sourceURL = '//# sourceURL=' +\n        ('sourceURL' in options\n          ? options.sourceURL\n          : ('lodash.templateSources[' + (++templateCounter) + ']')\n        ) + '\\n';\n\n      string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {\n        interpolateValue || (interpolateValue = esTemplateValue);\n\n        // Escape characters that can't be included in string literals.\n        source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);\n\n        // Replace delimiters with snippets.\n        if (escapeValue) {\n          isEscaping = true;\n          source += \"' +\\n__e(\" + escapeValue + \") +\\n'\";\n        }\n        if (evaluateValue) {\n          isEvaluating = true;\n          source += \"';\\n\" + evaluateValue + \";\\n__p += '\";\n        }\n        if (interpolateValue) {\n          source += \"' +\\n((__t = (\" + interpolateValue + \")) == null ? '' : __t) +\\n'\";\n        }\n        index = offset + match.length;\n\n        // The JS engine embedded in Adobe products needs `match` returned in\n        // order to produce the correct `offset` value.\n        return match;\n      });\n\n      source += \"';\\n\";\n\n      // If `variable` is not specified wrap a with-statement around the generated\n      // code to add the data object to the top of the scope chain.\n      var variable = options.variable;\n      if (!variable) {\n        source = 'with (obj) {\\n' + source + '\\n}\\n';\n      }\n      // Cleanup code by stripping empty strings.\n      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)\n        .replace(reEmptyStringMiddle, '$1')\n        .replace(reEmptyStringTrailing, '$1;');\n\n      // Frame code as the function body.\n      source = 'function(' + (variable || 'obj') + ') {\\n' +\n        (variable\n          ? ''\n          : 'obj || (obj = {});\\n'\n        ) +\n        \"var __t, __p = ''\" +\n        (isEscaping\n           ? ', __e = _.escape'\n           : ''\n        ) +\n        (isEvaluating\n          ? ', __j = Array.prototype.join;\\n' +\n            \"function print() { __p += __j.call(arguments, '') }\\n\"\n          : ';\\n'\n        ) +\n        source +\n        'return __p\\n}';\n\n      var result = attempt(function() {\n        return Function(importsKeys, sourceURL + 'return ' + source)\n          .apply(undefined, importsValues);\n      });\n\n      // Provide the compiled function's source by its `toString` method or\n      // the `source` property as a convenience for inlining compiled templates.\n      result.source = source;\n      if (isError(result)) {\n        throw result;\n      }\n      return result;\n    }\n\n    /**\n     * Converts `string`, as a whole, to lower case just like\n     * [String#toLowerCase](https://mdn.io/toLowerCase).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the lower cased string.\n     * @example\n     *\n     * _.toLower('--Foo-Bar--');\n     * // => '--foo-bar--'\n     *\n     * _.toLower('fooBar');\n     * // => 'foobar'\n     *\n     * _.toLower('__FOO_BAR__');\n     * // => '__foo_bar__'\n     */\n    function toLower(value) {\n      return toString(value).toLowerCase();\n    }\n\n    /**\n     * Converts `string`, as a whole, to upper case just like\n     * [String#toUpperCase](https://mdn.io/toUpperCase).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the upper cased string.\n     * @example\n     *\n     * _.toUpper('--foo-bar--');\n     * // => '--FOO-BAR--'\n     *\n     * _.toUpper('fooBar');\n     * // => 'FOOBAR'\n     *\n     * _.toUpper('__foo_bar__');\n     * // => '__FOO_BAR__'\n     */\n    function toUpper(value) {\n      return toString(value).toUpperCase();\n    }\n\n    /**\n     * Removes leading and trailing whitespace or specified characters from `string`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to trim.\n     * @param {string} [chars=whitespace] The characters to trim.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {string} Returns the trimmed string.\n     * @example\n     *\n     * _.trim('  abc  ');\n     * // => 'abc'\n     *\n     * _.trim('-_-abc-_-', '_-');\n     * // => 'abc'\n     *\n     * _.map(['  foo  ', '  bar  '], _.trim);\n     * // => ['foo', 'bar']\n     */\n    function trim(string, chars, guard) {\n      string = toString(string);\n      if (!string) {\n        return string;\n      }\n      if (guard || chars === undefined) {\n        return string.replace(reTrim, '');\n      }\n      if (!(chars += '')) {\n        return string;\n      }\n      var strSymbols = stringToArray(string),\n          chrSymbols = stringToArray(chars),\n          start = charsStartIndex(strSymbols, chrSymbols),\n          end = charsEndIndex(strSymbols, chrSymbols) + 1;\n\n      return castSlice(strSymbols, start, end).join('');\n    }\n\n    /**\n     * Removes trailing whitespace or specified characters from `string`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to trim.\n     * @param {string} [chars=whitespace] The characters to trim.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {string} Returns the trimmed string.\n     * @example\n     *\n     * _.trimEnd('  abc  ');\n     * // => '  abc'\n     *\n     * _.trimEnd('-_-abc-_-', '_-');\n     * // => '-_-abc'\n     */\n    function trimEnd(string, chars, guard) {\n      string = toString(string);\n      if (!string) {\n        return string;\n      }\n      if (guard || chars === undefined) {\n        return string.replace(reTrimEnd, '');\n      }\n      if (!(chars += '')) {\n        return string;\n      }\n      var strSymbols = stringToArray(string),\n          end = charsEndIndex(strSymbols, stringToArray(chars)) + 1;\n\n      return castSlice(strSymbols, 0, end).join('');\n    }\n\n    /**\n     * Removes leading whitespace or specified characters from `string`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to trim.\n     * @param {string} [chars=whitespace] The characters to trim.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {string} Returns the trimmed string.\n     * @example\n     *\n     * _.trimStart('  abc  ');\n     * // => 'abc  '\n     *\n     * _.trimStart('-_-abc-_-', '_-');\n     * // => 'abc-_-'\n     */\n    function trimStart(string, chars, guard) {\n      string = toString(string);\n      if (!string) {\n        return string;\n      }\n      if (guard || chars === undefined) {\n        return string.replace(reTrimStart, '');\n      }\n      if (!(chars += '')) {\n        return string;\n      }\n      var strSymbols = stringToArray(string),\n          start = charsStartIndex(strSymbols, stringToArray(chars));\n\n      return castSlice(strSymbols, start).join('');\n    }\n\n    /**\n     * Truncates `string` if it's longer than the given maximum string length.\n     * The last characters of the truncated string are replaced with the omission\n     * string which defaults to \"...\".\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to truncate.\n     * @param {Object} [options={}] The options object.\n     * @param {number} [options.length=30] The maximum string length.\n     * @param {string} [options.omission='...'] The string to indicate text is omitted.\n     * @param {RegExp|string} [options.separator] The separator pattern to truncate to.\n     * @returns {string} Returns the truncated string.\n     * @example\n     *\n     * _.truncate('hi-diddly-ho there, neighborino');\n     * // => 'hi-diddly-ho there, neighbo...'\n     *\n     * _.truncate('hi-diddly-ho there, neighborino', {\n     *   'length': 24,\n     *   'separator': ' '\n     * });\n     * // => 'hi-diddly-ho there,...'\n     *\n     * _.truncate('hi-diddly-ho there, neighborino', {\n     *   'length': 24,\n     *   'separator': /,? +/\n     * });\n     * // => 'hi-diddly-ho there...'\n     *\n     * _.truncate('hi-diddly-ho there, neighborino', {\n     *   'omission': ' [...]'\n     * });\n     * // => 'hi-diddly-ho there, neig [...]'\n     */\n    function truncate(string, options) {\n      var length = DEFAULT_TRUNC_LENGTH,\n          omission = DEFAULT_TRUNC_OMISSION;\n\n      if (isObject(options)) {\n        var separator = 'separator' in options ? options.separator : separator;\n        length = 'length' in options ? toInteger(options.length) : length;\n        omission = 'omission' in options ? toString(options.omission) : omission;\n      }\n      string = toString(string);\n\n      var strLength = string.length;\n      if (reHasComplexSymbol.test(string)) {\n        var strSymbols = stringToArray(string);\n        strLength = strSymbols.length;\n      }\n      if (length >= strLength) {\n        return string;\n      }\n      var end = length - stringSize(omission);\n      if (end < 1) {\n        return omission;\n      }\n      var result = strSymbols\n        ? castSlice(strSymbols, 0, end).join('')\n        : string.slice(0, end);\n\n      if (separator === undefined) {\n        return result + omission;\n      }\n      if (strSymbols) {\n        end += (result.length - end);\n      }\n      if (isRegExp(separator)) {\n        if (string.slice(end).search(separator)) {\n          var match,\n              substring = result;\n\n          if (!separator.global) {\n            separator = RegExp(separator.source, toString(reFlags.exec(separator)) + 'g');\n          }\n          separator.lastIndex = 0;\n          while ((match = separator.exec(substring))) {\n            var newEnd = match.index;\n          }\n          result = result.slice(0, newEnd === undefined ? end : newEnd);\n        }\n      } else if (string.indexOf(separator, end) != end) {\n        var index = result.lastIndexOf(separator);\n        if (index > -1) {\n          result = result.slice(0, index);\n        }\n      }\n      return result + omission;\n    }\n\n    /**\n     * The inverse of `_.escape`; this method converts the HTML entities\n     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, `&#39;`, and `&#96;` in `string` to\n     * their corresponding characters.\n     *\n     * **Note:** No other HTML entities are unescaped. To unescape additional\n     * HTML entities use a third-party library like [_he_](https://mths.be/he).\n     *\n     * @static\n     * @memberOf _\n     * @since 0.6.0\n     * @category String\n     * @param {string} [string=''] The string to unescape.\n     * @returns {string} Returns the unescaped string.\n     * @example\n     *\n     * _.unescape('fred, barney, &amp; pebbles');\n     * // => 'fred, barney, & pebbles'\n     */\n    function unescape(string) {\n      string = toString(string);\n      return (string && reHasEscapedHtml.test(string))\n        ? string.replace(reEscapedHtml, unescapeHtmlChar)\n        : string;\n    }\n\n    /**\n     * Converts `string`, as space separated words, to upper case.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the upper cased string.\n     * @example\n     *\n     * _.upperCase('--foo-bar');\n     * // => 'FOO BAR'\n     *\n     * _.upperCase('fooBar');\n     * // => 'FOO BAR'\n     *\n     * _.upperCase('__foo_bar__');\n     * // => 'FOO BAR'\n     */\n    var upperCase = createCompounder(function(result, word, index) {\n      return result + (index ? ' ' : '') + word.toUpperCase();\n    });\n\n    /**\n     * Converts the first character of `string` to upper case.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category String\n     * @param {string} [string=''] The string to convert.\n     * @returns {string} Returns the converted string.\n     * @example\n     *\n     * _.upperFirst('fred');\n     * // => 'Fred'\n     *\n     * _.upperFirst('FRED');\n     * // => 'FRED'\n     */\n    var upperFirst = createCaseFirst('toUpperCase');\n\n    /**\n     * Splits `string` into an array of its words.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category String\n     * @param {string} [string=''] The string to inspect.\n     * @param {RegExp|string} [pattern] The pattern to match words.\n     * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n     * @returns {Array} Returns the words of `string`.\n     * @example\n     *\n     * _.words('fred, barney, & pebbles');\n     * // => ['fred', 'barney', 'pebbles']\n     *\n     * _.words('fred, barney, & pebbles', /[^, ]+/g);\n     * // => ['fred', 'barney', '&', 'pebbles']\n     */\n    function words(string, pattern, guard) {\n      string = toString(string);\n      pattern = guard ? undefined : pattern;\n\n      if (pattern === undefined) {\n        pattern = reHasComplexWord.test(string) ? reComplexWord : reBasicWord;\n      }\n      return string.match(pattern) || [];\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Attempts to invoke `func`, returning either the result or the caught error\n     * object. Any additional arguments are provided to `func` when it's invoked.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Util\n     * @param {Function} func The function to attempt.\n     * @param {...*} [args] The arguments to invoke `func` with.\n     * @returns {*} Returns the `func` result or error object.\n     * @example\n     *\n     * // Avoid throwing errors for invalid selectors.\n     * var elements = _.attempt(function(selector) {\n     *   return document.querySelectorAll(selector);\n     * }, '>_>');\n     *\n     * if (_.isError(elements)) {\n     *   elements = [];\n     * }\n     */\n    var attempt = rest(function(func, args) {\n      try {\n        return apply(func, undefined, args);\n      } catch (e) {\n        return isError(e) ? e : new Error(e);\n      }\n    });\n\n    /**\n     * Binds methods of an object to the object itself, overwriting the existing\n     * method.\n     *\n     * **Note:** This method doesn't set the \"length\" property of bound functions.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {Object} object The object to bind and assign the bound methods to.\n     * @param {...(string|string[])} methodNames The object method names to bind.\n     * @returns {Object} Returns `object`.\n     * @example\n     *\n     * var view = {\n     *   'label': 'docs',\n     *   'onClick': function() {\n     *     console.log('clicked ' + this.label);\n     *   }\n     * };\n     *\n     * _.bindAll(view, 'onClick');\n     * jQuery(element).on('click', view.onClick);\n     * // => Logs 'clicked docs' when clicked.\n     */\n    var bindAll = rest(function(object, methodNames) {\n      arrayEach(baseFlatten(methodNames, 1), function(key) {\n        object[key] = bind(object[key], object);\n      });\n      return object;\n    });\n\n    /**\n     * Creates a function that iterates over `pairs` and invokes the corresponding\n     * function of the first predicate to return truthy. The predicate-function\n     * pairs are invoked with the `this` binding and arguments of the created\n     * function.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {Array} pairs The predicate-function pairs.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var func = _.cond([\n     *   [_.matches({ 'a': 1 }),           _.constant('matches A')],\n     *   [_.conforms({ 'b': _.isNumber }), _.constant('matches B')],\n     *   [_.constant(true),                _.constant('no match')]\n     * ]);\n     *\n     * func({ 'a': 1, 'b': 2 });\n     * // => 'matches A'\n     *\n     * func({ 'a': 0, 'b': 1 });\n     * // => 'matches B'\n     *\n     * func({ 'a': '1', 'b': '2' });\n     * // => 'no match'\n     */\n    function cond(pairs) {\n      var length = pairs ? pairs.length : 0,\n          toIteratee = getIteratee();\n\n      pairs = !length ? [] : arrayMap(pairs, function(pair) {\n        if (typeof pair[1] != 'function') {\n          throw new TypeError(FUNC_ERROR_TEXT);\n        }\n        return [toIteratee(pair[0]), pair[1]];\n      });\n\n      return rest(function(args) {\n        var index = -1;\n        while (++index < length) {\n          var pair = pairs[index];\n          if (apply(pair[0], this, args)) {\n            return apply(pair[1], this, args);\n          }\n        }\n      });\n    }\n\n    /**\n     * Creates a function that invokes the predicate properties of `source` with\n     * the corresponding property values of a given object, returning `true` if\n     * all predicates return truthy, else `false`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {Object} source The object of property predicates to conform to.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36 },\n     *   { 'user': 'fred',   'age': 40 }\n     * ];\n     *\n     * _.filter(users, _.conforms({ 'age': _.partial(_.gt, _, 38) }));\n     * // => [{ 'user': 'fred', 'age': 40 }]\n     */\n    function conforms(source) {\n      return baseConforms(baseClone(source, true));\n    }\n\n    /**\n     * Creates a function that returns `value`.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.4.0\n     * @category Util\n     * @param {*} value The value to return from the new function.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var object = { 'user': 'fred' };\n     * var getter = _.constant(object);\n     *\n     * getter() === object;\n     * // => true\n     */\n    function constant(value) {\n      return function() {\n        return value;\n      };\n    }\n\n    /**\n     * Creates a function that returns the result of invoking the given functions\n     * with the `this` binding of the created function, where each successive\n     * invocation is supplied the return value of the previous.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Util\n     * @param {...(Function|Function[])} [funcs] Functions to invoke.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * function square(n) {\n     *   return n * n;\n     * }\n     *\n     * var addSquare = _.flow(_.add, square);\n     * addSquare(1, 2);\n     * // => 9\n     */\n    var flow = createFlow();\n\n    /**\n     * This method is like `_.flow` except that it creates a function that\n     * invokes the given functions from right to left.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {...(Function|Function[])} [funcs] Functions to invoke.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * function square(n) {\n     *   return n * n;\n     * }\n     *\n     * var addSquare = _.flowRight(square, _.add);\n     * addSquare(1, 2);\n     * // => 9\n     */\n    var flowRight = createFlow(true);\n\n    /**\n     * This method returns the first argument given to it.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {*} value Any value.\n     * @returns {*} Returns `value`.\n     * @example\n     *\n     * var object = { 'user': 'fred' };\n     *\n     * _.identity(object) === object;\n     * // => true\n     */\n    function identity(value) {\n      return value;\n    }\n\n    /**\n     * Creates a function that invokes `func` with the arguments of the created\n     * function. If `func` is a property name, the created function returns the\n     * property value for a given element. If `func` is an array or object, the\n     * created function returns `true` for elements that contain the equivalent\n     * source properties, otherwise it returns `false`.\n     *\n     * @static\n     * @since 4.0.0\n     * @memberOf _\n     * @category Util\n     * @param {*} [func=_.identity] The value to convert to a callback.\n     * @returns {Function} Returns the callback.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36, 'active': true },\n     *   { 'user': 'fred',   'age': 40, 'active': false }\n     * ];\n     *\n     * // The `_.matches` iteratee shorthand.\n     * _.filter(users, _.iteratee({ 'user': 'barney', 'active': true }));\n     * // => [{ 'user': 'barney', 'age': 36, 'active': true }]\n     *\n     * // The `_.matchesProperty` iteratee shorthand.\n     * _.filter(users, _.iteratee(['user', 'fred']));\n     * // => [{ 'user': 'fred', 'age': 40 }]\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.map(users, _.iteratee('user'));\n     * // => ['barney', 'fred']\n     *\n     * // Create custom iteratee shorthands.\n     * _.iteratee = _.wrap(_.iteratee, function(iteratee, func) {\n     *   return !_.isRegExp(func) ? iteratee(func) : function(string) {\n     *     return func.test(string);\n     *   };\n     * });\n     *\n     * _.filter(['abc', 'def'], /ef/);\n     * // => ['def']\n     */\n    function iteratee(func) {\n      return baseIteratee(typeof func == 'function' ? func : baseClone(func, true));\n    }\n\n    /**\n     * Creates a function that performs a partial deep comparison between a given\n     * object and `source`, returning `true` if the given object has equivalent\n     * property values, else `false`. The created function is equivalent to\n     * `_.isMatch` with a `source` partially applied.\n     *\n     * **Note:** This method supports comparing the same values as `_.isEqual`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Util\n     * @param {Object} source The object of property values to match.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney', 'age': 36, 'active': true },\n     *   { 'user': 'fred',   'age': 40, 'active': false }\n     * ];\n     *\n     * _.filter(users, _.matches({ 'age': 40, 'active': false }));\n     * // => [{ 'user': 'fred', 'age': 40, 'active': false }]\n     */\n    function matches(source) {\n      return baseMatches(baseClone(source, true));\n    }\n\n    /**\n     * Creates a function that performs a partial deep comparison between the\n     * value at `path` of a given object to `srcValue`, returning `true` if the\n     * object value is equivalent, else `false`.\n     *\n     * **Note:** This method supports comparing the same values as `_.isEqual`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.2.0\n     * @category Util\n     * @param {Array|string} path The path of the property to get.\n     * @param {*} srcValue The value to match.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var users = [\n     *   { 'user': 'barney' },\n     *   { 'user': 'fred' }\n     * ];\n     *\n     * _.find(users, _.matchesProperty('user', 'fred'));\n     * // => { 'user': 'fred' }\n     */\n    function matchesProperty(path, srcValue) {\n      return baseMatchesProperty(path, baseClone(srcValue, true));\n    }\n\n    /**\n     * Creates a function that invokes the method at `path` of a given object.\n     * Any additional arguments are provided to the invoked method.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.7.0\n     * @category Util\n     * @param {Array|string} path The path of the method to invoke.\n     * @param {...*} [args] The arguments to invoke the method with.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var objects = [\n     *   { 'a': { 'b': _.constant(2) } },\n     *   { 'a': { 'b': _.constant(1) } }\n     * ];\n     *\n     * _.map(objects, _.method('a.b'));\n     * // => [2, 1]\n     *\n     * _.map(objects, _.method(['a', 'b']));\n     * // => [2, 1]\n     */\n    var method = rest(function(path, args) {\n      return function(object) {\n        return baseInvoke(object, path, args);\n      };\n    });\n\n    /**\n     * The opposite of `_.method`; this method creates a function that invokes\n     * the method at a given path of `object`. Any additional arguments are\n     * provided to the invoked method.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.7.0\n     * @category Util\n     * @param {Object} object The object to query.\n     * @param {...*} [args] The arguments to invoke the method with.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var array = _.times(3, _.constant),\n     *     object = { 'a': array, 'b': array, 'c': array };\n     *\n     * _.map(['a[2]', 'c[0]'], _.methodOf(object));\n     * // => [2, 0]\n     *\n     * _.map([['a', '2'], ['c', '0']], _.methodOf(object));\n     * // => [2, 0]\n     */\n    var methodOf = rest(function(object, args) {\n      return function(path) {\n        return baseInvoke(object, path, args);\n      };\n    });\n\n    /**\n     * Adds all own enumerable string keyed function properties of a source\n     * object to the destination object. If `object` is a function, then methods\n     * are added to its prototype as well.\n     *\n     * **Note:** Use `_.runInContext` to create a pristine `lodash` function to\n     * avoid conflicts caused by modifying the original.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {Function|Object} [object=lodash] The destination object.\n     * @param {Object} source The object of functions to add.\n     * @param {Object} [options={}] The options object.\n     * @param {boolean} [options.chain=true] Specify whether mixins are chainable.\n     * @returns {Function|Object} Returns `object`.\n     * @example\n     *\n     * function vowels(string) {\n     *   return _.filter(string, function(v) {\n     *     return /[aeiou]/i.test(v);\n     *   });\n     * }\n     *\n     * _.mixin({ 'vowels': vowels });\n     * _.vowels('fred');\n     * // => ['e']\n     *\n     * _('fred').vowels().value();\n     * // => ['e']\n     *\n     * _.mixin({ 'vowels': vowels }, { 'chain': false });\n     * _('fred').vowels();\n     * // => ['e']\n     */\n    function mixin(object, source, options) {\n      var props = keys(source),\n          methodNames = baseFunctions(source, props);\n\n      if (options == null &&\n          !(isObject(source) && (methodNames.length || !props.length))) {\n        options = source;\n        source = object;\n        object = this;\n        methodNames = baseFunctions(source, keys(source));\n      }\n      var chain = !(isObject(options) && 'chain' in options) || !!options.chain,\n          isFunc = isFunction(object);\n\n      arrayEach(methodNames, function(methodName) {\n        var func = source[methodName];\n        object[methodName] = func;\n        if (isFunc) {\n          object.prototype[methodName] = function() {\n            var chainAll = this.__chain__;\n            if (chain || chainAll) {\n              var result = object(this.__wrapped__),\n                  actions = result.__actions__ = copyArray(this.__actions__);\n\n              actions.push({ 'func': func, 'args': arguments, 'thisArg': object });\n              result.__chain__ = chainAll;\n              return result;\n            }\n            return func.apply(object, arrayPush([this.value()], arguments));\n          };\n        }\n      });\n\n      return object;\n    }\n\n    /**\n     * Reverts the `_` variable to its previous value and returns a reference to\n     * the `lodash` function.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @returns {Function} Returns the `lodash` function.\n     * @example\n     *\n     * var lodash = _.noConflict();\n     */\n    function noConflict() {\n      if (root._ === this) {\n        root._ = oldDash;\n      }\n      return this;\n    }\n\n    /**\n     * A no-operation function that returns `undefined` regardless of the\n     * arguments it receives.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.3.0\n     * @category Util\n     * @example\n     *\n     * var object = { 'user': 'fred' };\n     *\n     * _.noop(object) === undefined;\n     * // => true\n     */\n    function noop() {\n      // No operation performed.\n    }\n\n    /**\n     * Creates a function that returns its nth argument. If `n` is negative,\n     * the nth argument from the end is returned.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {number} [n=0] The index of the argument to return.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var func = _.nthArg(1);\n     * func('a', 'b', 'c', 'd');\n     * // => 'b'\n     *\n     * var func = _.nthArg(-2);\n     * func('a', 'b', 'c', 'd');\n     * // => 'c'\n     */\n    function nthArg(n) {\n      n = toInteger(n);\n      return rest(function(args) {\n        return baseNth(args, n);\n      });\n    }\n\n    /**\n     * Creates a function that invokes `iteratees` with the arguments it receives\n     * and returns their results.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {...(Array|Array[]|Function|Function[]|Object|Object[]|string|string[])}\n     *  [iteratees=[_.identity]] The iteratees to invoke.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var func = _.over(Math.max, Math.min);\n     *\n     * func(1, 2, 3, 4);\n     * // => [4, 1]\n     */\n    var over = createOver(arrayMap);\n\n    /**\n     * Creates a function that checks if **all** of the `predicates` return\n     * truthy when invoked with the arguments it receives.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {...(Array|Array[]|Function|Function[]|Object|Object[]|string|string[])}\n     *  [predicates=[_.identity]] The predicates to check.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var func = _.overEvery(Boolean, isFinite);\n     *\n     * func('1');\n     * // => true\n     *\n     * func(null);\n     * // => false\n     *\n     * func(NaN);\n     * // => false\n     */\n    var overEvery = createOver(arrayEvery);\n\n    /**\n     * Creates a function that checks if **any** of the `predicates` return\n     * truthy when invoked with the arguments it receives.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {...(Array|Array[]|Function|Function[]|Object|Object[]|string|string[])}\n     *  [predicates=[_.identity]] The predicates to check.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var func = _.overSome(Boolean, isFinite);\n     *\n     * func('1');\n     * // => true\n     *\n     * func(null);\n     * // => true\n     *\n     * func(NaN);\n     * // => false\n     */\n    var overSome = createOver(arraySome);\n\n    /**\n     * Creates a function that returns the value at `path` of a given object.\n     *\n     * @static\n     * @memberOf _\n     * @since 2.4.0\n     * @category Util\n     * @param {Array|string} path The path of the property to get.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var objects = [\n     *   { 'a': { 'b': 2 } },\n     *   { 'a': { 'b': 1 } }\n     * ];\n     *\n     * _.map(objects, _.property('a.b'));\n     * // => [2, 1]\n     *\n     * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');\n     * // => [1, 2]\n     */\n    function property(path) {\n      return isKey(path) ? baseProperty(path) : basePropertyDeep(path);\n    }\n\n    /**\n     * The opposite of `_.property`; this method creates a function that returns\n     * the value at a given path of `object`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.0.0\n     * @category Util\n     * @param {Object} object The object to query.\n     * @returns {Function} Returns the new function.\n     * @example\n     *\n     * var array = [0, 1, 2],\n     *     object = { 'a': array, 'b': array, 'c': array };\n     *\n     * _.map(['a[2]', 'c[0]'], _.propertyOf(object));\n     * // => [2, 0]\n     *\n     * _.map([['a', '2'], ['c', '0']], _.propertyOf(object));\n     * // => [2, 0]\n     */\n    function propertyOf(object) {\n      return function(path) {\n        return object == null ? undefined : baseGet(object, path);\n      };\n    }\n\n    /**\n     * Creates an array of numbers (positive and/or negative) progressing from\n     * `start` up to, but not including, `end`. A step of `-1` is used if a negative\n     * `start` is specified without an `end` or `step`. If `end` is not specified,\n     * it's set to `start` with `start` then set to `0`.\n     *\n     * **Note:** JavaScript follows the IEEE-754 standard for resolving\n     * floating-point values which can produce unexpected results.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {number} [start=0] The start of the range.\n     * @param {number} end The end of the range.\n     * @param {number} [step=1] The value to increment or decrement by.\n     * @returns {Array} Returns the new array of numbers.\n     * @example\n     *\n     * _.range(4);\n     * // => [0, 1, 2, 3]\n     *\n     * _.range(-4);\n     * // => [0, -1, -2, -3]\n     *\n     * _.range(1, 5);\n     * // => [1, 2, 3, 4]\n     *\n     * _.range(0, 20, 5);\n     * // => [0, 5, 10, 15]\n     *\n     * _.range(0, -4, -1);\n     * // => [0, -1, -2, -3]\n     *\n     * _.range(1, 4, 0);\n     * // => [1, 1, 1]\n     *\n     * _.range(0);\n     * // => []\n     */\n    var range = createRange();\n\n    /**\n     * This method is like `_.range` except that it populates values in\n     * descending order.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {number} [start=0] The start of the range.\n     * @param {number} end The end of the range.\n     * @param {number} [step=1] The value to increment or decrement by.\n     * @returns {Array} Returns the new array of numbers.\n     * @example\n     *\n     * _.rangeRight(4);\n     * // => [3, 2, 1, 0]\n     *\n     * _.rangeRight(-4);\n     * // => [-3, -2, -1, 0]\n     *\n     * _.rangeRight(1, 5);\n     * // => [4, 3, 2, 1]\n     *\n     * _.rangeRight(0, 20, 5);\n     * // => [15, 10, 5, 0]\n     *\n     * _.rangeRight(0, -4, -1);\n     * // => [-3, -2, -1, 0]\n     *\n     * _.rangeRight(1, 4, 0);\n     * // => [1, 1, 1]\n     *\n     * _.rangeRight(0);\n     * // => []\n     */\n    var rangeRight = createRange(true);\n\n    /**\n     * Invokes the iteratee `n` times, returning an array of the results of\n     * each invocation. The iteratee is invoked with one argument; (index).\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {number} n The number of times to invoke `iteratee`.\n     * @param {Function} [iteratee=_.identity] The function invoked per iteration.\n     * @returns {Array} Returns the array of results.\n     * @example\n     *\n     * _.times(3, String);\n     * // => ['0', '1', '2']\n     *\n     *  _.times(4, _.constant(true));\n     * // => [true, true, true, true]\n     */\n    function times(n, iteratee) {\n      n = toInteger(n);\n      if (n < 1 || n > MAX_SAFE_INTEGER) {\n        return [];\n      }\n      var index = MAX_ARRAY_LENGTH,\n          length = nativeMin(n, MAX_ARRAY_LENGTH);\n\n      iteratee = getIteratee(iteratee);\n      n -= MAX_ARRAY_LENGTH;\n\n      var result = baseTimes(length, iteratee);\n      while (++index < n) {\n        iteratee(index);\n      }\n      return result;\n    }\n\n    /**\n     * Converts `value` to a property path array.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Util\n     * @param {*} value The value to convert.\n     * @returns {Array} Returns the new property path array.\n     * @example\n     *\n     * _.toPath('a.b.c');\n     * // => ['a', 'b', 'c']\n     *\n     * _.toPath('a[0].b.c');\n     * // => ['a', '0', 'b', 'c']\n     *\n     * var path = ['a', 'b', 'c'],\n     *     newPath = _.toPath(path);\n     *\n     * console.log(newPath);\n     * // => ['a', 'b', 'c']\n     *\n     * console.log(path === newPath);\n     * // => false\n     */\n    function toPath(value) {\n      if (isArray(value)) {\n        return arrayMap(value, toKey);\n      }\n      return isSymbol(value) ? [value] : copyArray(stringToPath(value));\n    }\n\n    /**\n     * Generates a unique ID. If `prefix` is given, the ID is appended to it.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Util\n     * @param {string} [prefix=''] The value to prefix the ID with.\n     * @returns {string} Returns the unique ID.\n     * @example\n     *\n     * _.uniqueId('contact_');\n     * // => 'contact_104'\n     *\n     * _.uniqueId();\n     * // => '105'\n     */\n    function uniqueId(prefix) {\n      var id = ++idCounter;\n      return toString(prefix) + id;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Adds two numbers.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.4.0\n     * @category Math\n     * @param {number} augend The first number in an addition.\n     * @param {number} addend The second number in an addition.\n     * @returns {number} Returns the total.\n     * @example\n     *\n     * _.add(6, 4);\n     * // => 10\n     */\n    var add = createMathOperation(function(augend, addend) {\n      return augend + addend;\n    });\n\n    /**\n     * Computes `number` rounded up to `precision`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.10.0\n     * @category Math\n     * @param {number} number The number to round up.\n     * @param {number} [precision=0] The precision to round up to.\n     * @returns {number} Returns the rounded up number.\n     * @example\n     *\n     * _.ceil(4.006);\n     * // => 5\n     *\n     * _.ceil(6.004, 2);\n     * // => 6.01\n     *\n     * _.ceil(6040, -2);\n     * // => 6100\n     */\n    var ceil = createRound('ceil');\n\n    /**\n     * Divide two numbers.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.7.0\n     * @category Math\n     * @param {number} dividend The first number in a division.\n     * @param {number} divisor The second number in a division.\n     * @returns {number} Returns the quotient.\n     * @example\n     *\n     * _.divide(6, 4);\n     * // => 1.5\n     */\n    var divide = createMathOperation(function(dividend, divisor) {\n      return dividend / divisor;\n    });\n\n    /**\n     * Computes `number` rounded down to `precision`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.10.0\n     * @category Math\n     * @param {number} number The number to round down.\n     * @param {number} [precision=0] The precision to round down to.\n     * @returns {number} Returns the rounded down number.\n     * @example\n     *\n     * _.floor(4.006);\n     * // => 4\n     *\n     * _.floor(0.046, 2);\n     * // => 0.04\n     *\n     * _.floor(4060, -2);\n     * // => 4000\n     */\n    var floor = createRound('floor');\n\n    /**\n     * Computes the maximum value of `array`. If `array` is empty or falsey,\n     * `undefined` is returned.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @returns {*} Returns the maximum value.\n     * @example\n     *\n     * _.max([4, 2, 8, 6]);\n     * // => 8\n     *\n     * _.max([]);\n     * // => undefined\n     */\n    function max(array) {\n      return (array && array.length)\n        ? baseExtremum(array, identity, gt)\n        : undefined;\n    }\n\n    /**\n     * This method is like `_.max` except that it accepts `iteratee` which is\n     * invoked for each element in `array` to generate the criterion by which\n     * the value is ranked. The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {*} Returns the maximum value.\n     * @example\n     *\n     * var objects = [{ 'n': 1 }, { 'n': 2 }];\n     *\n     * _.maxBy(objects, function(o) { return o.n; });\n     * // => { 'n': 2 }\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.maxBy(objects, 'n');\n     * // => { 'n': 2 }\n     */\n    function maxBy(array, iteratee) {\n      return (array && array.length)\n        ? baseExtremum(array, getIteratee(iteratee), gt)\n        : undefined;\n    }\n\n    /**\n     * Computes the mean of the values in `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @returns {number} Returns the mean.\n     * @example\n     *\n     * _.mean([4, 2, 8, 6]);\n     * // => 5\n     */\n    function mean(array) {\n      return baseMean(array, identity);\n    }\n\n    /**\n     * This method is like `_.mean` except that it accepts `iteratee` which is\n     * invoked for each element in `array` to generate the value to be averaged.\n     * The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.7.0\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {number} Returns the mean.\n     * @example\n     *\n     * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];\n     *\n     * _.meanBy(objects, function(o) { return o.n; });\n     * // => 5\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.meanBy(objects, 'n');\n     * // => 5\n     */\n    function meanBy(array, iteratee) {\n      return baseMean(array, getIteratee(iteratee));\n    }\n\n    /**\n     * Computes the minimum value of `array`. If `array` is empty or falsey,\n     * `undefined` is returned.\n     *\n     * @static\n     * @since 0.1.0\n     * @memberOf _\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @returns {*} Returns the minimum value.\n     * @example\n     *\n     * _.min([4, 2, 8, 6]);\n     * // => 2\n     *\n     * _.min([]);\n     * // => undefined\n     */\n    function min(array) {\n      return (array && array.length)\n        ? baseExtremum(array, identity, lt)\n        : undefined;\n    }\n\n    /**\n     * This method is like `_.min` except that it accepts `iteratee` which is\n     * invoked for each element in `array` to generate the criterion by which\n     * the value is ranked. The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {*} Returns the minimum value.\n     * @example\n     *\n     * var objects = [{ 'n': 1 }, { 'n': 2 }];\n     *\n     * _.minBy(objects, function(o) { return o.n; });\n     * // => { 'n': 1 }\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.minBy(objects, 'n');\n     * // => { 'n': 1 }\n     */\n    function minBy(array, iteratee) {\n      return (array && array.length)\n        ? baseExtremum(array, getIteratee(iteratee), lt)\n        : undefined;\n    }\n\n    /**\n     * Multiply two numbers.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.7.0\n     * @category Math\n     * @param {number} multiplier The first number in a multiplication.\n     * @param {number} multiplicand The second number in a multiplication.\n     * @returns {number} Returns the product.\n     * @example\n     *\n     * _.multiply(6, 4);\n     * // => 24\n     */\n    var multiply = createMathOperation(function(multiplier, multiplicand) {\n      return multiplier * multiplicand;\n    });\n\n    /**\n     * Computes `number` rounded to `precision`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.10.0\n     * @category Math\n     * @param {number} number The number to round.\n     * @param {number} [precision=0] The precision to round to.\n     * @returns {number} Returns the rounded number.\n     * @example\n     *\n     * _.round(4.006);\n     * // => 4\n     *\n     * _.round(4.006, 2);\n     * // => 4.01\n     *\n     * _.round(4060, -2);\n     * // => 4100\n     */\n    var round = createRound('round');\n\n    /**\n     * Subtract two numbers.\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Math\n     * @param {number} minuend The first number in a subtraction.\n     * @param {number} subtrahend The second number in a subtraction.\n     * @returns {number} Returns the difference.\n     * @example\n     *\n     * _.subtract(6, 4);\n     * // => 2\n     */\n    var subtract = createMathOperation(function(minuend, subtrahend) {\n      return minuend - subtrahend;\n    });\n\n    /**\n     * Computes the sum of the values in `array`.\n     *\n     * @static\n     * @memberOf _\n     * @since 3.4.0\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @returns {number} Returns the sum.\n     * @example\n     *\n     * _.sum([4, 2, 8, 6]);\n     * // => 20\n     */\n    function sum(array) {\n      return (array && array.length)\n        ? baseSum(array, identity)\n        : 0;\n    }\n\n    /**\n     * This method is like `_.sum` except that it accepts `iteratee` which is\n     * invoked for each element in `array` to generate the value to be summed.\n     * The iteratee is invoked with one argument: (value).\n     *\n     * @static\n     * @memberOf _\n     * @since 4.0.0\n     * @category Math\n     * @param {Array} array The array to iterate over.\n     * @param {Array|Function|Object|string} [iteratee=_.identity]\n     *  The iteratee invoked per element.\n     * @returns {number} Returns the sum.\n     * @example\n     *\n     * var objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }];\n     *\n     * _.sumBy(objects, function(o) { return o.n; });\n     * // => 20\n     *\n     * // The `_.property` iteratee shorthand.\n     * _.sumBy(objects, 'n');\n     * // => 20\n     */\n    function sumBy(array, iteratee) {\n      return (array && array.length)\n        ? baseSum(array, getIteratee(iteratee))\n        : 0;\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    // Add methods that return wrapped values in chain sequences.\n    lodash.after = after;\n    lodash.ary = ary;\n    lodash.assign = assign;\n    lodash.assignIn = assignIn;\n    lodash.assignInWith = assignInWith;\n    lodash.assignWith = assignWith;\n    lodash.at = at;\n    lodash.before = before;\n    lodash.bind = bind;\n    lodash.bindAll = bindAll;\n    lodash.bindKey = bindKey;\n    lodash.castArray = castArray;\n    lodash.chain = chain;\n    lodash.chunk = chunk;\n    lodash.compact = compact;\n    lodash.concat = concat;\n    lodash.cond = cond;\n    lodash.conforms = conforms;\n    lodash.constant = constant;\n    lodash.countBy = countBy;\n    lodash.create = create;\n    lodash.curry = curry;\n    lodash.curryRight = curryRight;\n    lodash.debounce = debounce;\n    lodash.defaults = defaults;\n    lodash.defaultsDeep = defaultsDeep;\n    lodash.defer = defer;\n    lodash.delay = delay;\n    lodash.difference = difference;\n    lodash.differenceBy = differenceBy;\n    lodash.differenceWith = differenceWith;\n    lodash.drop = drop;\n    lodash.dropRight = dropRight;\n    lodash.dropRightWhile = dropRightWhile;\n    lodash.dropWhile = dropWhile;\n    lodash.fill = fill;\n    lodash.filter = filter;\n    lodash.flatMap = flatMap;\n    lodash.flatMapDeep = flatMapDeep;\n    lodash.flatMapDepth = flatMapDepth;\n    lodash.flatten = flatten;\n    lodash.flattenDeep = flattenDeep;\n    lodash.flattenDepth = flattenDepth;\n    lodash.flip = flip;\n    lodash.flow = flow;\n    lodash.flowRight = flowRight;\n    lodash.fromPairs = fromPairs;\n    lodash.functions = functions;\n    lodash.functionsIn = functionsIn;\n    lodash.groupBy = groupBy;\n    lodash.initial = initial;\n    lodash.intersection = intersection;\n    lodash.intersectionBy = intersectionBy;\n    lodash.intersectionWith = intersectionWith;\n    lodash.invert = invert;\n    lodash.invertBy = invertBy;\n    lodash.invokeMap = invokeMap;\n    lodash.iteratee = iteratee;\n    lodash.keyBy = keyBy;\n    lodash.keys = keys;\n    lodash.keysIn = keysIn;\n    lodash.map = map;\n    lodash.mapKeys = mapKeys;\n    lodash.mapValues = mapValues;\n    lodash.matches = matches;\n    lodash.matchesProperty = matchesProperty;\n    lodash.memoize = memoize;\n    lodash.merge = merge;\n    lodash.mergeWith = mergeWith;\n    lodash.method = method;\n    lodash.methodOf = methodOf;\n    lodash.mixin = mixin;\n    lodash.negate = negate;\n    lodash.nthArg = nthArg;\n    lodash.omit = omit;\n    lodash.omitBy = omitBy;\n    lodash.once = once;\n    lodash.orderBy = orderBy;\n    lodash.over = over;\n    lodash.overArgs = overArgs;\n    lodash.overEvery = overEvery;\n    lodash.overSome = overSome;\n    lodash.partial = partial;\n    lodash.partialRight = partialRight;\n    lodash.partition = partition;\n    lodash.pick = pick;\n    lodash.pickBy = pickBy;\n    lodash.property = property;\n    lodash.propertyOf = propertyOf;\n    lodash.pull = pull;\n    lodash.pullAll = pullAll;\n    lodash.pullAllBy = pullAllBy;\n    lodash.pullAllWith = pullAllWith;\n    lodash.pullAt = pullAt;\n    lodash.range = range;\n    lodash.rangeRight = rangeRight;\n    lodash.rearg = rearg;\n    lodash.reject = reject;\n    lodash.remove = remove;\n    lodash.rest = rest;\n    lodash.reverse = reverse;\n    lodash.sampleSize = sampleSize;\n    lodash.set = set;\n    lodash.setWith = setWith;\n    lodash.shuffle = shuffle;\n    lodash.slice = slice;\n    lodash.sortBy = sortBy;\n    lodash.sortedUniq = sortedUniq;\n    lodash.sortedUniqBy = sortedUniqBy;\n    lodash.split = split;\n    lodash.spread = spread;\n    lodash.tail = tail;\n    lodash.take = take;\n    lodash.takeRight = takeRight;\n    lodash.takeRightWhile = takeRightWhile;\n    lodash.takeWhile = takeWhile;\n    lodash.tap = tap;\n    lodash.throttle = throttle;\n    lodash.thru = thru;\n    lodash.toArray = toArray;\n    lodash.toPairs = toPairs;\n    lodash.toPairsIn = toPairsIn;\n    lodash.toPath = toPath;\n    lodash.toPlainObject = toPlainObject;\n    lodash.transform = transform;\n    lodash.unary = unary;\n    lodash.union = union;\n    lodash.unionBy = unionBy;\n    lodash.unionWith = unionWith;\n    lodash.uniq = uniq;\n    lodash.uniqBy = uniqBy;\n    lodash.uniqWith = uniqWith;\n    lodash.unset = unset;\n    lodash.unzip = unzip;\n    lodash.unzipWith = unzipWith;\n    lodash.update = update;\n    lodash.updateWith = updateWith;\n    lodash.values = values;\n    lodash.valuesIn = valuesIn;\n    lodash.without = without;\n    lodash.words = words;\n    lodash.wrap = wrap;\n    lodash.xor = xor;\n    lodash.xorBy = xorBy;\n    lodash.xorWith = xorWith;\n    lodash.zip = zip;\n    lodash.zipObject = zipObject;\n    lodash.zipObjectDeep = zipObjectDeep;\n    lodash.zipWith = zipWith;\n\n    // Add aliases.\n    lodash.entries = toPairs;\n    lodash.entriesIn = toPairsIn;\n    lodash.extend = assignIn;\n    lodash.extendWith = assignInWith;\n\n    // Add methods to `lodash.prototype`.\n    mixin(lodash, lodash);\n\n    /*------------------------------------------------------------------------*/\n\n    // Add methods that return unwrapped values in chain sequences.\n    lodash.add = add;\n    lodash.attempt = attempt;\n    lodash.camelCase = camelCase;\n    lodash.capitalize = capitalize;\n    lodash.ceil = ceil;\n    lodash.clamp = clamp;\n    lodash.clone = clone;\n    lodash.cloneDeep = cloneDeep;\n    lodash.cloneDeepWith = cloneDeepWith;\n    lodash.cloneWith = cloneWith;\n    lodash.deburr = deburr;\n    lodash.divide = divide;\n    lodash.endsWith = endsWith;\n    lodash.eq = eq;\n    lodash.escape = escape;\n    lodash.escapeRegExp = escapeRegExp;\n    lodash.every = every;\n    lodash.find = find;\n    lodash.findIndex = findIndex;\n    lodash.findKey = findKey;\n    lodash.findLast = findLast;\n    lodash.findLastIndex = findLastIndex;\n    lodash.findLastKey = findLastKey;\n    lodash.floor = floor;\n    lodash.forEach = forEach;\n    lodash.forEachRight = forEachRight;\n    lodash.forIn = forIn;\n    lodash.forInRight = forInRight;\n    lodash.forOwn = forOwn;\n    lodash.forOwnRight = forOwnRight;\n    lodash.get = get;\n    lodash.gt = gt;\n    lodash.gte = gte;\n    lodash.has = has;\n    lodash.hasIn = hasIn;\n    lodash.head = head;\n    lodash.identity = identity;\n    lodash.includes = includes;\n    lodash.indexOf = indexOf;\n    lodash.inRange = inRange;\n    lodash.invoke = invoke;\n    lodash.isArguments = isArguments;\n    lodash.isArray = isArray;\n    lodash.isArrayBuffer = isArrayBuffer;\n    lodash.isArrayLike = isArrayLike;\n    lodash.isArrayLikeObject = isArrayLikeObject;\n    lodash.isBoolean = isBoolean;\n    lodash.isBuffer = isBuffer;\n    lodash.isDate = isDate;\n    lodash.isElement = isElement;\n    lodash.isEmpty = isEmpty;\n    lodash.isEqual = isEqual;\n    lodash.isEqualWith = isEqualWith;\n    lodash.isError = isError;\n    lodash.isFinite = isFinite;\n    lodash.isFunction = isFunction;\n    lodash.isInteger = isInteger;\n    lodash.isLength = isLength;\n    lodash.isMap = isMap;\n    lodash.isMatch = isMatch;\n    lodash.isMatchWith = isMatchWith;\n    lodash.isNaN = isNaN;\n    lodash.isNative = isNative;\n    lodash.isNil = isNil;\n    lodash.isNull = isNull;\n    lodash.isNumber = isNumber;\n    lodash.isObject = isObject;\n    lodash.isObjectLike = isObjectLike;\n    lodash.isPlainObject = isPlainObject;\n    lodash.isRegExp = isRegExp;\n    lodash.isSafeInteger = isSafeInteger;\n    lodash.isSet = isSet;\n    lodash.isString = isString;\n    lodash.isSymbol = isSymbol;\n    lodash.isTypedArray = isTypedArray;\n    lodash.isUndefined = isUndefined;\n    lodash.isWeakMap = isWeakMap;\n    lodash.isWeakSet = isWeakSet;\n    lodash.join = join;\n    lodash.kebabCase = kebabCase;\n    lodash.last = last;\n    lodash.lastIndexOf = lastIndexOf;\n    lodash.lowerCase = lowerCase;\n    lodash.lowerFirst = lowerFirst;\n    lodash.lt = lt;\n    lodash.lte = lte;\n    lodash.max = max;\n    lodash.maxBy = maxBy;\n    lodash.mean = mean;\n    lodash.meanBy = meanBy;\n    lodash.min = min;\n    lodash.minBy = minBy;\n    lodash.multiply = multiply;\n    lodash.nth = nth;\n    lodash.noConflict = noConflict;\n    lodash.noop = noop;\n    lodash.now = now;\n    lodash.pad = pad;\n    lodash.padEnd = padEnd;\n    lodash.padStart = padStart;\n    lodash.parseInt = parseInt;\n    lodash.random = random;\n    lodash.reduce = reduce;\n    lodash.reduceRight = reduceRight;\n    lodash.repeat = repeat;\n    lodash.replace = replace;\n    lodash.result = result;\n    lodash.round = round;\n    lodash.runInContext = runInContext;\n    lodash.sample = sample;\n    lodash.size = size;\n    lodash.snakeCase = snakeCase;\n    lodash.some = some;\n    lodash.sortedIndex = sortedIndex;\n    lodash.sortedIndexBy = sortedIndexBy;\n    lodash.sortedIndexOf = sortedIndexOf;\n    lodash.sortedLastIndex = sortedLastIndex;\n    lodash.sortedLastIndexBy = sortedLastIndexBy;\n    lodash.sortedLastIndexOf = sortedLastIndexOf;\n    lodash.startCase = startCase;\n    lodash.startsWith = startsWith;\n    lodash.subtract = subtract;\n    lodash.sum = sum;\n    lodash.sumBy = sumBy;\n    lodash.template = template;\n    lodash.times = times;\n    lodash.toInteger = toInteger;\n    lodash.toLength = toLength;\n    lodash.toLower = toLower;\n    lodash.toNumber = toNumber;\n    lodash.toSafeInteger = toSafeInteger;\n    lodash.toString = toString;\n    lodash.toUpper = toUpper;\n    lodash.trim = trim;\n    lodash.trimEnd = trimEnd;\n    lodash.trimStart = trimStart;\n    lodash.truncate = truncate;\n    lodash.unescape = unescape;\n    lodash.uniqueId = uniqueId;\n    lodash.upperCase = upperCase;\n    lodash.upperFirst = upperFirst;\n\n    // Add aliases.\n    lodash.each = forEach;\n    lodash.eachRight = forEachRight;\n    lodash.first = head;\n\n    mixin(lodash, (function() {\n      var source = {};\n      baseForOwn(lodash, function(func, methodName) {\n        if (!hasOwnProperty.call(lodash.prototype, methodName)) {\n          source[methodName] = func;\n        }\n      });\n      return source;\n    }()), { 'chain': false });\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * The semantic version number.\n     *\n     * @static\n     * @memberOf _\n     * @type {string}\n     */\n    lodash.VERSION = VERSION;\n\n    // Assign default placeholders.\n    arrayEach(['bind', 'bindKey', 'curry', 'curryRight', 'partial', 'partialRight'], function(methodName) {\n      lodash[methodName].placeholder = lodash;\n    });\n\n    // Add `LazyWrapper` methods for `_.drop` and `_.take` variants.\n    arrayEach(['drop', 'take'], function(methodName, index) {\n      LazyWrapper.prototype[methodName] = function(n) {\n        var filtered = this.__filtered__;\n        if (filtered && !index) {\n          return new LazyWrapper(this);\n        }\n        n = n === undefined ? 1 : nativeMax(toInteger(n), 0);\n\n        var result = this.clone();\n        if (filtered) {\n          result.__takeCount__ = nativeMin(n, result.__takeCount__);\n        } else {\n          result.__views__.push({\n            'size': nativeMin(n, MAX_ARRAY_LENGTH),\n            'type': methodName + (result.__dir__ < 0 ? 'Right' : '')\n          });\n        }\n        return result;\n      };\n\n      LazyWrapper.prototype[methodName + 'Right'] = function(n) {\n        return this.reverse()[methodName](n).reverse();\n      };\n    });\n\n    // Add `LazyWrapper` methods that accept an `iteratee` value.\n    arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {\n      var type = index + 1,\n          isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;\n\n      LazyWrapper.prototype[methodName] = function(iteratee) {\n        var result = this.clone();\n        result.__iteratees__.push({\n          'iteratee': getIteratee(iteratee, 3),\n          'type': type\n        });\n        result.__filtered__ = result.__filtered__ || isFilter;\n        return result;\n      };\n    });\n\n    // Add `LazyWrapper` methods for `_.head` and `_.last`.\n    arrayEach(['head', 'last'], function(methodName, index) {\n      var takeName = 'take' + (index ? 'Right' : '');\n\n      LazyWrapper.prototype[methodName] = function() {\n        return this[takeName](1).value()[0];\n      };\n    });\n\n    // Add `LazyWrapper` methods for `_.initial` and `_.tail`.\n    arrayEach(['initial', 'tail'], function(methodName, index) {\n      var dropName = 'drop' + (index ? '' : 'Right');\n\n      LazyWrapper.prototype[methodName] = function() {\n        return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);\n      };\n    });\n\n    LazyWrapper.prototype.compact = function() {\n      return this.filter(identity);\n    };\n\n    LazyWrapper.prototype.find = function(predicate) {\n      return this.filter(predicate).head();\n    };\n\n    LazyWrapper.prototype.findLast = function(predicate) {\n      return this.reverse().find(predicate);\n    };\n\n    LazyWrapper.prototype.invokeMap = rest(function(path, args) {\n      if (typeof path == 'function') {\n        return new LazyWrapper(this);\n      }\n      return this.map(function(value) {\n        return baseInvoke(value, path, args);\n      });\n    });\n\n    LazyWrapper.prototype.reject = function(predicate) {\n      predicate = getIteratee(predicate, 3);\n      return this.filter(function(value) {\n        return !predicate(value);\n      });\n    };\n\n    LazyWrapper.prototype.slice = function(start, end) {\n      start = toInteger(start);\n\n      var result = this;\n      if (result.__filtered__ && (start > 0 || end < 0)) {\n        return new LazyWrapper(result);\n      }\n      if (start < 0) {\n        result = result.takeRight(-start);\n      } else if (start) {\n        result = result.drop(start);\n      }\n      if (end !== undefined) {\n        end = toInteger(end);\n        result = end < 0 ? result.dropRight(-end) : result.take(end - start);\n      }\n      return result;\n    };\n\n    LazyWrapper.prototype.takeRightWhile = function(predicate) {\n      return this.reverse().takeWhile(predicate).reverse();\n    };\n\n    LazyWrapper.prototype.toArray = function() {\n      return this.take(MAX_ARRAY_LENGTH);\n    };\n\n    // Add `LazyWrapper` methods to `lodash.prototype`.\n    baseForOwn(LazyWrapper.prototype, function(func, methodName) {\n      var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),\n          isTaker = /^(?:head|last)$/.test(methodName),\n          lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],\n          retUnwrapped = isTaker || /^find/.test(methodName);\n\n      if (!lodashFunc) {\n        return;\n      }\n      lodash.prototype[methodName] = function() {\n        var value = this.__wrapped__,\n            args = isTaker ? [1] : arguments,\n            isLazy = value instanceof LazyWrapper,\n            iteratee = args[0],\n            useLazy = isLazy || isArray(value);\n\n        var interceptor = function(value) {\n          var result = lodashFunc.apply(lodash, arrayPush([value], args));\n          return (isTaker && chainAll) ? result[0] : result;\n        };\n\n        if (useLazy && checkIteratee && typeof iteratee == 'function' && iteratee.length != 1) {\n          // Avoid lazy use if the iteratee has a \"length\" value other than `1`.\n          isLazy = useLazy = false;\n        }\n        var chainAll = this.__chain__,\n            isHybrid = !!this.__actions__.length,\n            isUnwrapped = retUnwrapped && !chainAll,\n            onlyLazy = isLazy && !isHybrid;\n\n        if (!retUnwrapped && useLazy) {\n          value = onlyLazy ? value : new LazyWrapper(this);\n          var result = func.apply(value, args);\n          result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });\n          return new LodashWrapper(result, chainAll);\n        }\n        if (isUnwrapped && onlyLazy) {\n          return func.apply(this, args);\n        }\n        result = this.thru(interceptor);\n        return isUnwrapped ? (isTaker ? result.value()[0] : result.value()) : result;\n      };\n    });\n\n    // Add `Array` methods to `lodash.prototype`.\n    arrayEach(['pop', 'push', 'shift', 'sort', 'splice', 'unshift'], function(methodName) {\n      var func = arrayProto[methodName],\n          chainName = /^(?:push|sort|unshift)$/.test(methodName) ? 'tap' : 'thru',\n          retUnwrapped = /^(?:pop|shift)$/.test(methodName);\n\n      lodash.prototype[methodName] = function() {\n        var args = arguments;\n        if (retUnwrapped && !this.__chain__) {\n          var value = this.value();\n          return func.apply(isArray(value) ? value : [], args);\n        }\n        return this[chainName](function(value) {\n          return func.apply(isArray(value) ? value : [], args);\n        });\n      };\n    });\n\n    // Map minified method names to their real names.\n    baseForOwn(LazyWrapper.prototype, function(func, methodName) {\n      var lodashFunc = lodash[methodName];\n      if (lodashFunc) {\n        var key = (lodashFunc.name + ''),\n            names = realNames[key] || (realNames[key] = []);\n\n        names.push({ 'name': methodName, 'func': lodashFunc });\n      }\n    });\n\n    realNames[createHybridWrapper(undefined, BIND_KEY_FLAG).name] = [{\n      'name': 'wrapper',\n      'func': undefined\n    }];\n\n    // Add methods to `LazyWrapper`.\n    LazyWrapper.prototype.clone = lazyClone;\n    LazyWrapper.prototype.reverse = lazyReverse;\n    LazyWrapper.prototype.value = lazyValue;\n\n    // Add chain sequence methods to the `lodash` wrapper.\n    lodash.prototype.at = wrapperAt;\n    lodash.prototype.chain = wrapperChain;\n    lodash.prototype.commit = wrapperCommit;\n    lodash.prototype.next = wrapperNext;\n    lodash.prototype.plant = wrapperPlant;\n    lodash.prototype.reverse = wrapperReverse;\n    lodash.prototype.toJSON = lodash.prototype.valueOf = lodash.prototype.value = wrapperValue;\n\n    if (iteratorSymbol) {\n      lodash.prototype[iteratorSymbol] = wrapperToIterator;\n    }\n    return lodash;\n  }\n\n  /*--------------------------------------------------------------------------*/\n\n  // Export lodash.\n  var _ = runInContext();\n\n  // Expose lodash on the free variable `window` or `self` when available. This\n  // prevents errors in cases where lodash is loaded by a script tag in the presence\n  // of an AMD loader. See http://requirejs.org/docs/errors.html#mismatch for more details.\n  (freeWindow || freeSelf || {})._ = _;\n\n  // Some AMD build optimizers like r.js check for condition patterns like the following:\n  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {\n    // Define as an anonymous module so, through path mapping, it can be\n    // referenced as the \"underscore\" module.\n    define(function() {\n      return _;\n    });\n  }\n  // Check for `exports` after `define` in case a build optimizer adds an `exports` object.\n  else if (freeExports && freeModule) {\n    // Export for Node.js.\n    if (moduleExports) {\n      (freeModule.exports = _)._ = _;\n    }\n    // Export for CommonJS support.\n    freeExports._ = _;\n  }\n  else {\n    // Export to the global object.\n    root._ = _;\n  }\n}.call(this));\n"
  },
  {
    "path": "assets/js/vendor/platform.js",
    "content": "/*!\n * Platform.js v1.3.1 <http://mths.be/platform>\n * Copyright 2014-2016 Benjamin Tan <https://d10.github.io/>\n * Copyright 2011-2013 John-David Dalton <http://allyoucanleet.com/>\n * Available under MIT license <http://mths.be/mit>\n */\n;(function() {\n  'use strict';\n\n  /** Used to determine if values are of the language type `Object` */\n  var objectTypes = {\n    'function': true,\n    'object': true\n  };\n\n  /** Used as a reference to the global object */\n  var root = (objectTypes[typeof window] && window) || this;\n\n  /** Backup possible global object */\n  var oldRoot = root;\n\n  /** Detect free variable `exports` */\n  var freeExports = objectTypes[typeof exports] && exports;\n\n  /** Detect free variable `module` */\n  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;\n\n  /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */\n  var freeGlobal = freeExports && freeModule && typeof global == 'object' && global;\n  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal)) {\n    root = freeGlobal;\n  }\n\n  /**\n   * Used as the maximum length of an array-like object.\n   * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)\n   * for more details.\n   */\n  var maxSafeInteger = Math.pow(2, 53) - 1;\n\n  /** Opera regexp */\n  var reOpera = /\\bOpera/;\n\n  /** Possible global object */\n  var thisBinding = this;\n\n  /** Used for native method references */\n  var objectProto = Object.prototype;\n\n  /** Used to check for own properties of an object */\n  var hasOwnProperty = objectProto.hasOwnProperty;\n\n  /** Used to resolve the internal `[[Class]]` of values */\n  var toString = objectProto.toString;\n\n  /*--------------------------------------------------------------------------*/\n\n  /**\n   * Capitalizes a string value.\n   *\n   * @private\n   * @param {string} string The string to capitalize.\n   * @returns {string} The capitalized string.\n   */\n  function capitalize(string) {\n    string = String(string);\n    return string.charAt(0).toUpperCase() + string.slice(1);\n  }\n\n  /**\n   * A utility function to clean up the OS name.\n   *\n   * @private\n   * @param {string} os The OS name to clean up.\n   * @param {string} [pattern] A `RegExp` pattern matching the OS name.\n   * @param {string} [label] A label for the OS.\n   */\n  function cleanupOS(os, pattern, label) {\n    // platform tokens defined at\n    // http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx\n    // http://web.archive.org/web/20081122053950/http://msdn.microsoft.com/en-us/library/ms537503(VS.85).aspx\n    var data = {\n      '6.4':  '10',\n      '6.3':  '8.1',\n      '6.2':  '8',\n      '6.1':  'Server 2008 R2 / 7',\n      '6.0':  'Server 2008 / Vista',\n      '5.2':  'Server 2003 / XP 64-bit',\n      '5.1':  'XP',\n      '5.01': '2000 SP1',\n      '5.0':  '2000',\n      '4.0':  'NT',\n      '4.90': 'ME'\n    };\n    // detect Windows version from platform tokens\n    if (pattern && label && /^Win/i.test(os) &&\n        (data = data[0/*Opera 9.25 fix*/, /[\\d.]+$/.exec(os)])) {\n      os = 'Windows ' + data;\n    }\n    // correct character case and cleanup\n    os = String(os);\n\n    if (pattern && label) {\n      os = os.replace(RegExp(pattern, 'i'), label);\n    }\n\n    os = format(\n      os.replace(/ ce$/i, ' CE')\n        .replace(/\\bhpw/i, 'web')\n        .replace(/\\bMacintosh\\b/, 'Mac OS')\n        .replace(/_PowerPC\\b/i, ' OS')\n        .replace(/\\b(OS X) [^ \\d]+/i, '$1')\n        .replace(/\\bMac (OS X)\\b/, '$1')\n        .replace(/\\/(\\d)/, ' $1')\n        .replace(/_/g, '.')\n        .replace(/(?: BePC|[ .]*fc[ \\d.]+)$/i, '')\n        .replace(/\\bx86\\.64\\b/gi, 'x86_64')\n        .replace(/\\b(Windows Phone) OS\\b/, '$1')\n        .split(' on ')[0]\n    );\n\n    return os;\n  }\n\n  /**\n   * An iteration utility for arrays and objects.\n   *\n   * @private\n   * @param {Array|Object} object The object to iterate over.\n   * @param {Function} callback The function called per iteration.\n   */\n  function each(object, callback) {\n    var index = -1,\n        length = object ? object.length : 0;\n\n    if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {\n      while (++index < length) {\n        callback(object[index], index, object);\n      }\n    } else {\n      forOwn(object, callback);\n    }\n  }\n\n  /**\n   * Trim and conditionally capitalize string values.\n   *\n   * @private\n   * @param {string} string The string to format.\n   * @returns {string} The formatted string.\n   */\n  function format(string) {\n    string = trim(string);\n    return /^(?:webOS|i(?:OS|P))/.test(string)\n      ? string\n      : capitalize(string);\n  }\n\n  /**\n   * Iterates over an object's own properties, executing the `callback` for each.\n   *\n   * @private\n   * @param {Object} object The object to iterate over.\n   * @param {Function} callback The function executed per own property.\n   */\n  function forOwn(object, callback) {\n    for (var key in object) {\n      if (hasOwnProperty.call(object, key)) {\n        callback(object[key], key, object);\n      }\n    }\n  }\n\n  /**\n   * Gets the internal `[[Class]]` of a value.\n   *\n   * @private\n   * @param {*} value The value.\n   * @returns {string} The `[[Class]]`.\n   */\n  function getClassOf(value) {\n    return value == null\n      ? capitalize(value)\n      : toString.call(value).slice(8, -1);\n  }\n\n  /**\n   * Host objects can return type values that are different from their actual\n   * data type. The objects we are concerned with usually return non-primitive\n   * types of \"object\", \"function\", or \"unknown\".\n   *\n   * @private\n   * @param {*} object The owner of the property.\n   * @param {string} property The property to check.\n   * @returns {boolean} Returns `true` if the property value is a non-primitive, else `false`.\n   */\n  function isHostType(object, property) {\n    var type = object != null ? typeof object[property] : 'number';\n    return !/^(?:boolean|number|string|undefined)$/.test(type) &&\n      (type == 'object' ? !!object[property] : true);\n  }\n\n  /**\n   * Prepares a string for use in a `RegExp` by making hyphens and spaces optional.\n   *\n   * @private\n   * @param {string} string The string to qualify.\n   * @returns {string} The qualified string.\n   */\n  function qualify(string) {\n    return String(string).replace(/([ -])(?!$)/g, '$1?');\n  }\n\n  /**\n   * A bare-bones `Array#reduce` like utility function.\n   *\n   * @private\n   * @param {Array} array The array to iterate over.\n   * @param {Function} callback The function called per iteration.\n   * @returns {*} The accumulated result.\n   */\n  function reduce(array, callback) {\n    var accumulator = null;\n    each(array, function(value, index) {\n      accumulator = callback(accumulator, value, index, array);\n    });\n    return accumulator;\n  }\n\n  /**\n   * Removes leading and trailing whitespace from a string.\n   *\n   * @private\n   * @param {string} string The string to trim.\n   * @returns {string} The trimmed string.\n   */\n  function trim(string) {\n    return String(string).replace(/^ +| +$/g, '');\n  }\n\n  /*--------------------------------------------------------------------------*/\n\n  /**\n   * Creates a new platform object.\n   *\n   * @memberOf platform\n   * @param {Object|string} [ua=navigator.userAgent] The user agent string or\n   *  context object.\n   * @returns {Object} A platform object.\n   */\n  function parse(ua) {\n\n    /** The environment context object */\n    var context = root;\n\n    /** Used to flag when a custom context is provided */\n    var isCustomContext = ua && typeof ua == 'object' && getClassOf(ua) != 'String';\n\n    // juggle arguments\n    if (isCustomContext) {\n      context = ua;\n      ua = null;\n    }\n\n    /** Browser navigator object */\n    var nav = context.navigator || {};\n\n    /** Browser user agent string */\n    var userAgent = nav.userAgent || '';\n\n    ua || (ua = userAgent);\n\n    /** Used to flag when `thisBinding` is the [ModuleScope] */\n    var isModuleScope = isCustomContext || thisBinding == oldRoot;\n\n    /** Used to detect if browser is like Chrome */\n    var likeChrome = isCustomContext\n      ? !!nav.likeChrome\n      : /\\bChrome\\b/.test(ua) && !/internal|\\n/i.test(toString.toString());\n\n    /** Internal `[[Class]]` value shortcuts */\n    var objectClass = 'Object',\n        airRuntimeClass = isCustomContext ? objectClass : 'ScriptBridgingProxyObject',\n        enviroClass = isCustomContext ? objectClass : 'Environment',\n        javaClass = (isCustomContext && context.java) ? 'JavaPackage' : getClassOf(context.java),\n        phantomClass = isCustomContext ? objectClass : 'RuntimeObject';\n\n    /** Detect Java environment */\n    var java = /\\bJava/.test(javaClass) && context.java;\n\n    /** Detect Rhino */\n    var rhino = java && getClassOf(context.environment) == enviroClass;\n\n    /** A character to represent alpha */\n    var alpha = java ? 'a' : '\\u03b1';\n\n    /** A character to represent beta */\n    var beta = java ? 'b' : '\\u03b2';\n\n    /** Browser document object */\n    var doc = context.document || {};\n\n    /**\n     * Detect Opera browser (Presto-based)\n     * http://www.howtocreate.co.uk/operaStuff/operaObject.html\n     * http://dev.opera.com/articles/view/opera-mini-web-content-authoring-guidelines/#operamini\n     */\n    var opera = context.operamini || context.opera;\n\n    /** Opera `[[Class]]` */\n    var operaClass = reOpera.test(operaClass = (isCustomContext && opera) ? opera['[[Class]]'] : getClassOf(opera))\n      ? operaClass\n      : (opera = null);\n\n    /*------------------------------------------------------------------------*/\n\n    /** Temporary variable used over the script's lifetime */\n    var data;\n\n    /** The CPU architecture */\n    var arch = ua;\n\n    /** Platform description array */\n    var description = [];\n\n    /** Platform alpha/beta indicator */\n    var prerelease = null;\n\n    /** A flag to indicate that environment features should be used to resolve the platform */\n    var useFeatures = ua == userAgent;\n\n    /** The browser/environment version */\n    var version = useFeatures && opera && typeof opera.version == 'function' && opera.version();\n\n    /** A flag to indicate if the OS ends with \"/ Version\" */\n    var isSpecialCasedOS;\n\n    /* Detectable layout engines (order is important) */\n    var layout = getLayout([\n      'Trident',\n      { 'label': 'WebKit', 'pattern': 'AppleWebKit' },\n      'iCab',\n      'Presto',\n      'NetFront',\n      'Tasman',\n      'KHTML',\n      'Gecko'\n    ]);\n\n    /* Detectable browser names (order is important) */\n    var name = getName([\n      'Adobe AIR',\n      'Arora',\n      'Avant Browser',\n      'Breach',\n      'Camino',\n      'Epiphany',\n      'Fennec',\n      'Flock',\n      'Galeon',\n      'GreenBrowser',\n      'iCab',\n      'Iceweasel',\n      { 'label': 'SRWare Iron', 'pattern': 'Iron' },\n      'K-Meleon',\n      'Konqueror',\n      'Lunascape',\n      'Maxthon',\n      'Midori',\n      'Nook Browser',\n      'PhantomJS',\n      'Raven',\n      'Rekonq',\n      'RockMelt',\n      'SeaMonkey',\n      { 'label': 'Silk', 'pattern': '(?:Cloud9|Silk-Accelerated)' },\n      'Sleipnir',\n      'SlimBrowser',\n      'Sunrise',\n      'Swiftfox',\n      'WebPositive',\n      'Opera Mini',\n      { 'label': 'Opera Mini', 'pattern': 'OPiOS' },\n      'Opera',\n      { 'label': 'Opera', 'pattern': 'OPR' },\n      'Chrome',\n      { 'label': 'Chrome Mobile', 'pattern': '(?:CriOS|CrMo)' },\n      { 'label': 'Firefox', 'pattern': '(?:Firefox|Minefield)' },\n      { 'label': 'IE', 'pattern': 'IEMobile' },\n      { 'label': 'IE', 'pattern': 'MSIE' },\n      'Safari'\n    ]);\n\n    /* Detectable products (order is important) */\n    var product = getProduct([\n      { 'label': 'BlackBerry', 'pattern': 'BB10' },\n      'BlackBerry',\n      { 'label': 'Galaxy S', 'pattern': 'GT-I9000' },\n      { 'label': 'Galaxy S2', 'pattern': 'GT-I9100' },\n      { 'label': 'Galaxy S3', 'pattern': 'GT-I9300' },\n      { 'label': 'Galaxy S4', 'pattern': 'GT-I9500' },\n      'Google TV',\n      'Lumia',\n      'iPad',\n      'iPod',\n      'iPhone',\n      'Kindle',\n      { 'label': 'Kindle Fire', 'pattern': '(?:Cloud9|Silk-Accelerated)' },\n      'Nook',\n      'PlayBook',\n      'PlayStation 4',\n      'PlayStation 3',\n      'PlayStation Vita',\n      'TouchPad',\n      'Transformer',\n      { 'label': 'Wii U', 'pattern': 'WiiU' },\n      'Wii',\n      'Xbox One',\n      { 'label': 'Xbox 360', 'pattern': 'Xbox' },\n      'Xoom'\n    ]);\n\n    /* Detectable manufacturers */\n    var manufacturer = getManufacturer({\n      'Apple': { 'iPad': 1, 'iPhone': 1, 'iPod': 1 },\n      'Amazon': { 'Kindle': 1, 'Kindle Fire': 1 },\n      'Asus': { 'Transformer': 1 },\n      'Barnes & Noble': { 'Nook': 1 },\n      'BlackBerry': { 'PlayBook': 1 },\n      'Google': { 'Google TV': 1 },\n      'HP': { 'TouchPad': 1 },\n      'HTC': {},\n      'LG': {},\n      'Microsoft': { 'Xbox': 1, 'Xbox One': 1 },\n      'Motorola': { 'Xoom': 1 },\n      'Nintendo': { 'Wii U': 1,  'Wii': 1 },\n      'Nokia': { 'Lumia': 1 },\n      'Samsung': { 'Galaxy S': 1, 'Galaxy S2': 1, 'Galaxy S3': 1, 'Galaxy S4': 1 },\n      'Sony': { 'PlayStation 4': 1, 'PlayStation 3': 1, 'PlayStation Vita': 1 }\n    });\n\n    /* Detectable OSes (order is important) */\n    var os = getOS([\n      'Windows Phone ',\n      'Android',\n      'CentOS',\n      'Debian',\n      'Fedora',\n      'FreeBSD',\n      'Gentoo',\n      'Haiku',\n      'Kubuntu',\n      'Linux Mint',\n      'Red Hat',\n      'SuSE',\n      'Ubuntu',\n      'Xubuntu',\n      'Cygwin',\n      'Symbian OS',\n      'hpwOS',\n      'webOS ',\n      'webOS',\n      'Tablet OS',\n      'Linux',\n      'Mac OS X',\n      'Macintosh',\n      'Mac',\n      'Windows 98;',\n      'Windows '\n    ]);\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * Picks the layout engine from an array of guesses.\n     *\n     * @private\n     * @param {Array} guesses An array of guesses.\n     * @returns {null|string} The detected layout engine.\n     */\n    function getLayout(guesses) {\n      return reduce(guesses, function(result, guess) {\n        return result || RegExp('\\\\b' + (\n          guess.pattern || qualify(guess)\n        ) + '\\\\b', 'i').exec(ua) && (guess.label || guess);\n      });\n    }\n\n    /**\n     * Picks the manufacturer from an array of guesses.\n     *\n     * @private\n     * @param {Array} guesses An object of guesses.\n     * @returns {null|string} The detected manufacturer.\n     */\n    function getManufacturer(guesses) {\n      return reduce(guesses, function(result, value, key) {\n        // lookup the manufacturer by product or scan the UA for the manufacturer\n        return result || (\n          value[product] ||\n          value[0/*Opera 9.25 fix*/, /^[a-z]+(?: +[a-z]+\\b)*/i.exec(product)] ||\n          RegExp('\\\\b' + qualify(key) + '(?:\\\\b|\\\\w*\\\\d)', 'i').exec(ua)\n        ) && key;\n      });\n    }\n\n    /**\n     * Picks the browser name from an array of guesses.\n     *\n     * @private\n     * @param {Array} guesses An array of guesses.\n     * @returns {null|string} The detected browser name.\n     */\n    function getName(guesses) {\n      return reduce(guesses, function(result, guess) {\n        return result || RegExp('\\\\b' + (\n          guess.pattern || qualify(guess)\n        ) + '\\\\b', 'i').exec(ua) && (guess.label || guess);\n      });\n    }\n\n    /**\n     * Picks the OS name from an array of guesses.\n     *\n     * @private\n     * @param {Array} guesses An array of guesses.\n     * @returns {null|string} The detected OS name.\n     */\n    function getOS(guesses) {\n      return reduce(guesses, function(result, guess) {\n        var pattern = guess.pattern || qualify(guess);\n        if (!result && (result =\n              RegExp('\\\\b' + pattern + '(?:/[\\\\d.]+|[ \\\\w.]*)', 'i').exec(ua)\n            )) {\n          result = cleanupOS(result, pattern, guess.label || guess);\n        }\n        return result;\n      });\n    }\n\n    /**\n     * Picks the product name from an array of guesses.\n     *\n     * @private\n     * @param {Array} guesses An array of guesses.\n     * @returns {null|string} The detected product name.\n     */\n    function getProduct(guesses) {\n      return reduce(guesses, function(result, guess) {\n        var pattern = guess.pattern || qualify(guess);\n        if (!result && (result =\n              RegExp('\\\\b' + pattern + ' *\\\\d+[.\\\\w_]*', 'i').exec(ua) ||\n              RegExp('\\\\b' + pattern + '(?:; *(?:[a-z]+[_-])?[a-z]+\\\\d+|[^ ();-]*)', 'i').exec(ua)\n            )) {\n          // split by forward slash and append product version if needed\n          if ((result = String((guess.label && !RegExp(pattern, 'i').test(guess.label)) ? guess.label : result).split('/'))[1] && !/[\\d.]+/.test(result[0])) {\n            result[0] += ' ' + result[1];\n          }\n          // correct character case and cleanup\n          guess = guess.label || guess;\n          result = format(result[0]\n            .replace(RegExp(pattern, 'i'), guess)\n            .replace(RegExp('; *(?:' + guess + '[_-])?', 'i'), ' ')\n            .replace(RegExp('(' + guess + ')[-_.]?(\\\\w)', 'i'), '$1 $2'));\n        }\n        return result;\n      });\n    }\n\n    /**\n     * Resolves the version using an array of UA patterns.\n     *\n     * @private\n     * @param {Array} patterns An array of UA patterns.\n     * @returns {null|string} The detected version.\n     */\n    function getVersion(patterns) {\n      return reduce(patterns, function(result, pattern) {\n        return result || (RegExp(pattern +\n          '(?:-[\\\\d.]+/|(?: for [\\\\w-]+)?[ /-])([\\\\d.]+[^ ();/_-]*)', 'i').exec(ua) || 0)[1] || null;\n      });\n    }\n\n    /**\n     * Returns `platform.description` when the platform object is coerced to a string.\n     *\n     * @name toString\n     * @memberOf platform\n     * @returns {string} Returns `platform.description` if available, else an empty string.\n     */\n    function toStringPlatform() {\n      return this.description || '';\n    }\n\n    /*------------------------------------------------------------------------*/\n\n    // convert layout to an array so we can add extra details\n    layout && (layout = [layout]);\n\n    // detect product names that contain their manufacturer's name\n    if (manufacturer && !product) {\n      product = getProduct([manufacturer]);\n    }\n    // clean up Google TV\n    if ((data = /\\bGoogle TV\\b/.exec(product))) {\n      product = data[0];\n    }\n    // detect simulators\n    if (/\\bSimulator\\b/i.test(ua)) {\n      product = (product ? product + ' ' : '') + 'Simulator';\n    }\n    // detect Opera Mini 8+ running in Turbo/Uncompressed mode on iOS\n    if (name == 'Opera Mini' && /\\bOPiOS\\b/.test(ua)) {\n      description.push('running in Turbo/Uncompressed mode');\n    }\n    // detect iOS\n    if (/^iP/.test(product)) {\n      name || (name = 'Safari');\n      os = 'iOS' + ((data = / OS ([\\d_]+)/i.exec(ua))\n        ? ' ' + data[1].replace(/_/g, '.')\n        : '');\n    }\n    // detect Kubuntu\n    else if (name == 'Konqueror' && !/buntu/i.test(os)) {\n      os = 'Kubuntu';\n    }\n    // detect Android browsers\n    else if (manufacturer && manufacturer != 'Google' &&\n        ((/Chrome/.test(name) && !/\\bMobile Safari\\b/i.test(ua)) || /\\bVita\\b/.test(product))) {\n      name = 'Android Browser';\n      os = /\\bAndroid\\b/.test(os) ? os : 'Android';\n    }\n    // detect false positives for Firefox/Safari\n    else if (!name || (data = !/\\bMinefield\\b|\\(Android;/i.test(ua) && /\\b(?:Firefox|Safari)\\b/.exec(name))) {\n      // escape the `/` for Firefox 1\n      if (name && !product && /[\\/,]|^[^(]+?\\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {\n        // clear name of false positives\n        name = null;\n      }\n      // reassign a generic name\n      if ((data = product || manufacturer || os) &&\n          (product || manufacturer || /\\b(?:Android|Symbian OS|Tablet OS|webOS)\\b/.test(os))) {\n        name = /[a-z]+(?: Hat)?/i.exec(/\\bAndroid\\b/.test(os) ? os : data) + ' Browser';\n      }\n    }\n    // detect Firefox OS\n    if ((data = /\\((Mobile|Tablet).*?Firefox\\b/i.exec(ua)) && data[1]) {\n      os = 'Firefox OS';\n      if (!product) {\n        product = data[1];\n      }\n    }\n    // detect non-Opera versions (order is important)\n    if (!version) {\n      version = getVersion([\n        '(?:Cloud9|CriOS|CrMo|IEMobile|Iron|Opera ?Mini|OPiOS|OPR|Raven|Silk(?!/[\\\\d.]+$))',\n        'Version',\n        qualify(name),\n        '(?:Firefox|Minefield|NetFront)'\n      ]);\n    }\n    // detect stubborn layout engines\n    if (layout == 'iCab' && parseFloat(version) > 3) {\n      layout = ['WebKit'];\n    } else if (\n        layout != 'Trident' &&\n        (data =\n          /\\bOpera\\b/.test(name) && (/\\bOPR\\b/.test(ua) ? 'Blink' : 'Presto') ||\n          /\\b(?:Midori|Nook|Safari)\\b/i.test(ua) && 'WebKit' ||\n          !layout && /\\bMSIE\\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident')\n        )\n    ) {\n      layout = [data];\n    }\n    // detect NetFront on PlayStation\n    else if (/\\bPlayStation\\b(?! Vita\\b)/i.test(name) && layout == 'WebKit') {\n      layout = ['NetFront'];\n    }\n    // detect Windows Phone 7 desktop mode\n    if (name == 'IE' && (data = (/; *(?:XBLWP|ZuneWP)(\\d+)/i.exec(ua) || 0)[1])) {\n      name += ' Mobile';\n      os = 'Windows Phone ' + (/\\+$/.test(data) ? data : data + '.x');\n      description.unshift('desktop mode');\n    }\n    // detect Windows Phone 8+ desktop mode\n    else if (/\\bWPDesktop\\b/i.test(ua)) {\n      name = 'IE Mobile';\n      os = 'Windows Phone 8+';\n      description.unshift('desktop mode');\n      version || (version = (/\\brv:([\\d.]+)/.exec(ua) || 0)[1]);\n    }\n    // detect IE 11 and above\n    else if (name != 'IE' && layout == 'Trident' && (data = /\\brv:([\\d.]+)/.exec(ua))) {\n      if (!/\\bWPDesktop\\b/i.test(ua)) {\n        if (name) {\n          description.push('identifying as ' + name + (version ? ' ' + version : ''));\n        }\n        name = 'IE';\n      }\n      version = data[1];\n    }\n    // detect Microsoft Edge\n    else if ((name == 'Chrome' || name != 'IE') && (data = /\\bEdge\\/([\\d.]+)/.exec(ua))) {\n      name = 'Microsoft Edge';\n      version = data[1];\n      layout = ['Trident'];\n    }\n    // leverage environment features\n    if (useFeatures) {\n      // detect server-side environments\n      // Rhino has a global function while others have a global object\n      if (isHostType(context, 'global')) {\n        if (java) {\n          data = java.lang.System;\n          arch = data.getProperty('os.arch');\n          os = os || data.getProperty('os.name') + ' ' + data.getProperty('os.version');\n        }\n        if (isModuleScope && isHostType(context, 'system') && (data = [context.system])[0]) {\n          os || (os = data[0].os || null);\n          try {\n            data[1] = context.require('ringo/engine').version;\n            version = data[1].join('.');\n            name = 'RingoJS';\n          } catch(e) {\n            if (data[0].global.system == context.system) {\n              name = 'Narwhal';\n            }\n          }\n        }\n        else if (typeof context.process == 'object' && (data = context.process)) {\n          name = 'Node.js';\n          arch = data.arch;\n          os = data.platform;\n          version = /[\\d.]+/.exec(data.version)[0];\n        }\n        else if (rhino) {\n          name = 'Rhino';\n        }\n      }\n      // detect Adobe AIR\n      else if (getClassOf((data = context.runtime)) == airRuntimeClass) {\n        name = 'Adobe AIR';\n        os = data.flash.system.Capabilities.os;\n      }\n      // detect PhantomJS\n      else if (getClassOf((data = context.phantom)) == phantomClass) {\n        name = 'PhantomJS';\n        version = (data = data.version || null) && (data.major + '.' + data.minor + '.' + data.patch);\n      }\n      // detect IE compatibility modes\n      else if (typeof doc.documentMode == 'number' && (data = /\\bTrident\\/(\\d+)/i.exec(ua))) {\n        // we're in compatibility mode when the Trident version + 4 doesn't\n        // equal the document mode\n        version = [version, doc.documentMode];\n        if ((data = +data[1] + 4) != version[1]) {\n          description.push('IE ' + version[1] + ' mode');\n          layout && (layout[1] = '');\n          version[1] = data;\n        }\n        version = name == 'IE' ? String(version[1].toFixed(1)) : version[0];\n      }\n      os = os && format(os);\n    }\n    // detect prerelease phases\n    if (version && (data =\n          /(?:[ab]|dp|pre|[ab]\\d+pre)(?:\\d+\\+?)?$/i.exec(version) ||\n          /(?:alpha|beta)(?: ?\\d)?/i.exec(ua + ';' + (useFeatures && nav.appMinorVersion)) ||\n          /\\bMinefield\\b/i.test(ua) && 'a'\n        )) {\n      prerelease = /b/i.test(data) ? 'beta' : 'alpha';\n      version = version.replace(RegExp(data + '\\\\+?$'), '') +\n        (prerelease == 'beta' ? beta : alpha) + (/\\d+\\+?/.exec(data) || '');\n    }\n    // detect Firefox Mobile\n    if (name == 'Fennec' || name == 'Firefox' && /\\b(?:Android|Firefox OS)\\b/.test(os)) {\n      name = 'Firefox Mobile';\n    }\n    // obscure Maxthon's unreliable version\n    else if (name == 'Maxthon' && version) {\n      version = version.replace(/\\.[\\d.]+/, '.x');\n    }\n    // detect Silk desktop/accelerated modes\n    else if (name == 'Silk') {\n      if (!/\\bMobi/i.test(ua)) {\n        os = 'Android';\n        description.unshift('desktop mode');\n      }\n      if (/Accelerated *= *true/i.test(ua)) {\n        description.unshift('accelerated');\n      }\n    }\n    // detect Xbox 360 and Xbox One\n    else if (/\\bXbox\\b/i.test(product)) {\n      os = null;\n      if (product == 'Xbox 360' && /\\bIEMobile\\b/.test(ua)) {\n        description.unshift('mobile mode');\n      }\n    }\n    // add mobile postfix\n    else if ((/^(?:Chrome|IE|Opera)$/.test(name) || name && !product && !/Browser|Mobi/.test(name)) &&\n        (os == 'Windows CE' || /Mobi/i.test(ua))) {\n      name += ' Mobile';\n    }\n    // detect IE platform preview\n    else if (name == 'IE' && useFeatures && context.external === null) {\n      description.unshift('platform preview');\n    }\n    // detect BlackBerry OS version\n    // http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp\n    else if ((/\\bBlackBerry\\b/.test(product) || /\\bBB10\\b/.test(ua)) && (data =\n          (RegExp(product.replace(/ +/g, ' *') + '/([.\\\\d]+)', 'i').exec(ua) || 0)[1] ||\n          version\n        )) {\n      data = [data, /BB10/.test(ua)];\n      os = (data[1] ? (product = null, manufacturer = 'BlackBerry') : 'Device Software') + ' ' + data[0];\n      version = null;\n    }\n    // detect Opera identifying/masking itself as another browser\n    // http://www.opera.com/support/kb/view/843/\n    else if (this != forOwn && (\n          product != 'Wii' && (\n            (useFeatures && opera) ||\n            (/Opera/.test(name) && /\\b(?:MSIE|Firefox)\\b/i.test(ua)) ||\n            (name == 'Firefox' && /\\bOS X (?:\\d+\\.){2,}/.test(os)) ||\n            (name == 'IE' && (\n              (os && !/^Win/.test(os) && version > 5.5) ||\n              /\\bWindows XP\\b/.test(os) && version > 8 ||\n              version == 8 && !/\\bTrident\\b/.test(ua)\n            ))\n          )\n        ) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) {\n\n      // when \"indentifying\", the UA contains both Opera and the other browser's name\n      data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');\n      if (reOpera.test(name)) {\n        if (/\\bIE\\b/.test(data) && os == 'Mac OS') {\n          os = null;\n        }\n        data = 'identify' + data;\n      }\n      // when \"masking\", the UA contains only the other browser's name\n      else {\n        data = 'mask' + data;\n        if (operaClass) {\n          name = format(operaClass.replace(/([a-z])([A-Z])/g, '$1 $2'));\n        } else {\n          name = 'Opera';\n        }\n        if (/\\bIE\\b/.test(data)) {\n          os = null;\n        }\n        if (!useFeatures) {\n          version = null;\n        }\n      }\n      layout = ['Presto'];\n      description.push(data);\n    }\n    // detect WebKit Nightly and approximate Chrome/Safari versions\n    if ((data = (/\\bAppleWebKit\\/([\\d.]+\\+?)/i.exec(ua) || 0)[1])) {\n      // correct build for numeric comparison\n      // (e.g. \"532.5\" becomes \"532.05\")\n      data = [parseFloat(data.replace(/\\.(\\d)$/, '.0$1')), data];\n      // nightly builds are postfixed with a `+`\n      if (name == 'Safari' && data[1].slice(-1) == '+') {\n        name = 'WebKit Nightly';\n        prerelease = 'alpha';\n        version = data[1].slice(0, -1);\n      }\n      // clear incorrect browser versions\n      else if (version == data[1] ||\n          version == (data[2] = (/\\bSafari\\/([\\d.]+\\+?)/i.exec(ua) || 0)[1])) {\n        version = null;\n      }\n      // use the full Chrome version when available\n      data[1] = (/\\bChrome\\/([\\d.]+)/i.exec(ua) || 0)[1];\n      // detect Blink layout engine\n      if (data[0] == 537.36 && data[2] == 537.36 && parseFloat(data[1]) >= 28 && name != 'IE' && name != 'Microsoft Edge') {\n        layout = ['Blink'];\n      }\n      // detect JavaScriptCore\n      // http://stackoverflow.com/questions/6768474/how-can-i-detect-which-javascript-engine-v8-or-jsc-is-used-at-runtime-in-androi\n      if (!useFeatures || (!likeChrome && !data[1])) {\n        layout && (layout[1] = 'like Safari');\n        data = (data = data[0], data < 400 ? 1 : data < 500 ? 2 : data < 526 ? 3 : data < 533 ? 4 : data < 534 ? '4+' : data < 535 ? 5 : data < 537 ? 6 : data < 538 ? 7 : data < 601 ? 8 : '8');\n      } else {\n        layout && (layout[1] = 'like Chrome');\n        data = data[1] || (data = data[0], data < 530 ? 1 : data < 532 ? 2 : data < 532.05 ? 3 : data < 533 ? 4 : data < 534.03 ? 5 : data < 534.07 ? 6 : data < 534.10 ? 7 : data < 534.13 ? 8 : data < 534.16 ? 9 : data < 534.24 ? 10 : data < 534.30 ? 11 : data < 535.01 ? 12 : data < 535.02 ? '13+' : data < 535.07 ? 15 : data < 535.11 ? 16 : data < 535.19 ? 17 : data < 536.05 ? 18 : data < 536.10 ? 19 : data < 537.01 ? 20 : data < 537.11 ? '21+' : data < 537.13 ? 23 : data < 537.18 ? 24 : data < 537.24 ? 25 : data < 537.36 ? 26 : layout != 'Blink' ? '27' : '28');\n      }\n      // add the postfix of \".x\" or \"+\" for approximate versions\n      layout && (layout[1] += ' ' + (data += typeof data == 'number' ? '.x' : /[.+]/.test(data) ? '' : '+'));\n      // obscure version for some Safari 1-2 releases\n      if (name == 'Safari' && (!version || parseInt(version) > 45)) {\n        version = data;\n      }\n    }\n    // detect Opera desktop modes\n    if (name == 'Opera' &&  (data = /\\bzbov|zvav$/.exec(os))) {\n      name += ' ';\n      description.unshift('desktop mode');\n      if (data == 'zvav') {\n        name += 'Mini';\n        version = null;\n      } else {\n        name += 'Mobile';\n      }\n      os = os.replace(RegExp(' *' + data + '$'), '');\n    }\n    // detect Chrome desktop mode\n    else if (name == 'Safari' && /\\bChrome\\b/.exec(layout && layout[1])) {\n      description.unshift('desktop mode');\n      name = 'Chrome Mobile';\n      version = null;\n\n      if (/\\bOS X\\b/.test(os)) {\n        manufacturer = 'Apple';\n        os = 'iOS 4.3+';\n      } else {\n        os = null;\n      }\n    }\n    // strip incorrect OS versions\n    if (version && version.indexOf((data = /[\\d.]+$/.exec(os))) == 0 &&\n        ua.indexOf('/' + data + '-') > -1) {\n      os = trim(os.replace(data, ''));\n    }\n    // add layout engine\n    if (layout && !/\\b(?:Avant|Nook)\\b/.test(name) && (\n        /Browser|Lunascape|Maxthon/.test(name) ||\n        /^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Sleipnir|Web)/.test(name) && layout[1])) {\n      // don't add layout details to description if they are falsey\n      (data = layout[layout.length - 1]) && description.push(data);\n    }\n    // combine contextual information\n    if (description.length) {\n      description = ['(' + description.join('; ') + ')'];\n    }\n    // append manufacturer\n    if (manufacturer && product && product.indexOf(manufacturer) < 0) {\n      description.push('on ' + manufacturer);\n    }\n    // append product\n    if (product) {\n      description.push((/^on /.test(description[description.length -1]) ? '' : 'on ') + product);\n    }\n    // parse OS into an object\n    if (os) {\n      data = / ([\\d.+]+)$/.exec(os);\n      isSpecialCasedOS = data && os.charAt(os.length - data[0].length - 1) == '/';\n      os = {\n        'architecture': 32,\n        'family': (data && !isSpecialCasedOS) ? os.replace(data[0], '') : os,\n        'version': data ? data[1] : null,\n        'toString': function() {\n          var version = this.version;\n          return this.family + ((version && !isSpecialCasedOS) ? ' ' + version : '') + (this.architecture == 64 ? ' 64-bit' : '');\n        }\n      };\n    }\n    // add browser/OS architecture\n    if ((data = /\\b(?:AMD|IA|Win|WOW|x86_|x)64\\b/i.exec(arch)) && !/\\bi686\\b/i.test(arch)) {\n      if (os) {\n        os.architecture = 64;\n        os.family = os.family.replace(RegExp(' *' + data), '');\n      }\n      if (\n          name && (/\\bWOW64\\b/i.test(ua) ||\n          (useFeatures && /\\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/\\bWin64; x64\\b/i.test(ua)))\n      ) {\n        description.unshift('32-bit');\n      }\n    }\n\n    ua || (ua = null);\n\n    /*------------------------------------------------------------------------*/\n\n    /**\n     * The platform object.\n     *\n     * @name platform\n     * @type Object\n     */\n    var platform = {};\n\n    /**\n     * The platform description.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.description = ua;\n\n    /**\n     * The name of the browser's layout engine.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.layout = layout && layout[0];\n\n    /**\n     * The name of the product's manufacturer.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.manufacturer = manufacturer;\n\n    /**\n     * The name of the browser/environment.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.name = name;\n\n    /**\n     * The alpha/beta release indicator.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.prerelease = prerelease;\n\n    /**\n     * The name of the product hosting the browser.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.product = product;\n\n    /**\n     * The browser's user agent string.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.ua = ua;\n\n    /**\n     * The browser/environment version.\n     *\n     * @memberOf platform\n     * @type string|null\n     */\n    platform.version = name && version;\n\n    /**\n     * The name of the operating system.\n     *\n     * @memberOf platform\n     * @type Object\n     */\n    platform.os = os || {\n\n      /**\n       * The CPU architecture the OS is built for.\n       *\n       * @memberOf platform.os\n       * @type number|null\n       */\n      'architecture': null,\n\n      /**\n       * The family of the OS.\n       *\n       * Common values include:\n       * \"Windows\", \"Windows Server 2008 R2 / 7\", \"Windows Server 2008 / Vista\",\n       * \"Windows XP\", \"OS X\", \"Ubuntu\", \"Debian\", \"Fedora\", \"Red Hat\", \"SuSE\",\n       * \"Android\", \"iOS\" and \"Windows Phone\"\n       *\n       * @memberOf platform.os\n       * @type string|null\n       */\n      'family': null,\n\n      /**\n       * The version of the OS.\n       *\n       * @memberOf platform.os\n       * @type string|null\n       */\n      'version': null,\n\n      /**\n       * Returns the OS string.\n       *\n       * @memberOf platform.os\n       * @returns {string} The OS string.\n       */\n      'toString': function() { return 'null'; }\n    };\n\n    platform.parse = parse;\n    platform.toString = toStringPlatform;\n\n    if (platform.version) {\n      description.unshift(version);\n    }\n    if (platform.name) {\n      description.unshift(name);\n    }\n    if (os && name && !(os == String(os).split(' ')[0] && (os == name.split(' ')[0] || product))) {\n      description.push(product ? '(' + os + ')' : 'on ' + os);\n    }\n    if (description.length) {\n      platform.description = description.join(' ');\n    }\n    return platform;\n  }\n\n  /*--------------------------------------------------------------------------*/\n\n  // export platform\n  // some AMD build optimizers, like r.js, check for condition patterns like the following:\n  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {\n    // define as an anonymous module so, through path mapping, it can be aliased\n    define(function() {\n      return parse();\n    });\n  }\n  // check for `exports` after `define` in case a build optimizer adds an `exports` object\n  else if (freeExports && freeModule) {\n    // in Narwhal, Node.js, Rhino -require, or RingoJS\n    forOwn(parse(), function(value, key) {\n      freeExports[key] = value;\n    });\n  }\n  // in a browser or Rhino\n  else {\n    root.platform = parse();\n  }\n}.call(this));\n"
  },
  {
    "path": "assets/js/vendor/rollbar-snippet.js",
    "content": "// Rollbar Snippet\n!function(r){function e(t){if(o[t])return o[t].exports;var n=o[t]={exports:{},id:t,loaded:!1};return r[t].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var o={};return e.m=r,e.c=o,e.p=\"\",e(0)}([function(r,e,o){\"use strict\";var t=o(1).Rollbar,n=o(2);_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||\"https://d37gvrvc0wt4s1.cloudfront.net/js/v1.9/rollbar.min.js\";var a=t.init(window,_rollbarConfig),i=n(a,_rollbarConfig);a.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,i)},function(r,e){\"use strict\";function o(r){return function(){try{return r.apply(this,arguments)}catch(e){try{console.error(\"[Rollbar]: Internal error\",e)}catch(o){}}}}function t(r,e,o){window._rollbarWrappedError&&(o[4]||(o[4]=window._rollbarWrappedError),o[5]||(o[5]=window._rollbarWrappedError._rollbarContext),window._rollbarWrappedError=null),r.uncaughtError.apply(r,o),e&&e.apply(window,o)}function n(r){var e=function(){var e=Array.prototype.slice.call(arguments,0);t(r,r._rollbarOldOnError,e)};return e.belongsToShim=!0,e}function a(r){this.shimId=++c,this.notifier=null,this.parentShim=r,this._rollbarOldOnError=null}function i(r){var e=a;return o(function(){if(this.notifier)return this.notifier[r].apply(this.notifier,arguments);var o=this,t=\"scope\"===r;t&&(o=new e(this));var n=Array.prototype.slice.call(arguments,0),a={shim:o,method:r,args:n,ts:new Date};return window._rollbarShimQueue.push(a),t?o:void 0})}function l(r,e){if(e.hasOwnProperty&&e.hasOwnProperty(\"addEventListener\")){var o=e.addEventListener;e.addEventListener=function(e,t,n){o.call(this,e,r.wrap(t),n)};var t=e.removeEventListener;e.removeEventListener=function(r,e,o){t.call(this,r,e&&e._wrapped?e._wrapped:e,o)}}}var c=0;a.init=function(r,e){var t=e.globalAlias||\"Rollbar\";if(\"object\"==typeof r[t])return r[t];r._rollbarShimQueue=[],r._rollbarWrappedError=null,e=e||{};var i=new a;return o(function(){if(i.configure(e),e.captureUncaught){i._rollbarOldOnError=r.onerror,r.onerror=n(i);var o,a,c=\"EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload\".split(\",\");for(o=0;o<c.length;++o)a=c[o],r[a]&&r[a].prototype&&l(i,r[a].prototype)}return e.captureUnhandledRejections&&(i._unhandledRejectionHandler=function(r){var e=r.reason,o=r.promise,t=r.detail;!e&&t&&(e=t.reason,o=t.promise),i.unhandledRejection(e,o)},r.addEventListener(\"unhandledrejection\",i._unhandledRejectionHandler)),r[t]=i,i})()},a.prototype.loadFull=function(r,e,t,n,a){var i=function(){var e;if(void 0===r._rollbarPayloadQueue){var o,t,n,i;for(e=new Error(\"rollbar.js did not load\");o=r._rollbarShimQueue.shift();)for(n=o.args,i=0;i<n.length;++i)if(t=n[i],\"function\"==typeof t){t(e);break}}\"function\"==typeof a&&a(e)},l=!1,c=e.createElement(\"script\"),d=e.getElementsByTagName(\"script\")[0],p=d.parentNode;c.crossOrigin=\"\",c.src=n.rollbarJsUrl,c.async=!t,c.onload=c.onreadystatechange=o(function(){if(!(l||this.readyState&&\"loaded\"!==this.readyState&&\"complete\"!==this.readyState)){c.onload=c.onreadystatechange=null;try{p.removeChild(c)}catch(r){}l=!0,i()}}),p.insertBefore(c,d)},a.prototype.wrap=function(r,e){try{var o;if(o=\"function\"==typeof e?e:function(){return e||{}},\"function\"!=typeof r)return r;if(r._isWrap)return r;if(!r._wrapped){r._wrapped=function(){try{return r.apply(this,arguments)}catch(e){throw e._rollbarContext=o()||{},e._rollbarContext._wrappedSource=r.toString(),window._rollbarWrappedError=e,e}},r._wrapped._isWrap=!0;for(var t in r)r.hasOwnProperty(t)&&(r._wrapped[t]=r[t])}return r._wrapped}catch(n){return r}};for(var d=\"log,debug,info,warn,warning,error,critical,global,configure,scope,uncaughtError,unhandledRejection\".split(\",\"),p=0;p<d.length;++p)a.prototype[d[p]]=i(d[p]);r.exports={Rollbar:a,_rollbarWindowOnError:t}},function(r,e){\"use strict\";r.exports=function(r,e){return function(o){if(!o&&!window._rollbarInitialized){var t=window.RollbarNotifier,n=e||{},a=n.globalAlias||\"Rollbar\",i=window.Rollbar.init(n,r);i._processShimQueue(window._rollbarShimQueue||[]),window[a]=i,window._rollbarInitialized=!0,t.processPayloads()}}}}]);\n// End Rollbar Snippet\n"
  },
  {
    "path": "assets/workspaces.html",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n        <link href=\"https://fonts.googleapis.com/css?family=Open+Sans\" rel=\"stylesheet\">\n    </head>\n    <body>\n        <div id=\"app\"></div>\n        <!-- you might need to change the js path depending on your configuration -->\n        <script src=\"/js/workspaces/main.js\" type=\"text/javascript\"></script>\n        <link rel=\"stylesheet\" href=\"//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css\">\n    </body>\n</html>\n"
  },
  {
    "path": "bin/deploy",
    "content": "#!/usr/bin/env bash\n\nprogname=$(basename $0)\n\nfunction usage {\n  echo \"usage: $progname [-s SOURCE] -b BUCKET [-p PREFIX] [-P]\"\n  echo \"\t-s SOURCE\tdirectory containing files to deploy\"\n  echo \"\t-b BUCKET\tS3 bucket to deploy to\"\n  echo \"\t-p PREFIX\tprefix within bucket\"\n  echo \"\t-P\t\tmake files public on S3\"\n}\n\nsource='target'\n\nwhile getopts \":s:b:p:P\" opt; do\n  case $opt in\n    s)\n      source=\"$OPTARG\"\n      ;;\n    b)\n      bucket=\"$OPTARG\"\n      ;;\n    p)\n      prefix=\"$OPTARG\"\n      ;;\n    P)\n      public_opt=--acl-public\n      ;;\n    \\?)\n      usage\n      echo \"error: invalid option: -$OPTARG\" >&2\n      exit 1\n      ;;\n    :)\n      usage\n      echo \"error: option -$OPTARG requires an argument\" >&2\n      exit 1\n      ;;\n  esac\ndone\n\nif [[ ! $bucket ]]; then\n  usage\n  echo \"error: you must specify an S3 bucket\"\n  exit 1\nfi\n\ndestination=\"s3://$bucket\"\n\nif [[ $prefix ]]; then\n  destination=\"${destination}${prefix}/\"\nfi\n\ngzip_opt=\"--add-header=Content-Encoding: gzip\"\ncache_opt=\"--add-header=Cache-Control: max-age=86400\"\nimg_include_opts=\"--include '*.png' --include '*.jpg' --include '*.ico' --include '*.gif'\"\nrexlude='(\\.htpasswd|doc.*|builds.*)'\n\ns3cmd sync --exclude  '*'       --rinclude '^(?!doc/).*\\.css$'  -m 'text/css'               $public_opt \"$gzip_opt\"  $source/ $destination\ns3cmd sync --exclude  '*'       --rinclude '^(?!doc/).*\\.js$'   -m 'application/javascript' $public_opt \"$gzip_opt\"  $source/ $destination\ns3cmd sync --exclude  '*'       --rinclude '^(?!doc/).*\\.html$' -m 'text/html'              $public_opt \"$gzip_opt\"  $source/ $destination\ns3cmd sync --exclude  '*'       $img_include_opts                                           $public_opt \"$cache_opt\" $source/ $destination\ns3cmd sync --exclude  '*'       --rinclude '^(?!doc/).*\\.svg$'  -m 'image/svg+xml'          $public_opt \"$cache_opt\" $source/ $destination\ns3cmd sync --rexclude $rexclude --delete-removed                                            $public_opt              $source/ $destination\n\ns3cmd sync $public_opt $source/doc/ $destination/doc/\n"
  },
  {
    "path": "boot.properties",
    "content": "BOOT_CLOJURE_VERSION=1.9.0-alpha14\nBOOT_VERSION=2.7.1\n"
  },
  {
    "path": "build.boot",
    "content": "(set-env!\n :source-paths #{\"src/clj\" \"src/cljc\" \"src/cljs\" \"src/js\" \"src/scss\" \"test/cljs\" \"site\"}\n :resource-paths #{\"assets\"}\n :dependencies\n '[;; Dev\n   [adzerk/boot-cljs \"2.0.0\" :scope \"test\"]\n   [adzerk/boot-cljs-repl \"0.3.3\" :scope \"test\"]\n   [adzerk/boot-reload \"0.5.1\" :scope \"test\"]\n   [binaryage/devtools \"0.9.1\" :scope \"test\"]\n   [boot-codox \"0.10.1\" :scope \"test\"]\n   [com.cemerick/piggieback \"0.2.1\" :scope \"test\"]\n   [crisptrutski/boot-cljs-test \"0.3.0\" :scope \"test\"]\n   [deraen/boot-sass \"0.3.0\" :scope \"test\"]\n   [devcards \"0.2.2\" :scope \"test\"]\n   [metosin/boot-alt-http \"0.1.2\" :scope \"test\"]\n   [org.clojars.micha/boot-cp \"1.0.1\" :scope \"test\"]\n   [org.clojure/tools.nrepl \"0.2.12\" :scope \"test\"]\n   [org.martinklepsch/boot-gzip \"0.1.3\" :scope \"test\"]\n   [org.slf4j/slf4j-nop \"1.7.21\" :scope \"test\"]\n   [perun \"0.4.1-SNAPSHOT\" :scope \"test\"]\n   [weasel \"0.7.0\" :scope \"test\"]\n\n   ;; App\n   [cljsjs/clipboard \"1.5.9-0\"]\n   [cljsjs/raven \"3.9.1-0\"]\n   [cljsjs/react \"15.4.2-2\"]\n   [cljsjs/react-bootstrap \"0.30.6-0\"]\n   [cljsjs/react-dom \"15.4.2-2\"]\n   [cljsjs/react-dom-server \"15.4.2-2\"]\n   [com.cognitect/transit-cljs \"0.8.239\"]\n   [com.gfredericks/test.chuck \"0.2.7\"]\n   [danielsz/boot-autoprefixer \"0.0.9\"]\n   [datascript \"0.15.5\"]\n   [hiccup \"1.0.5\"]\n   [integrant \"0.2.1\"]\n   [org.clojure/clojure \"1.9.0-alpha14\"]\n   [org.clojure/clojurescript \"1.9.293\"]\n   [org.clojure/core.async \"0.2.395\"]\n   [org.clojure/test.check \"0.9.0\"]\n   [org.webjars/bootstrap-sass \"3.3.7\"]\n   [org.webjars/font-awesome \"4.7.0\"]\n   [posh \"0.5.5\"]\n   [reagent \"0.6.1\"]\n   [tongue \"0.2.0\"]])\n\n(require\n '[adzerk.boot-cljs :refer :all]\n '[adzerk.boot-cljs-repl :refer :all]\n '[adzerk.boot-reload :refer :all]\n '[clojure.edn :as edn]\n '[clojure.string :as string]\n '[codox.boot :refer [codox]]\n '[crisptrutski.boot-cljs-test :refer [test-cljs]]\n '[danielsz.autoprefixer :refer [autoprefixer]]\n '[deraen.boot-sass :refer :all]\n '[io.perun :refer :all]\n '[org.martinklepsch.boot-gzip :refer [gzip]]\n '[metosin.boot-alt-http :refer [serve]]\n '[zetawar.site]\n '[integrant.core])\n\n(task-options!\n test-cljs    {:js-env :phantom}\n autoprefixer {:files [\"main.css\"]\n               :browsers \"> 5%\"})\n\n(deftask build-css\n  \"Build Zetawar CSS.\"\n  []\n  (comp\n   (sift :add-jar {'org.webjars/bootstrap-sass #\"META-INF/resources/webjars/bootstrap-sass/3\\.3\\.7/stylesheets/.*\\.scss$\"\n                   'org.webjars/font-awesome #\"META-INF/resources/webjars/font-awesome/4\\.7\\.0/(fonts|scss/.*\\.scss)\"})\n   (sift :move {#\"META-INF/resources/webjars/bootstrap-sass/3\\.3\\.7/stylesheets\" \"bootstrap\"\n                #\"META-INF/resources/webjars/font-awesome/4\\.7\\.0/fonts\" \"fonts\"\n                #\"META-INF/resources/webjars/font-awesome/4\\.7\\.0/scss/(_.*\\.scss)\" \"font-awesome/$1\"\n                #\"META-INF/resources/webjars/font-awesome/4\\.7\\.0/scss/font-awesome.scss\" \"font-awesome/_font-awesome.scss\"})\n   (sift :to-source #{#\"bootstrap\" #\"font-awesome\"}\n         :to-resource #{#\"fonts\"})\n   (sass :options {:precision 8})\n   (autoprefixer :exec-path \"node_modules/.bin/postcss\")\n   (sift :move {#\"^main.css$\" \"css/main.css\"})))\n\n(deftask build-html\n  \"Build Zetawar HTML.\"\n  [m metadata-file FILE str]\n  (comp (global-metadata :filename metadata-file)\n        (markdown)\n        (slug :slug-fn zetawar.site/slug-fn)\n        (permalink :permalink-fn zetawar.site/permalink-fn)\n        (collection :renderer 'zetawar.views.site/render-index\n                    :out-dir \".\"\n                    :page \"index.html\")\n        (collection :renderer 'zetawar.views.site/render-blog-index\n                    :filterer zetawar.site/post?\n                    :out-dir \".\"\n                    :page \"blog/index.html\")\n        (render :renderer 'zetawar.views.site/render-page\n                :filterer zetawar.site/page?\n                :out-dir \".\")\n        (render :renderer 'zetawar.views.site/render-blog-post\n                :filterer zetawar.site/post?\n                :out-dir \".\")\n        (render :renderer 'zetawar.views.site/render-devcards\n                :filterer zetawar.site/devcards?\n                :out-dir \".\")))\n\n(deftask build-cljs\n  \"Build Zetawar ClojureScript for deployment.\"\n  []\n  (cljs :ids [\"js/main\"]\n        :optimizations :advanced\n        :source-map true\n        :compiler-options {:asset-path (str (System/getenv \"ZETAWAR_PREFIX\")\n                                            \"/js/main.out\")\n                           :parallel-build true}))\n\n(deftask build-site\n  \"Build Zetawar site for deployment.\"\n  [e environment ENV  str \"Perun environment\"\n   t target-dir  PATH str \"Target directory\"]\n  (comp (build-cljs)\n        (build-html :metadata-file (str \"perun.base.\" environment \".edn\"))\n        (build-css)\n        (gzip :regex #{#\"\\.html$\" #\"\\.css$\" #\"^(?!.*main\\.out).*\\.js$\"})\n        ;; Fileset gets confused without move to *.orig\n        (sift :move {#\"^(.*)\\.html$\" \"$1.html.orig\"\n                     #\"^(.*)\\.css$\" \"$1.css.orig\"\n                     #\"^(?!.*main\\.out)(.*)\\.js$\" \"$1.js.orig\"})\n        (sift :to-source #{#\"\\.orig$\"})\n        (sift :move {#\"^(.*)\\.html\\.gz$\" \"$1.html\"\n                     #\"^(.*)\\.css\\.gz$\" \"$1.css\"\n                     #\"^(.*)\\.js\\.gz$\" \"$1.js\"})\n        (codox :name \"Zetawar\" :language :clojurescript)\n        (target :dir (when target-dir #{target-dir}))))\n\n(deftask build-cli\n  \"Build Zetawar CLI.\"\n  []\n  (comp (cljs :ids [\"js/cli\"]\n              :optimizations :simple\n              :source-map true)\n        (target)))\n\n(deftask dev\n  \"Run Zetawar dev environment.\"\n  [_ http-host      HOST str \"HTTP server host\"\n   _ http-port      HOST str \"HTTP server port\"\n   _ reload-host    HOST str \"Reload WebSocket host\"\n   _ reload-port    PORT int \"Reload WebSocket port\"\n   _ cljs-repl-host HOST str \"ClojureScript REPL host\"\n   _ cljs-repl-port PORT int \"ClojureScript REPL port\"]\n  (comp (serve :ip (or http-host\n                       (System/getenv \"ZETAWAR_DEV_HOST\")\n                       \"127.0.0.1\")\n               :port (Integer. (or http-port\n                                   (System/getenv \"ZETAWAR_HTTP_PORT\")\n                                   3000))\n               :prefixes #{\".\"})\n        (repl)\n        (watch)\n        (notify :visual true)\n        (build-html :metadata-file \"perun.base.dev.edn\")\n        (build-css)\n        (reload :on-jsload 'zetawar.core/reload\n                :cljs-asset-path \"\"\n                :ws-host (or reload-host\n                             (System/getenv \"ZETAWAR_RELOAD_HOST\")\n                             (System/getenv \"ZETAWAR_DEV_HOST\"))\n                :ws-port (or reload-host\n                             (System/getenv \"ZETAWAR_RELOAD_PORT\")))\n        (cljs-repl-env :ws-host (or reload-host\n                                    (System/getenv \"ZETAWAR_CLJS_REPL_HOST\")\n                                    (System/getenv \"ZETAWAR_DEV_HOST\"))\n                       :port (or reload-host\n                                 (System/getenv \"ZETAWAR_CLJS_REPL_PORT\")))\n        (cljs :ids [\"js/main\"]\n              :optimizations :none\n              :compiler-options {:preloads '[zetawar.dev]\n                                 :parallel-build true})\n        (target)))\n\n(deftask dev-cli\n  \"Build Zetawar CLI dev environment.\"\n  []\n  (comp (watch)\n        (cljs :ids [\"js/cli\"]\n              :optimizations :none\n              :source-map true)\n        (target)))\n\n(deftask run-tests\n  \"Run Zetawar tests.\"\n  []\n  (test-cljs :exit? true\n             :cljs-opts {:externs [\"js/externs.js\"]\n                         :foreign-libs [{:file \"lzw.js\"\n                                         :provides [\"lzw\"]}]}))\n\n(deftask serve-target\n  \"Serve files in target (useful for checking builds).\"\n  []\n  (comp (serve :dir \"target\")\n        (wait)))\n"
  },
  {
    "path": "circle.yml",
    "content": "general:\n  artifacts:\n    - \"target\"\nmachine:\n  java:\n    version: oraclejdk8\n  node:\n    version: 6.9.1\n  environment:\n    _JAVA_OPTIONS: \"-Xms512m -Xmx1024m\"\ndependencies:\n  cache_directories:\n    - \"node_modules\"\n    - \"~/.boot/cache/bin\"\n    - \"~/.boot/cache/lib\"\n  pre:\n    - curl -L https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh -o ~/bin/boot\n    - chmod +x ~/bin/boot\n    - npm install\n  override:\n    - boot show -d\ntest:\n  override:\n    - boot run-tests\n    - boot build-site -e test\n"
  },
  {
    "path": "default.nix",
    "content": "with import <nixpkgs> {}; {\n  sdlEnv = stdenv.mkDerivation {\n    name = \"zetawar\";\n    buildInputs = [ boot git nodejs phantomjs2 s3cmd stdenv ];\n    LD_LIBRARY_PATH=\"${stdenv.cc.cc.lib}/lib\";\n  };\n}\n"
  },
  {
    "path": "deps.edn",
    "content": "{:paths [\"assets\" \"src/clj\" \"src/cljc\" \"src/cljs\"]\n :deps\n {com.cognitect/transit-cljs {:mvn/version \"0.8.239\"}\n  datascript {:mvn/version \"0.15.5\"}\n  integrant {:mvn/version \"0.2.1\"}\n  org.clojure/clojurescript {:mvn/version \"1.10.439\"}\n  org.clojure/core.async {:mvn/version \"0.2.395\"}\n  org.clojure/tools.cli {:mvn/version \"0.4.1\"}\n  posh {:mvn/version \"0.5.5\"}\n  reagent {:mvn/version \"0.6.1\"}}\n\n :aliases\n {:build-dev {:extra-paths [\"test/cljs\"]\n              :extra-deps\n              {com.bhauman/figwheel-main {:mvn/version \"0.1.9\"}\n               com.bhauman/rebel-readline-cljs {:mvn/version \"0.1.4\"}\n               devcards {:mvn/version \"0.2.2\"}}\n              :main-opts [\"-m\" \"figwheel.main\" \"-b\" \"dev\" \"-r\"]}}}\n"
  },
  {
    "path": "dev/cljs/user.cljs",
    "content": "(ns cljs.user\n  (:require\n   [datascript.core :as d]\n   [zetawar.core :as core]\n   [zetawar.game :as game]\n   [zetawar.util :as util]))\n\n(comment\n\n  ;; capturing units\n  (doall\n   (d/q '[:find ?u\n          :in $ ?r\n          :where\n          [_ :game/current-faction ?f]\n          [?f :faction/units ?u]\n          [?u :unit/capturing true]\n          [?u :unit/capture-round ?r]]\n        @core/conn 1))\n\n  )\n"
  },
  {
    "path": "dev.cljs.edn",
    "content": "^{:connect-url \"ws://penguin.linux.test:9500/figwheel-connect\"}\n{:main zetawar.test-runner}\n"
  },
  {
    "path": "npm-shrinkwrap.json",
    "content": "{\n  \"name\": \"zetawar\",\n  \"version\": \"0.1.0\",\n  \"lockfileVersion\": 1,\n  \"requires\": true,\n  \"dependencies\": {\n    \"ansi-regex\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"http://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz\",\n      \"integrity\": \"sha1-xQYbbg74qBd15Q9dZhUb9r83EQc=\",\n      \"dev\": true\n    },\n    \"ansi-styles\": {\n      \"version\": \"2.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz\",\n      \"integrity\": \"sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=\",\n      \"dev\": true\n    },\n    \"anymatch\": {\n      \"version\": \"1.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz\",\n      \"integrity\": \"sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"arrify\": \"^1.0.0\",\n        \"micromatch\": \"^2.1.5\"\n      }\n    },\n    \"arr-diff\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz\",\n      \"integrity\": \"sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"arr-flatten\": \"^1.0.1\"\n      }\n    },\n    \"arr-flatten\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz\",\n      \"integrity\": \"sha1-5f/lTUXhnzLyFukeuZyM6JK7YEs=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"array-union\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz\",\n      \"integrity\": \"sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=\",\n      \"dev\": true,\n      \"requires\": {\n        \"array-uniq\": \"^1.0.1\"\n      }\n    },\n    \"array-uniq\": {\n      \"version\": \"1.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz\",\n      \"integrity\": \"sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=\",\n      \"dev\": true\n    },\n    \"array-unique\": {\n      \"version\": \"0.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz\",\n      \"integrity\": \"sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"arrify\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz\",\n      \"integrity\": \"sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=\",\n      \"dev\": true\n    },\n    \"async-each\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz\",\n      \"integrity\": \"sha1-GdOGodntxufByF04iu28xW0zYC0=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"autoprefixer\": {\n      \"version\": \"6.5.3\",\n      \"resolved\": \"http://registry.npmjs.org/autoprefixer/-/autoprefixer-6.5.3.tgz\",\n      \"integrity\": \"sha1-LYU69m0ERJ/PUNswZieatUw+SwE=\",\n      \"dev\": true,\n      \"requires\": {\n        \"browserslist\": \"~1.4.0\",\n        \"caniuse-db\": \"^1.0.30000578\",\n        \"normalize-range\": \"^0.1.2\",\n        \"num2fraction\": \"^1.2.2\",\n        \"postcss\": \"^5.2.5\",\n        \"postcss-value-parser\": \"^3.2.3\"\n      }\n    },\n    \"balanced-match\": {\n      \"version\": \"0.4.2\",\n      \"resolved\": \"https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz\",\n      \"integrity\": \"sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=\",\n      \"dev\": true\n    },\n    \"binary-extensions\": {\n      \"version\": \"1.8.0\",\n      \"resolved\": \"http://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz\",\n      \"integrity\": \"sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"brace-expansion\": {\n      \"version\": \"1.1.6\",\n      \"resolved\": \"https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz\",\n      \"integrity\": \"sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk=\",\n      \"dev\": true,\n      \"requires\": {\n        \"balanced-match\": \"^0.4.1\",\n        \"concat-map\": \"0.0.1\"\n      }\n    },\n    \"braces\": {\n      \"version\": \"1.8.5\",\n      \"resolved\": \"https://registry.npmjs.org/braces/-/braces-1.8.5.tgz\",\n      \"integrity\": \"sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"expand-range\": \"^1.8.1\",\n        \"preserve\": \"^0.2.0\",\n        \"repeat-element\": \"^1.1.2\"\n      }\n    },\n    \"browserslist\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"http://registry.npmjs.org/browserslist/-/browserslist-1.4.0.tgz\",\n      \"integrity\": \"sha1-nP3PU4TZFY9bcNoqoAsw6P8BkEk=\",\n      \"dev\": true,\n      \"requires\": {\n        \"caniuse-db\": \"^1.0.30000539\"\n      }\n    },\n    \"buffer-from\": {\n      \"version\": \"1.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz\",\n      \"integrity\": \"sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==\",\n      \"dev\": true\n    },\n    \"buffer-shims\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz\",\n      \"integrity\": \"sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"builtin-modules\": {\n      \"version\": \"1.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz\",\n      \"integrity\": \"sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=\",\n      \"dev\": true\n    },\n    \"camelcase\": {\n      \"version\": \"3.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz\",\n      \"integrity\": \"sha1-MvxLn82vhF/N9+c7uXysImHwqwo=\",\n      \"dev\": true\n    },\n    \"caniuse-db\": {\n      \"version\": \"1.0.30000591\",\n      \"resolved\": \"http://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000591.tgz\",\n      \"integrity\": \"sha1-dNe+fOIsi2sI869mR9GU5TSY25c=\",\n      \"dev\": true\n    },\n    \"chalk\": {\n      \"version\": \"1.1.3\",\n      \"resolved\": \"http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz\",\n      \"integrity\": \"sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=\",\n      \"dev\": true,\n      \"requires\": {\n        \"ansi-styles\": \"^2.2.1\",\n        \"escape-string-regexp\": \"^1.0.2\",\n        \"has-ansi\": \"^2.0.0\",\n        \"strip-ansi\": \"^3.0.0\",\n        \"supports-color\": \"^2.0.0\"\n      },\n      \"dependencies\": {\n        \"supports-color\": {\n          \"version\": \"2.0.0\",\n          \"resolved\": \"https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz\",\n          \"integrity\": \"sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=\",\n          \"dev\": true\n        }\n      }\n    },\n    \"chokidar\": {\n      \"version\": \"1.6.1\",\n      \"resolved\": \"http://registry.npmjs.org/chokidar/-/chokidar-1.6.1.tgz\",\n      \"integrity\": \"sha1-L0RHq16W5Q+z14n9kNTHLg5McMI=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"anymatch\": \"^1.3.0\",\n        \"async-each\": \"^1.0.0\",\n        \"fsevents\": \"^1.0.0\",\n        \"glob-parent\": \"^2.0.0\",\n        \"inherits\": \"^2.0.1\",\n        \"is-binary-path\": \"^1.0.0\",\n        \"is-glob\": \"^2.0.0\",\n        \"path-is-absolute\": \"^1.0.0\",\n        \"readdirp\": \"^2.0.0\"\n      }\n    },\n    \"cliui\": {\n      \"version\": \"3.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz\",\n      \"integrity\": \"sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=\",\n      \"dev\": true,\n      \"requires\": {\n        \"string-width\": \"^1.0.1\",\n        \"strip-ansi\": \"^3.0.1\",\n        \"wrap-ansi\": \"^2.0.0\"\n      }\n    },\n    \"code-point-at\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz\",\n      \"integrity\": \"sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=\",\n      \"dev\": true\n    },\n    \"concat-map\": {\n      \"version\": \"0.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz\",\n      \"integrity\": \"sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=\",\n      \"dev\": true\n    },\n    \"core-util-is\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz\",\n      \"integrity\": \"sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"decamelize\": {\n      \"version\": \"1.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz\",\n      \"integrity\": \"sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=\",\n      \"dev\": true\n    },\n    \"error-ex\": {\n      \"version\": \"1.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/error-ex/-/error-ex-1.3.0.tgz\",\n      \"integrity\": \"sha1-5ntD8+gsluo6WE/+4Ln8MyXYAtk=\",\n      \"dev\": true,\n      \"requires\": {\n        \"is-arrayish\": \"^0.2.1\"\n      }\n    },\n    \"escape-string-regexp\": {\n      \"version\": \"1.0.5\",\n      \"resolved\": \"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz\",\n      \"integrity\": \"sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=\",\n      \"dev\": true\n    },\n    \"expand-brackets\": {\n      \"version\": \"0.1.5\",\n      \"resolved\": \"https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz\",\n      \"integrity\": \"sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"is-posix-bracket\": \"^0.1.0\"\n      }\n    },\n    \"expand-range\": {\n      \"version\": \"1.8.2\",\n      \"resolved\": \"https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz\",\n      \"integrity\": \"sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"fill-range\": \"^2.1.0\"\n      }\n    },\n    \"extglob\": {\n      \"version\": \"0.3.2\",\n      \"resolved\": \"https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz\",\n      \"integrity\": \"sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"is-extglob\": \"^1.0.0\"\n      }\n    },\n    \"filename-regex\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz\",\n      \"integrity\": \"sha1-mW4+gEebmLmJfxWopYs9CE6SZ3U=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"fill-range\": {\n      \"version\": \"2.2.3\",\n      \"resolved\": \"https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz\",\n      \"integrity\": \"sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"is-number\": \"^2.1.0\",\n        \"isobject\": \"^2.0.0\",\n        \"randomatic\": \"^1.1.3\",\n        \"repeat-element\": \"^1.1.2\",\n        \"repeat-string\": \"^1.5.2\"\n      }\n    },\n    \"find-up\": {\n      \"version\": \"1.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz\",\n      \"integrity\": \"sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=\",\n      \"dev\": true,\n      \"requires\": {\n        \"path-exists\": \"^2.0.0\",\n        \"pinkie-promise\": \"^2.0.0\"\n      }\n    },\n    \"for-in\": {\n      \"version\": \"0.1.6\",\n      \"resolved\": \"https://registry.npmjs.org/for-in/-/for-in-0.1.6.tgz\",\n      \"integrity\": \"sha1-yfluib+tGKVFr17D7TUqHZ5bTcg=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"for-own\": {\n      \"version\": \"0.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/for-own/-/for-own-0.1.4.tgz\",\n      \"integrity\": \"sha1-AUm0GjkIjHUV9R6+HBOG1F+TUHI=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"for-in\": \"^0.1.5\"\n      }\n    },\n    \"fsevents\": {\n      \"version\": \"1.2.4\",\n      \"resolved\": \"https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz\",\n      \"integrity\": \"sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"nan\": \"^2.9.2\",\n        \"node-pre-gyp\": \"^0.10.0\"\n      },\n      \"dependencies\": {\n        \"abbrev\": {\n          \"version\": \"1.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"ansi-regex\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"aproba\": {\n          \"version\": \"1.2.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"are-we-there-yet\": {\n          \"version\": \"1.1.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"delegates\": \"^1.0.0\",\n            \"readable-stream\": \"^2.0.6\"\n          }\n        },\n        \"balanced-match\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"brace-expansion\": {\n          \"version\": \"1.1.11\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"balanced-match\": \"^1.0.0\",\n            \"concat-map\": \"0.0.1\"\n          }\n        },\n        \"chownr\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"code-point-at\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"concat-map\": {\n          \"version\": \"0.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"console-control-strings\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"core-util-is\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"debug\": {\n          \"version\": \"2.6.9\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"ms\": \"2.0.0\"\n          }\n        },\n        \"deep-extend\": {\n          \"version\": \"0.5.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"delegates\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"detect-libc\": {\n          \"version\": \"1.0.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"fs-minipass\": {\n          \"version\": \"1.2.5\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"minipass\": \"^2.2.1\"\n          }\n        },\n        \"fs.realpath\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"gauge\": {\n          \"version\": \"2.7.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"aproba\": \"^1.0.3\",\n            \"console-control-strings\": \"^1.0.0\",\n            \"has-unicode\": \"^2.0.0\",\n            \"object-assign\": \"^4.1.0\",\n            \"signal-exit\": \"^3.0.0\",\n            \"string-width\": \"^1.0.1\",\n            \"strip-ansi\": \"^3.0.1\",\n            \"wide-align\": \"^1.1.0\"\n          }\n        },\n        \"glob\": {\n          \"version\": \"7.1.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"fs.realpath\": \"^1.0.0\",\n            \"inflight\": \"^1.0.4\",\n            \"inherits\": \"2\",\n            \"minimatch\": \"^3.0.4\",\n            \"once\": \"^1.3.0\",\n            \"path-is-absolute\": \"^1.0.0\"\n          }\n        },\n        \"has-unicode\": {\n          \"version\": \"2.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"iconv-lite\": {\n          \"version\": \"0.4.21\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"safer-buffer\": \"^2.1.0\"\n          }\n        },\n        \"ignore-walk\": {\n          \"version\": \"3.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"minimatch\": \"^3.0.4\"\n          }\n        },\n        \"inflight\": {\n          \"version\": \"1.0.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"once\": \"^1.3.0\",\n            \"wrappy\": \"1\"\n          }\n        },\n        \"inherits\": {\n          \"version\": \"2.0.3\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"ini\": {\n          \"version\": \"1.3.5\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"is-fullwidth-code-point\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"number-is-nan\": \"^1.0.0\"\n          }\n        },\n        \"isarray\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"minimatch\": {\n          \"version\": \"3.0.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"brace-expansion\": \"^1.1.7\"\n          }\n        },\n        \"minimist\": {\n          \"version\": \"0.0.8\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"minipass\": {\n          \"version\": \"2.2.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"safe-buffer\": \"^5.1.1\",\n            \"yallist\": \"^3.0.0\"\n          }\n        },\n        \"minizlib\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"minipass\": \"^2.2.1\"\n          }\n        },\n        \"mkdirp\": {\n          \"version\": \"0.5.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"minimist\": \"0.0.8\"\n          }\n        },\n        \"ms\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"needle\": {\n          \"version\": \"2.2.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"debug\": \"^2.1.2\",\n            \"iconv-lite\": \"^0.4.4\",\n            \"sax\": \"^1.2.4\"\n          }\n        },\n        \"node-pre-gyp\": {\n          \"version\": \"0.10.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"detect-libc\": \"^1.0.2\",\n            \"mkdirp\": \"^0.5.1\",\n            \"needle\": \"^2.2.0\",\n            \"nopt\": \"^4.0.1\",\n            \"npm-packlist\": \"^1.1.6\",\n            \"npmlog\": \"^4.0.2\",\n            \"rc\": \"^1.1.7\",\n            \"rimraf\": \"^2.6.1\",\n            \"semver\": \"^5.3.0\",\n            \"tar\": \"^4\"\n          }\n        },\n        \"nopt\": {\n          \"version\": \"4.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"abbrev\": \"1\",\n            \"osenv\": \"^0.1.4\"\n          }\n        },\n        \"npm-bundled\": {\n          \"version\": \"1.0.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"npm-packlist\": {\n          \"version\": \"1.1.10\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"ignore-walk\": \"^3.0.1\",\n            \"npm-bundled\": \"^1.0.1\"\n          }\n        },\n        \"npmlog\": {\n          \"version\": \"4.1.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"are-we-there-yet\": \"~1.1.2\",\n            \"console-control-strings\": \"~1.1.0\",\n            \"gauge\": \"~2.7.3\",\n            \"set-blocking\": \"~2.0.0\"\n          }\n        },\n        \"number-is-nan\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"object-assign\": {\n          \"version\": \"4.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"once\": {\n          \"version\": \"1.4.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"wrappy\": \"1\"\n          }\n        },\n        \"os-homedir\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"os-tmpdir\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"osenv\": {\n          \"version\": \"0.1.5\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"os-homedir\": \"^1.0.0\",\n            \"os-tmpdir\": \"^1.0.0\"\n          }\n        },\n        \"path-is-absolute\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"process-nextick-args\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"rc\": {\n          \"version\": \"1.2.7\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"deep-extend\": \"^0.5.1\",\n            \"ini\": \"~1.3.0\",\n            \"minimist\": \"^1.2.0\",\n            \"strip-json-comments\": \"~2.0.1\"\n          },\n          \"dependencies\": {\n            \"minimist\": {\n              \"version\": \"1.2.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"optional\": true\n            }\n          }\n        },\n        \"readable-stream\": {\n          \"version\": \"2.3.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"core-util-is\": \"~1.0.0\",\n            \"inherits\": \"~2.0.3\",\n            \"isarray\": \"~1.0.0\",\n            \"process-nextick-args\": \"~2.0.0\",\n            \"safe-buffer\": \"~5.1.1\",\n            \"string_decoder\": \"~1.1.1\",\n            \"util-deprecate\": \"~1.0.1\"\n          }\n        },\n        \"rimraf\": {\n          \"version\": \"2.6.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"glob\": \"^7.0.5\"\n          }\n        },\n        \"safe-buffer\": {\n          \"version\": \"5.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"safer-buffer\": {\n          \"version\": \"2.1.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"sax\": {\n          \"version\": \"1.2.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"semver\": {\n          \"version\": \"5.5.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"set-blocking\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"signal-exit\": {\n          \"version\": \"3.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"string-width\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"code-point-at\": \"^1.0.0\",\n            \"is-fullwidth-code-point\": \"^1.0.0\",\n            \"strip-ansi\": \"^3.0.0\"\n          }\n        },\n        \"string_decoder\": {\n          \"version\": \"1.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"safe-buffer\": \"~5.1.0\"\n          }\n        },\n        \"strip-ansi\": {\n          \"version\": \"3.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"ansi-regex\": \"^2.0.0\"\n          }\n        },\n        \"strip-json-comments\": {\n          \"version\": \"2.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"tar\": {\n          \"version\": \"4.4.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"chownr\": \"^1.0.1\",\n            \"fs-minipass\": \"^1.2.5\",\n            \"minipass\": \"^2.2.4\",\n            \"minizlib\": \"^1.1.0\",\n            \"mkdirp\": \"^0.5.0\",\n            \"safe-buffer\": \"^5.1.1\",\n            \"yallist\": \"^3.0.2\"\n          }\n        },\n        \"util-deprecate\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"wide-align\": {\n          \"version\": \"1.1.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"string-width\": \"^1.0.2\"\n          }\n        },\n        \"wrappy\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"yallist\": {\n          \"version\": \"3.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        }\n      }\n    },\n    \"gather-stream\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/gather-stream/-/gather-stream-1.0.0.tgz\",\n      \"integrity\": \"sha1-szmUr0V6gRVwDUEPMXczy+egkEs=\",\n      \"dev\": true\n    },\n    \"get-caller-file\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz\",\n      \"integrity\": \"sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=\",\n      \"dev\": true\n    },\n    \"glob\": {\n      \"version\": \"6.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/glob/-/glob-6.0.4.tgz\",\n      \"integrity\": \"sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=\",\n      \"dev\": true,\n      \"requires\": {\n        \"inflight\": \"^1.0.4\",\n        \"inherits\": \"2\",\n        \"minimatch\": \"2 || 3\",\n        \"once\": \"^1.3.0\",\n        \"path-is-absolute\": \"^1.0.0\"\n      }\n    },\n    \"glob-base\": {\n      \"version\": \"0.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz\",\n      \"integrity\": \"sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"glob-parent\": \"^2.0.0\",\n        \"is-glob\": \"^2.0.0\"\n      }\n    },\n    \"glob-parent\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz\",\n      \"integrity\": \"sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=\",\n      \"dev\": true,\n      \"requires\": {\n        \"is-glob\": \"^2.0.0\"\n      }\n    },\n    \"globby\": {\n      \"version\": \"4.1.0\",\n      \"resolved\": \"http://registry.npmjs.org/globby/-/globby-4.1.0.tgz\",\n      \"integrity\": \"sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=\",\n      \"dev\": true,\n      \"requires\": {\n        \"array-union\": \"^1.0.1\",\n        \"arrify\": \"^1.0.0\",\n        \"glob\": \"^6.0.1\",\n        \"object-assign\": \"^4.0.1\",\n        \"pify\": \"^2.0.0\",\n        \"pinkie-promise\": \"^2.0.0\"\n      }\n    },\n    \"graceful-fs\": {\n      \"version\": \"4.1.11\",\n      \"resolved\": \"http://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz\",\n      \"integrity\": \"sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=\",\n      \"dev\": true\n    },\n    \"has-ansi\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz\",\n      \"integrity\": \"sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=\",\n      \"dev\": true,\n      \"requires\": {\n        \"ansi-regex\": \"^2.0.0\"\n      }\n    },\n    \"has-flag\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz\",\n      \"integrity\": \"sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=\",\n      \"dev\": true\n    },\n    \"hosted-git-info\": {\n      \"version\": \"2.1.5\",\n      \"resolved\": \"https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.1.5.tgz\",\n      \"integrity\": \"sha1-C6gdkNouJas0ozLm7HeTbhWYEYs=\",\n      \"dev\": true\n    },\n    \"inflight\": {\n      \"version\": \"1.0.6\",\n      \"resolved\": \"https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz\",\n      \"integrity\": \"sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=\",\n      \"dev\": true,\n      \"requires\": {\n        \"once\": \"^1.3.0\",\n        \"wrappy\": \"1\"\n      }\n    },\n    \"inherits\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz\",\n      \"integrity\": \"sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=\",\n      \"dev\": true\n    },\n    \"invert-kv\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz\",\n      \"integrity\": \"sha1-EEqOSqym09jNFXqO+L+rLXo//bY=\",\n      \"dev\": true\n    },\n    \"is-arrayish\": {\n      \"version\": \"0.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz\",\n      \"integrity\": \"sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=\",\n      \"dev\": true\n    },\n    \"is-binary-path\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz\",\n      \"integrity\": \"sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"binary-extensions\": \"^1.0.0\"\n      }\n    },\n    \"is-buffer\": {\n      \"version\": \"1.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.4.tgz\",\n      \"integrity\": \"sha1-z8hszV3FpS+oBIkRHGkgxFfi2Ys=\",\n      \"dev\": true\n    },\n    \"is-builtin-module\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz\",\n      \"integrity\": \"sha1-VAVy0096wxGfj3bDDLwbHgN6/74=\",\n      \"dev\": true,\n      \"requires\": {\n        \"builtin-modules\": \"^1.0.0\"\n      }\n    },\n    \"is-dotfile\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.2.tgz\",\n      \"integrity\": \"sha1-LBMjg/ORmfjtwmjKAbmwB9IFzE0=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"is-equal-shallow\": {\n      \"version\": \"0.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz\",\n      \"integrity\": \"sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"is-primitive\": \"^2.0.0\"\n      }\n    },\n    \"is-extendable\": {\n      \"version\": \"0.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz\",\n      \"integrity\": \"sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"is-extglob\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz\",\n      \"integrity\": \"sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=\",\n      \"dev\": true\n    },\n    \"is-fullwidth-code-point\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz\",\n      \"integrity\": \"sha1-754xOG8DGn8NZDr4L95QxFfvAMs=\",\n      \"dev\": true,\n      \"requires\": {\n        \"number-is-nan\": \"^1.0.0\"\n      }\n    },\n    \"is-glob\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz\",\n      \"integrity\": \"sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=\",\n      \"dev\": true,\n      \"requires\": {\n        \"is-extglob\": \"^1.0.0\"\n      }\n    },\n    \"is-number\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz\",\n      \"integrity\": \"sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=\",\n      \"dev\": true,\n      \"requires\": {\n        \"kind-of\": \"^3.0.2\"\n      }\n    },\n    \"is-posix-bracket\": {\n      \"version\": \"0.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz\",\n      \"integrity\": \"sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"is-primitive\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz\",\n      \"integrity\": \"sha1-IHurkWOEmcB7Kt8kCkGochADRXU=\",\n      \"dev\": true\n    },\n    \"is-utf8\": {\n      \"version\": \"0.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz\",\n      \"integrity\": \"sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=\",\n      \"dev\": true\n    },\n    \"isarray\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz\",\n      \"integrity\": \"sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=\",\n      \"dev\": true\n    },\n    \"isobject\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz\",\n      \"integrity\": \"sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"isarray\": \"1.0.0\"\n      }\n    },\n    \"js-base64\": {\n      \"version\": \"2.1.9\",\n      \"resolved\": \"https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz\",\n      \"integrity\": \"sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=\",\n      \"dev\": true\n    },\n    \"kind-of\": {\n      \"version\": \"3.0.4\",\n      \"resolved\": \"http://registry.npmjs.org/kind-of/-/kind-of-3.0.4.tgz\",\n      \"integrity\": \"sha1-e47PGKThf4Jp1ztQHJ8jLJaIenQ=\",\n      \"dev\": true,\n      \"requires\": {\n        \"is-buffer\": \"^1.0.2\"\n      }\n    },\n    \"lcid\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz\",\n      \"integrity\": \"sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=\",\n      \"dev\": true,\n      \"requires\": {\n        \"invert-kv\": \"^1.0.0\"\n      }\n    },\n    \"load-json-file\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz\",\n      \"integrity\": \"sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=\",\n      \"dev\": true,\n      \"requires\": {\n        \"graceful-fs\": \"^4.1.2\",\n        \"parse-json\": \"^2.2.0\",\n        \"pify\": \"^2.0.0\",\n        \"pinkie-promise\": \"^2.0.0\",\n        \"strip-bom\": \"^2.0.0\"\n      }\n    },\n    \"lodash.assign\": {\n      \"version\": \"4.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz\",\n      \"integrity\": \"sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=\",\n      \"dev\": true\n    },\n    \"micromatch\": {\n      \"version\": \"2.3.11\",\n      \"resolved\": \"https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz\",\n      \"integrity\": \"sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"arr-diff\": \"^2.0.0\",\n        \"array-unique\": \"^0.2.1\",\n        \"braces\": \"^1.8.2\",\n        \"expand-brackets\": \"^0.1.4\",\n        \"extglob\": \"^0.3.1\",\n        \"filename-regex\": \"^2.0.0\",\n        \"is-extglob\": \"^1.0.0\",\n        \"is-glob\": \"^2.0.1\",\n        \"kind-of\": \"^3.0.2\",\n        \"normalize-path\": \"^2.0.1\",\n        \"object.omit\": \"^2.0.0\",\n        \"parse-glob\": \"^3.0.4\",\n        \"regex-cache\": \"^0.4.2\"\n      }\n    },\n    \"minimatch\": {\n      \"version\": \"3.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz\",\n      \"integrity\": \"sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=\",\n      \"dev\": true,\n      \"requires\": {\n        \"brace-expansion\": \"^1.0.0\"\n      }\n    },\n    \"minimist\": {\n      \"version\": \"0.0.8\",\n      \"resolved\": \"http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz\",\n      \"integrity\": \"sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=\",\n      \"dev\": true\n    },\n    \"mkdirp\": {\n      \"version\": \"0.5.1\",\n      \"resolved\": \"http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz\",\n      \"integrity\": \"sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=\",\n      \"dev\": true,\n      \"requires\": {\n        \"minimist\": \"0.0.8\"\n      }\n    },\n    \"nan\": {\n      \"version\": \"2.11.1\",\n      \"resolved\": \"https://registry.npmjs.org/nan/-/nan-2.11.1.tgz\",\n      \"integrity\": \"sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"neo-async\": {\n      \"version\": \"1.8.2\",\n      \"resolved\": \"http://registry.npmjs.org/neo-async/-/neo-async-1.8.2.tgz\",\n      \"integrity\": \"sha1-MXlYiLed0ENXp8UhE6ZRg+k7ZzU=\",\n      \"dev\": true\n    },\n    \"normalize-package-data\": {\n      \"version\": \"2.3.5\",\n      \"resolved\": \"https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.5.tgz\",\n      \"integrity\": \"sha1-jZJPFClg4Xd+f/4XBUNjHMfLAt8=\",\n      \"dev\": true,\n      \"requires\": {\n        \"hosted-git-info\": \"^2.1.4\",\n        \"is-builtin-module\": \"^1.0.0\",\n        \"semver\": \"2 || 3 || 4 || 5\",\n        \"validate-npm-package-license\": \"^3.0.1\"\n      }\n    },\n    \"normalize-path\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/normalize-path/-/normalize-path-2.0.1.tgz\",\n      \"integrity\": \"sha1-R4hqwWYnYNQmG32XnSQXCdPOP3o=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"normalize-range\": {\n      \"version\": \"0.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz\",\n      \"integrity\": \"sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=\",\n      \"dev\": true\n    },\n    \"num2fraction\": {\n      \"version\": \"1.2.2\",\n      \"resolved\": \"https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz\",\n      \"integrity\": \"sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=\",\n      \"dev\": true\n    },\n    \"number-is-nan\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz\",\n      \"integrity\": \"sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=\",\n      \"dev\": true\n    },\n    \"nyc\": {\n      \"version\": \"11.9.0\",\n      \"resolved\": \"https://registry.npmjs.org/nyc/-/nyc-11.9.0.tgz\",\n      \"integrity\": \"sha512-w8OdJAhXL5izerzZMdqzYKMj/pgHJyY3qEPYBjLLxrhcVoHEY9pU5ENIiZyCgG9OR7x3VcUMoD40o6PtVpfR4g==\",\n      \"dev\": true,\n      \"requires\": {\n        \"archy\": \"^1.0.0\",\n        \"arrify\": \"^1.0.1\",\n        \"caching-transform\": \"^1.0.0\",\n        \"convert-source-map\": \"^1.5.1\",\n        \"debug-log\": \"^1.0.1\",\n        \"default-require-extensions\": \"^1.0.0\",\n        \"find-cache-dir\": \"^0.1.1\",\n        \"find-up\": \"^2.1.0\",\n        \"foreground-child\": \"^1.5.3\",\n        \"glob\": \"^7.0.6\",\n        \"istanbul-lib-coverage\": \"^1.1.2\",\n        \"istanbul-lib-hook\": \"^1.1.0\",\n        \"istanbul-lib-instrument\": \"^1.10.0\",\n        \"istanbul-lib-report\": \"^1.1.3\",\n        \"istanbul-lib-source-maps\": \"^1.2.3\",\n        \"istanbul-reports\": \"^1.4.0\",\n        \"md5-hex\": \"^1.2.0\",\n        \"merge-source-map\": \"^1.1.0\",\n        \"micromatch\": \"^3.1.10\",\n        \"mkdirp\": \"^0.5.0\",\n        \"resolve-from\": \"^2.0.0\",\n        \"rimraf\": \"^2.6.2\",\n        \"signal-exit\": \"^3.0.1\",\n        \"spawn-wrap\": \"^1.4.2\",\n        \"test-exclude\": \"^4.2.0\",\n        \"yargs\": \"11.1.0\",\n        \"yargs-parser\": \"^8.0.0\"\n      },\n      \"dependencies\": {\n        \"align-text\": {\n          \"version\": \"0.1.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^3.0.2\",\n            \"longest\": \"^1.0.1\",\n            \"repeat-string\": \"^1.5.2\"\n          }\n        },\n        \"amdefine\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"ansi-regex\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"ansi-styles\": {\n          \"version\": \"2.2.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"append-transform\": {\n          \"version\": \"0.4.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"default-require-extensions\": \"^1.0.0\"\n          }\n        },\n        \"archy\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"arr-diff\": {\n          \"version\": \"4.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"arr-flatten\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"arr-union\": {\n          \"version\": \"3.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"array-unique\": {\n          \"version\": \"0.3.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"arrify\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"assign-symbols\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"async\": {\n          \"version\": \"1.5.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"atob\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"babel-code-frame\": {\n          \"version\": \"6.26.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"chalk\": \"^1.1.3\",\n            \"esutils\": \"^2.0.2\",\n            \"js-tokens\": \"^3.0.2\"\n          }\n        },\n        \"babel-generator\": {\n          \"version\": \"6.26.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"babel-messages\": \"^6.23.0\",\n            \"babel-runtime\": \"^6.26.0\",\n            \"babel-types\": \"^6.26.0\",\n            \"detect-indent\": \"^4.0.0\",\n            \"jsesc\": \"^1.3.0\",\n            \"lodash\": \"^4.17.4\",\n            \"source-map\": \"^0.5.7\",\n            \"trim-right\": \"^1.0.1\"\n          }\n        },\n        \"babel-messages\": {\n          \"version\": \"6.23.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"babel-runtime\": \"^6.22.0\"\n          }\n        },\n        \"babel-runtime\": {\n          \"version\": \"6.26.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"core-js\": \"^2.4.0\",\n            \"regenerator-runtime\": \"^0.11.0\"\n          }\n        },\n        \"babel-template\": {\n          \"version\": \"6.26.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"babel-runtime\": \"^6.26.0\",\n            \"babel-traverse\": \"^6.26.0\",\n            \"babel-types\": \"^6.26.0\",\n            \"babylon\": \"^6.18.0\",\n            \"lodash\": \"^4.17.4\"\n          }\n        },\n        \"babel-traverse\": {\n          \"version\": \"6.26.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"babel-code-frame\": \"^6.26.0\",\n            \"babel-messages\": \"^6.23.0\",\n            \"babel-runtime\": \"^6.26.0\",\n            \"babel-types\": \"^6.26.0\",\n            \"babylon\": \"^6.18.0\",\n            \"debug\": \"^2.6.8\",\n            \"globals\": \"^9.18.0\",\n            \"invariant\": \"^2.2.2\",\n            \"lodash\": \"^4.17.4\"\n          }\n        },\n        \"babel-types\": {\n          \"version\": \"6.26.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"babel-runtime\": \"^6.26.0\",\n            \"esutils\": \"^2.0.2\",\n            \"lodash\": \"^4.17.4\",\n            \"to-fast-properties\": \"^1.0.3\"\n          }\n        },\n        \"babylon\": {\n          \"version\": \"6.18.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"balanced-match\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"base\": {\n          \"version\": \"0.11.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"cache-base\": \"^1.0.1\",\n            \"class-utils\": \"^0.3.5\",\n            \"component-emitter\": \"^1.2.1\",\n            \"define-property\": \"^1.0.0\",\n            \"isobject\": \"^3.0.1\",\n            \"mixin-deep\": \"^1.2.0\",\n            \"pascalcase\": \"^0.1.1\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^1.0.0\"\n              }\n            },\n            \"is-accessor-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-data-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-descriptor\": {\n              \"version\": \"1.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-accessor-descriptor\": \"^1.0.0\",\n                \"is-data-descriptor\": \"^1.0.0\",\n                \"kind-of\": \"^6.0.2\"\n              }\n            },\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"brace-expansion\": {\n          \"version\": \"1.1.11\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"balanced-match\": \"^1.0.0\",\n            \"concat-map\": \"0.0.1\"\n          }\n        },\n        \"braces\": {\n          \"version\": \"2.3.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"arr-flatten\": \"^1.1.0\",\n            \"array-unique\": \"^0.3.2\",\n            \"extend-shallow\": \"^2.0.1\",\n            \"fill-range\": \"^4.0.0\",\n            \"isobject\": \"^3.0.1\",\n            \"repeat-element\": \"^1.1.2\",\n            \"snapdragon\": \"^0.8.1\",\n            \"snapdragon-node\": \"^2.0.1\",\n            \"split-string\": \"^3.0.2\",\n            \"to-regex\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"builtin-modules\": {\n          \"version\": \"1.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"cache-base\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"collection-visit\": \"^1.0.0\",\n            \"component-emitter\": \"^1.2.1\",\n            \"get-value\": \"^2.0.6\",\n            \"has-value\": \"^1.0.0\",\n            \"isobject\": \"^3.0.1\",\n            \"set-value\": \"^2.0.0\",\n            \"to-object-path\": \"^0.3.0\",\n            \"union-value\": \"^1.0.0\",\n            \"unset-value\": \"^1.0.0\"\n          },\n          \"dependencies\": {\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"caching-transform\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"md5-hex\": \"^1.2.0\",\n            \"mkdirp\": \"^0.5.1\",\n            \"write-file-atomic\": \"^1.1.4\"\n          }\n        },\n        \"camelcase\": {\n          \"version\": \"1.2.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"center-align\": {\n          \"version\": \"0.1.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"align-text\": \"^0.1.3\",\n            \"lazy-cache\": \"^1.0.3\"\n          }\n        },\n        \"chalk\": {\n          \"version\": \"1.1.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"ansi-styles\": \"^2.2.1\",\n            \"escape-string-regexp\": \"^1.0.2\",\n            \"has-ansi\": \"^2.0.0\",\n            \"strip-ansi\": \"^3.0.0\",\n            \"supports-color\": \"^2.0.0\"\n          }\n        },\n        \"class-utils\": {\n          \"version\": \"0.3.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"arr-union\": \"^3.1.0\",\n            \"define-property\": \"^0.2.5\",\n            \"isobject\": \"^3.0.0\",\n            \"static-extend\": \"^0.1.1\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"0.2.5\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^0.1.0\"\n              }\n            },\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"cliui\": {\n          \"version\": \"2.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"center-align\": \"^0.1.1\",\n            \"right-align\": \"^0.1.1\",\n            \"wordwrap\": \"0.0.2\"\n          },\n          \"dependencies\": {\n            \"wordwrap\": {\n              \"version\": \"0.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"optional\": true\n            }\n          }\n        },\n        \"code-point-at\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"collection-visit\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"map-visit\": \"^1.0.0\",\n            \"object-visit\": \"^1.0.0\"\n          }\n        },\n        \"commondir\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"component-emitter\": {\n          \"version\": \"1.2.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"concat-map\": {\n          \"version\": \"0.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"convert-source-map\": {\n          \"version\": \"1.5.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"copy-descriptor\": {\n          \"version\": \"0.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"core-js\": {\n          \"version\": \"2.5.6\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"cross-spawn\": {\n          \"version\": \"4.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"lru-cache\": \"^4.0.1\",\n            \"which\": \"^1.2.9\"\n          }\n        },\n        \"debug\": {\n          \"version\": \"2.6.9\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"ms\": \"2.0.0\"\n          }\n        },\n        \"debug-log\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"decamelize\": {\n          \"version\": \"1.2.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"decode-uri-component\": {\n          \"version\": \"0.2.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"default-require-extensions\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"strip-bom\": \"^2.0.0\"\n          }\n        },\n        \"define-property\": {\n          \"version\": \"2.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-descriptor\": \"^1.0.2\",\n            \"isobject\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"is-accessor-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-data-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-descriptor\": {\n              \"version\": \"1.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-accessor-descriptor\": \"^1.0.0\",\n                \"is-data-descriptor\": \"^1.0.0\",\n                \"kind-of\": \"^6.0.2\"\n              }\n            },\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"detect-indent\": {\n          \"version\": \"4.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"repeating\": \"^2.0.0\"\n          }\n        },\n        \"error-ex\": {\n          \"version\": \"1.3.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-arrayish\": \"^0.2.1\"\n          }\n        },\n        \"escape-string-regexp\": {\n          \"version\": \"1.0.5\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"esutils\": {\n          \"version\": \"2.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"execa\": {\n          \"version\": \"0.7.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"cross-spawn\": \"^5.0.1\",\n            \"get-stream\": \"^3.0.0\",\n            \"is-stream\": \"^1.1.0\",\n            \"npm-run-path\": \"^2.0.0\",\n            \"p-finally\": \"^1.0.0\",\n            \"signal-exit\": \"^3.0.0\",\n            \"strip-eof\": \"^1.0.0\"\n          },\n          \"dependencies\": {\n            \"cross-spawn\": {\n              \"version\": \"5.1.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"lru-cache\": \"^4.0.1\",\n                \"shebang-command\": \"^1.2.0\",\n                \"which\": \"^1.2.9\"\n              }\n            }\n          }\n        },\n        \"expand-brackets\": {\n          \"version\": \"2.1.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"debug\": \"^2.3.3\",\n            \"define-property\": \"^0.2.5\",\n            \"extend-shallow\": \"^2.0.1\",\n            \"posix-character-classes\": \"^0.1.0\",\n            \"regex-not\": \"^1.0.0\",\n            \"snapdragon\": \"^0.8.1\",\n            \"to-regex\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"0.2.5\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^0.1.0\"\n              }\n            },\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"extend-shallow\": {\n          \"version\": \"3.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"assign-symbols\": \"^1.0.0\",\n            \"is-extendable\": \"^1.0.1\"\n          },\n          \"dependencies\": {\n            \"is-extendable\": {\n              \"version\": \"1.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-plain-object\": \"^2.0.4\"\n              }\n            }\n          }\n        },\n        \"extglob\": {\n          \"version\": \"2.0.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"array-unique\": \"^0.3.2\",\n            \"define-property\": \"^1.0.0\",\n            \"expand-brackets\": \"^2.1.4\",\n            \"extend-shallow\": \"^2.0.1\",\n            \"fragment-cache\": \"^0.2.1\",\n            \"regex-not\": \"^1.0.0\",\n            \"snapdragon\": \"^0.8.1\",\n            \"to-regex\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^1.0.0\"\n              }\n            },\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            },\n            \"is-accessor-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-data-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-descriptor\": {\n              \"version\": \"1.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-accessor-descriptor\": \"^1.0.0\",\n                \"is-data-descriptor\": \"^1.0.0\",\n                \"kind-of\": \"^6.0.2\"\n              }\n            },\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"fill-range\": {\n          \"version\": \"4.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"extend-shallow\": \"^2.0.1\",\n            \"is-number\": \"^3.0.0\",\n            \"repeat-string\": \"^1.6.1\",\n            \"to-regex-range\": \"^2.1.0\"\n          },\n          \"dependencies\": {\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"find-cache-dir\": {\n          \"version\": \"0.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"commondir\": \"^1.0.1\",\n            \"mkdirp\": \"^0.5.1\",\n            \"pkg-dir\": \"^1.0.0\"\n          }\n        },\n        \"find-up\": {\n          \"version\": \"2.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"locate-path\": \"^2.0.0\"\n          }\n        },\n        \"for-in\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"foreground-child\": {\n          \"version\": \"1.5.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"cross-spawn\": \"^4\",\n            \"signal-exit\": \"^3.0.0\"\n          }\n        },\n        \"fragment-cache\": {\n          \"version\": \"0.2.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"map-cache\": \"^0.2.2\"\n          }\n        },\n        \"fs.realpath\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"get-caller-file\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"get-stream\": {\n          \"version\": \"3.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"get-value\": {\n          \"version\": \"2.0.6\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"glob\": {\n          \"version\": \"7.1.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"fs.realpath\": \"^1.0.0\",\n            \"inflight\": \"^1.0.4\",\n            \"inherits\": \"2\",\n            \"minimatch\": \"^3.0.4\",\n            \"once\": \"^1.3.0\",\n            \"path-is-absolute\": \"^1.0.0\"\n          }\n        },\n        \"globals\": {\n          \"version\": \"9.18.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"graceful-fs\": {\n          \"version\": \"4.1.11\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"handlebars\": {\n          \"version\": \"4.0.11\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"async\": \"^1.4.0\",\n            \"optimist\": \"^0.6.1\",\n            \"source-map\": \"^0.4.4\",\n            \"uglify-js\": \"^2.6\"\n          },\n          \"dependencies\": {\n            \"source-map\": {\n              \"version\": \"0.4.4\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"amdefine\": \">=0.0.4\"\n              }\n            }\n          }\n        },\n        \"has-ansi\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"ansi-regex\": \"^2.0.0\"\n          }\n        },\n        \"has-flag\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"has-value\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"get-value\": \"^2.0.6\",\n            \"has-values\": \"^1.0.0\",\n            \"isobject\": \"^3.0.0\"\n          },\n          \"dependencies\": {\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"has-values\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-number\": \"^3.0.0\",\n            \"kind-of\": \"^4.0.0\"\n          },\n          \"dependencies\": {\n            \"is-number\": {\n              \"version\": \"3.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^3.0.2\"\n              },\n              \"dependencies\": {\n                \"kind-of\": {\n                  \"version\": \"3.2.2\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-buffer\": \"^1.1.5\"\n                  }\n                }\n              }\n            },\n            \"kind-of\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-buffer\": \"^1.1.5\"\n              }\n            }\n          }\n        },\n        \"hosted-git-info\": {\n          \"version\": \"2.6.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"imurmurhash\": {\n          \"version\": \"0.1.4\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"inflight\": {\n          \"version\": \"1.0.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"once\": \"^1.3.0\",\n            \"wrappy\": \"1\"\n          }\n        },\n        \"inherits\": {\n          \"version\": \"2.0.3\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"invariant\": {\n          \"version\": \"2.2.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"loose-envify\": \"^1.0.0\"\n          }\n        },\n        \"invert-kv\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-accessor-descriptor\": {\n          \"version\": \"0.1.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^3.0.2\"\n          }\n        },\n        \"is-arrayish\": {\n          \"version\": \"0.2.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-buffer\": {\n          \"version\": \"1.1.6\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-builtin-module\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"builtin-modules\": \"^1.0.0\"\n          }\n        },\n        \"is-data-descriptor\": {\n          \"version\": \"0.1.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^3.0.2\"\n          }\n        },\n        \"is-descriptor\": {\n          \"version\": \"0.1.6\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-accessor-descriptor\": \"^0.1.6\",\n            \"is-data-descriptor\": \"^0.1.4\",\n            \"kind-of\": \"^5.0.0\"\n          },\n          \"dependencies\": {\n            \"kind-of\": {\n              \"version\": \"5.1.0\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"is-extendable\": {\n          \"version\": \"0.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-finite\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"number-is-nan\": \"^1.0.0\"\n          }\n        },\n        \"is-fullwidth-code-point\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-number\": {\n          \"version\": \"3.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^3.0.2\"\n          }\n        },\n        \"is-odd\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-number\": \"^4.0.0\"\n          },\n          \"dependencies\": {\n            \"is-number\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"is-plain-object\": {\n          \"version\": \"2.0.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"isobject\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"is-stream\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-utf8\": {\n          \"version\": \"0.2.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"is-windows\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"isarray\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"isexe\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"isobject\": {\n          \"version\": \"3.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"istanbul-lib-coverage\": {\n          \"version\": \"1.2.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"istanbul-lib-hook\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"append-transform\": \"^0.4.0\"\n          }\n        },\n        \"istanbul-lib-instrument\": {\n          \"version\": \"1.10.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"babel-generator\": \"^6.18.0\",\n            \"babel-template\": \"^6.16.0\",\n            \"babel-traverse\": \"^6.18.0\",\n            \"babel-types\": \"^6.18.0\",\n            \"babylon\": \"^6.18.0\",\n            \"istanbul-lib-coverage\": \"^1.2.0\",\n            \"semver\": \"^5.3.0\"\n          }\n        },\n        \"istanbul-lib-report\": {\n          \"version\": \"1.1.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"istanbul-lib-coverage\": \"^1.1.2\",\n            \"mkdirp\": \"^0.5.1\",\n            \"path-parse\": \"^1.0.5\",\n            \"supports-color\": \"^3.1.2\"\n          },\n          \"dependencies\": {\n            \"supports-color\": {\n              \"version\": \"3.2.3\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"has-flag\": \"^1.0.0\"\n              }\n            }\n          }\n        },\n        \"istanbul-lib-source-maps\": {\n          \"version\": \"1.2.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"debug\": \"^3.1.0\",\n            \"istanbul-lib-coverage\": \"^1.1.2\",\n            \"mkdirp\": \"^0.5.1\",\n            \"rimraf\": \"^2.6.1\",\n            \"source-map\": \"^0.5.3\"\n          },\n          \"dependencies\": {\n            \"debug\": {\n              \"version\": \"3.1.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"ms\": \"2.0.0\"\n              }\n            }\n          }\n        },\n        \"istanbul-reports\": {\n          \"version\": \"1.4.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"handlebars\": \"^4.0.3\"\n          }\n        },\n        \"js-tokens\": {\n          \"version\": \"3.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"jsesc\": {\n          \"version\": \"1.3.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"kind-of\": {\n          \"version\": \"3.2.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-buffer\": \"^1.1.5\"\n          }\n        },\n        \"lazy-cache\": {\n          \"version\": \"1.0.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"lcid\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"invert-kv\": \"^1.0.0\"\n          }\n        },\n        \"load-json-file\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"graceful-fs\": \"^4.1.2\",\n            \"parse-json\": \"^2.2.0\",\n            \"pify\": \"^2.0.0\",\n            \"pinkie-promise\": \"^2.0.0\",\n            \"strip-bom\": \"^2.0.0\"\n          }\n        },\n        \"locate-path\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"p-locate\": \"^2.0.0\",\n            \"path-exists\": \"^3.0.0\"\n          },\n          \"dependencies\": {\n            \"path-exists\": {\n              \"version\": \"3.0.0\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"lodash\": {\n          \"version\": \"4.17.10\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"longest\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"loose-envify\": {\n          \"version\": \"1.3.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"js-tokens\": \"^3.0.0\"\n          }\n        },\n        \"lru-cache\": {\n          \"version\": \"4.1.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"pseudomap\": \"^1.0.2\",\n            \"yallist\": \"^2.1.2\"\n          }\n        },\n        \"map-cache\": {\n          \"version\": \"0.2.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"map-visit\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"object-visit\": \"^1.0.0\"\n          }\n        },\n        \"md5-hex\": {\n          \"version\": \"1.3.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"md5-o-matic\": \"^0.1.1\"\n          }\n        },\n        \"md5-o-matic\": {\n          \"version\": \"0.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"mem\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"mimic-fn\": \"^1.0.0\"\n          }\n        },\n        \"merge-source-map\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"source-map\": \"^0.6.1\"\n          },\n          \"dependencies\": {\n            \"source-map\": {\n              \"version\": \"0.6.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"micromatch\": {\n          \"version\": \"3.1.10\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"arr-diff\": \"^4.0.0\",\n            \"array-unique\": \"^0.3.2\",\n            \"braces\": \"^2.3.1\",\n            \"define-property\": \"^2.0.2\",\n            \"extend-shallow\": \"^3.0.2\",\n            \"extglob\": \"^2.0.4\",\n            \"fragment-cache\": \"^0.2.1\",\n            \"kind-of\": \"^6.0.2\",\n            \"nanomatch\": \"^1.2.9\",\n            \"object.pick\": \"^1.3.0\",\n            \"regex-not\": \"^1.0.0\",\n            \"snapdragon\": \"^0.8.1\",\n            \"to-regex\": \"^3.0.2\"\n          },\n          \"dependencies\": {\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"mimic-fn\": {\n          \"version\": \"1.2.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"minimatch\": {\n          \"version\": \"3.0.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"brace-expansion\": \"^1.1.7\"\n          }\n        },\n        \"minimist\": {\n          \"version\": \"0.0.8\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"mixin-deep\": {\n          \"version\": \"1.3.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"for-in\": \"^1.0.2\",\n            \"is-extendable\": \"^1.0.1\"\n          },\n          \"dependencies\": {\n            \"is-extendable\": {\n              \"version\": \"1.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-plain-object\": \"^2.0.4\"\n              }\n            }\n          }\n        },\n        \"mkdirp\": {\n          \"version\": \"0.5.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"minimist\": \"0.0.8\"\n          }\n        },\n        \"ms\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"nanomatch\": {\n          \"version\": \"1.2.9\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"arr-diff\": \"^4.0.0\",\n            \"array-unique\": \"^0.3.2\",\n            \"define-property\": \"^2.0.2\",\n            \"extend-shallow\": \"^3.0.2\",\n            \"fragment-cache\": \"^0.2.1\",\n            \"is-odd\": \"^2.0.0\",\n            \"is-windows\": \"^1.0.2\",\n            \"kind-of\": \"^6.0.2\",\n            \"object.pick\": \"^1.3.0\",\n            \"regex-not\": \"^1.0.0\",\n            \"snapdragon\": \"^0.8.1\",\n            \"to-regex\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"arr-diff\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"array-unique\": {\n              \"version\": \"0.3.2\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"normalize-package-data\": {\n          \"version\": \"2.4.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"hosted-git-info\": \"^2.1.4\",\n            \"is-builtin-module\": \"^1.0.0\",\n            \"semver\": \"2 || 3 || 4 || 5\",\n            \"validate-npm-package-license\": \"^3.0.1\"\n          }\n        },\n        \"npm-run-path\": {\n          \"version\": \"2.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"path-key\": \"^2.0.0\"\n          }\n        },\n        \"number-is-nan\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"object-assign\": {\n          \"version\": \"4.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"object-copy\": {\n          \"version\": \"0.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"copy-descriptor\": \"^0.1.0\",\n            \"define-property\": \"^0.2.5\",\n            \"kind-of\": \"^3.0.3\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"0.2.5\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"object-visit\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"isobject\": \"^3.0.0\"\n          },\n          \"dependencies\": {\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"object.pick\": {\n          \"version\": \"1.3.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"isobject\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"once\": {\n          \"version\": \"1.4.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"wrappy\": \"1\"\n          }\n        },\n        \"optimist\": {\n          \"version\": \"0.6.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"minimist\": \"~0.0.1\",\n            \"wordwrap\": \"~0.0.2\"\n          }\n        },\n        \"os-homedir\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"os-locale\": {\n          \"version\": \"2.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"execa\": \"^0.7.0\",\n            \"lcid\": \"^1.0.0\",\n            \"mem\": \"^1.1.0\"\n          }\n        },\n        \"p-finally\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"p-limit\": {\n          \"version\": \"1.2.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"p-try\": \"^1.0.0\"\n          }\n        },\n        \"p-locate\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"p-limit\": \"^1.1.0\"\n          }\n        },\n        \"p-try\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"parse-json\": {\n          \"version\": \"2.2.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"error-ex\": \"^1.2.0\"\n          }\n        },\n        \"pascalcase\": {\n          \"version\": \"0.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"path-exists\": {\n          \"version\": \"2.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"pinkie-promise\": \"^2.0.0\"\n          }\n        },\n        \"path-is-absolute\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"path-key\": {\n          \"version\": \"2.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"path-parse\": {\n          \"version\": \"1.0.5\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"path-type\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"graceful-fs\": \"^4.1.2\",\n            \"pify\": \"^2.0.0\",\n            \"pinkie-promise\": \"^2.0.0\"\n          }\n        },\n        \"pify\": {\n          \"version\": \"2.3.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"pinkie\": {\n          \"version\": \"2.0.4\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"pinkie-promise\": {\n          \"version\": \"2.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"pinkie\": \"^2.0.0\"\n          }\n        },\n        \"pkg-dir\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"find-up\": \"^1.0.0\"\n          },\n          \"dependencies\": {\n            \"find-up\": {\n              \"version\": \"1.1.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"path-exists\": \"^2.0.0\",\n                \"pinkie-promise\": \"^2.0.0\"\n              }\n            }\n          }\n        },\n        \"posix-character-classes\": {\n          \"version\": \"0.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"pseudomap\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"read-pkg\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"load-json-file\": \"^1.0.0\",\n            \"normalize-package-data\": \"^2.3.2\",\n            \"path-type\": \"^1.0.0\"\n          }\n        },\n        \"read-pkg-up\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"find-up\": \"^1.0.0\",\n            \"read-pkg\": \"^1.0.0\"\n          },\n          \"dependencies\": {\n            \"find-up\": {\n              \"version\": \"1.1.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"path-exists\": \"^2.0.0\",\n                \"pinkie-promise\": \"^2.0.0\"\n              }\n            }\n          }\n        },\n        \"regenerator-runtime\": {\n          \"version\": \"0.11.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"regex-not\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"extend-shallow\": \"^3.0.2\",\n            \"safe-regex\": \"^1.1.0\"\n          }\n        },\n        \"repeat-element\": {\n          \"version\": \"1.1.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"repeat-string\": {\n          \"version\": \"1.6.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"repeating\": {\n          \"version\": \"2.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-finite\": \"^1.0.0\"\n          }\n        },\n        \"require-directory\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"require-main-filename\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"resolve-from\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"resolve-url\": {\n          \"version\": \"0.2.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"ret\": {\n          \"version\": \"0.1.15\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"right-align\": {\n          \"version\": \"0.1.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"align-text\": \"^0.1.1\"\n          }\n        },\n        \"rimraf\": {\n          \"version\": \"2.6.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"glob\": \"^7.0.5\"\n          }\n        },\n        \"safe-regex\": {\n          \"version\": \"1.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"ret\": \"~0.1.10\"\n          }\n        },\n        \"semver\": {\n          \"version\": \"5.5.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"set-blocking\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"set-value\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"extend-shallow\": \"^2.0.1\",\n            \"is-extendable\": \"^0.1.1\",\n            \"is-plain-object\": \"^2.0.3\",\n            \"split-string\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"shebang-command\": {\n          \"version\": \"1.2.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"shebang-regex\": \"^1.0.0\"\n          }\n        },\n        \"shebang-regex\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"signal-exit\": {\n          \"version\": \"3.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"slide\": {\n          \"version\": \"1.1.6\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"snapdragon\": {\n          \"version\": \"0.8.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"base\": \"^0.11.1\",\n            \"debug\": \"^2.2.0\",\n            \"define-property\": \"^0.2.5\",\n            \"extend-shallow\": \"^2.0.1\",\n            \"map-cache\": \"^0.2.2\",\n            \"source-map\": \"^0.5.6\",\n            \"source-map-resolve\": \"^0.5.0\",\n            \"use\": \"^3.1.0\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"0.2.5\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^0.1.0\"\n              }\n            },\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"snapdragon-node\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"define-property\": \"^1.0.0\",\n            \"isobject\": \"^3.0.0\",\n            \"snapdragon-util\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^1.0.0\"\n              }\n            },\n            \"is-accessor-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-data-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-descriptor\": {\n              \"version\": \"1.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-accessor-descriptor\": \"^1.0.0\",\n                \"is-data-descriptor\": \"^1.0.0\",\n                \"kind-of\": \"^6.0.2\"\n              }\n            },\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"snapdragon-util\": {\n          \"version\": \"3.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^3.2.0\"\n          }\n        },\n        \"source-map\": {\n          \"version\": \"0.5.7\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"source-map-resolve\": {\n          \"version\": \"0.5.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"atob\": \"^2.0.0\",\n            \"decode-uri-component\": \"^0.2.0\",\n            \"resolve-url\": \"^0.2.1\",\n            \"source-map-url\": \"^0.4.0\",\n            \"urix\": \"^0.1.0\"\n          }\n        },\n        \"source-map-url\": {\n          \"version\": \"0.4.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"spawn-wrap\": {\n          \"version\": \"1.4.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"foreground-child\": \"^1.5.6\",\n            \"mkdirp\": \"^0.5.0\",\n            \"os-homedir\": \"^1.0.1\",\n            \"rimraf\": \"^2.6.2\",\n            \"signal-exit\": \"^3.0.2\",\n            \"which\": \"^1.3.0\"\n          }\n        },\n        \"spdx-correct\": {\n          \"version\": \"3.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"spdx-expression-parse\": \"^3.0.0\",\n            \"spdx-license-ids\": \"^3.0.0\"\n          }\n        },\n        \"spdx-exceptions\": {\n          \"version\": \"2.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"spdx-expression-parse\": {\n          \"version\": \"3.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"spdx-exceptions\": \"^2.1.0\",\n            \"spdx-license-ids\": \"^3.0.0\"\n          }\n        },\n        \"spdx-license-ids\": {\n          \"version\": \"3.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"split-string\": {\n          \"version\": \"3.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"extend-shallow\": \"^3.0.0\"\n          }\n        },\n        \"static-extend\": {\n          \"version\": \"0.1.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"define-property\": \"^0.2.5\",\n            \"object-copy\": \"^0.1.0\"\n          },\n          \"dependencies\": {\n            \"define-property\": {\n              \"version\": \"0.2.5\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-descriptor\": \"^0.1.0\"\n              }\n            }\n          }\n        },\n        \"string-width\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-fullwidth-code-point\": \"^2.0.0\",\n            \"strip-ansi\": \"^4.0.0\"\n          },\n          \"dependencies\": {\n            \"ansi-regex\": {\n              \"version\": \"3.0.0\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"strip-ansi\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"ansi-regex\": \"^3.0.0\"\n              }\n            }\n          }\n        },\n        \"strip-ansi\": {\n          \"version\": \"3.0.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"ansi-regex\": \"^2.0.0\"\n          }\n        },\n        \"strip-bom\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-utf8\": \"^0.2.0\"\n          }\n        },\n        \"strip-eof\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"supports-color\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"test-exclude\": {\n          \"version\": \"4.2.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"arrify\": \"^1.0.1\",\n            \"micromatch\": \"^3.1.8\",\n            \"object-assign\": \"^4.1.0\",\n            \"read-pkg-up\": \"^1.0.1\",\n            \"require-main-filename\": \"^1.0.1\"\n          },\n          \"dependencies\": {\n            \"arr-diff\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"array-unique\": {\n              \"version\": \"0.3.2\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"braces\": {\n              \"version\": \"2.3.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"arr-flatten\": \"^1.1.0\",\n                \"array-unique\": \"^0.3.2\",\n                \"extend-shallow\": \"^2.0.1\",\n                \"fill-range\": \"^4.0.0\",\n                \"isobject\": \"^3.0.1\",\n                \"repeat-element\": \"^1.1.2\",\n                \"snapdragon\": \"^0.8.1\",\n                \"snapdragon-node\": \"^2.0.1\",\n                \"split-string\": \"^3.0.2\",\n                \"to-regex\": \"^3.0.1\"\n              },\n              \"dependencies\": {\n                \"extend-shallow\": {\n                  \"version\": \"2.0.1\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-extendable\": \"^0.1.0\"\n                  }\n                }\n              }\n            },\n            \"expand-brackets\": {\n              \"version\": \"2.1.4\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"debug\": \"^2.3.3\",\n                \"define-property\": \"^0.2.5\",\n                \"extend-shallow\": \"^2.0.1\",\n                \"posix-character-classes\": \"^0.1.0\",\n                \"regex-not\": \"^1.0.0\",\n                \"snapdragon\": \"^0.8.1\",\n                \"to-regex\": \"^3.0.1\"\n              },\n              \"dependencies\": {\n                \"define-property\": {\n                  \"version\": \"0.2.5\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-descriptor\": \"^0.1.0\"\n                  }\n                },\n                \"extend-shallow\": {\n                  \"version\": \"2.0.1\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-extendable\": \"^0.1.0\"\n                  }\n                },\n                \"is-accessor-descriptor\": {\n                  \"version\": \"0.1.6\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"kind-of\": \"^3.0.2\"\n                  },\n                  \"dependencies\": {\n                    \"kind-of\": {\n                      \"version\": \"3.2.2\",\n                      \"bundled\": true,\n                      \"dev\": true,\n                      \"requires\": {\n                        \"is-buffer\": \"^1.1.5\"\n                      }\n                    }\n                  }\n                },\n                \"is-data-descriptor\": {\n                  \"version\": \"0.1.4\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"kind-of\": \"^3.0.2\"\n                  },\n                  \"dependencies\": {\n                    \"kind-of\": {\n                      \"version\": \"3.2.2\",\n                      \"bundled\": true,\n                      \"dev\": true,\n                      \"requires\": {\n                        \"is-buffer\": \"^1.1.5\"\n                      }\n                    }\n                  }\n                },\n                \"is-descriptor\": {\n                  \"version\": \"0.1.6\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-accessor-descriptor\": \"^0.1.6\",\n                    \"is-data-descriptor\": \"^0.1.4\",\n                    \"kind-of\": \"^5.0.0\"\n                  }\n                },\n                \"kind-of\": {\n                  \"version\": \"5.1.0\",\n                  \"bundled\": true,\n                  \"dev\": true\n                }\n              }\n            },\n            \"extglob\": {\n              \"version\": \"2.0.4\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"array-unique\": \"^0.3.2\",\n                \"define-property\": \"^1.0.0\",\n                \"expand-brackets\": \"^2.1.4\",\n                \"extend-shallow\": \"^2.0.1\",\n                \"fragment-cache\": \"^0.2.1\",\n                \"regex-not\": \"^1.0.0\",\n                \"snapdragon\": \"^0.8.1\",\n                \"to-regex\": \"^3.0.1\"\n              },\n              \"dependencies\": {\n                \"define-property\": {\n                  \"version\": \"1.0.0\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-descriptor\": \"^1.0.0\"\n                  }\n                },\n                \"extend-shallow\": {\n                  \"version\": \"2.0.1\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-extendable\": \"^0.1.0\"\n                  }\n                }\n              }\n            },\n            \"fill-range\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"extend-shallow\": \"^2.0.1\",\n                \"is-number\": \"^3.0.0\",\n                \"repeat-string\": \"^1.6.1\",\n                \"to-regex-range\": \"^2.1.0\"\n              },\n              \"dependencies\": {\n                \"extend-shallow\": {\n                  \"version\": \"2.0.1\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-extendable\": \"^0.1.0\"\n                  }\n                }\n              }\n            },\n            \"is-accessor-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-data-descriptor\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^6.0.0\"\n              }\n            },\n            \"is-descriptor\": {\n              \"version\": \"1.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-accessor-descriptor\": \"^1.0.0\",\n                \"is-data-descriptor\": \"^1.0.0\",\n                \"kind-of\": \"^6.0.2\"\n              }\n            },\n            \"is-number\": {\n              \"version\": \"3.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^3.0.2\"\n              },\n              \"dependencies\": {\n                \"kind-of\": {\n                  \"version\": \"3.2.2\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"is-buffer\": \"^1.1.5\"\n                  }\n                }\n              }\n            },\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"micromatch\": {\n              \"version\": \"3.1.10\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"arr-diff\": \"^4.0.0\",\n                \"array-unique\": \"^0.3.2\",\n                \"braces\": \"^2.3.1\",\n                \"define-property\": \"^2.0.2\",\n                \"extend-shallow\": \"^3.0.2\",\n                \"extglob\": \"^2.0.4\",\n                \"fragment-cache\": \"^0.2.1\",\n                \"kind-of\": \"^6.0.2\",\n                \"nanomatch\": \"^1.2.9\",\n                \"object.pick\": \"^1.3.0\",\n                \"regex-not\": \"^1.0.0\",\n                \"snapdragon\": \"^0.8.1\",\n                \"to-regex\": \"^3.0.2\"\n              }\n            }\n          }\n        },\n        \"to-fast-properties\": {\n          \"version\": \"1.0.3\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"to-object-path\": {\n          \"version\": \"0.3.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^3.0.2\"\n          }\n        },\n        \"to-regex\": {\n          \"version\": \"3.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"define-property\": \"^2.0.2\",\n            \"extend-shallow\": \"^3.0.2\",\n            \"regex-not\": \"^1.0.2\",\n            \"safe-regex\": \"^1.1.0\"\n          }\n        },\n        \"to-regex-range\": {\n          \"version\": \"2.1.1\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"is-number\": \"^3.0.0\",\n            \"repeat-string\": \"^1.6.1\"\n          },\n          \"dependencies\": {\n            \"is-number\": {\n              \"version\": \"3.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"kind-of\": \"^3.0.2\"\n              }\n            }\n          }\n        },\n        \"trim-right\": {\n          \"version\": \"1.0.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"uglify-js\": {\n          \"version\": \"2.8.29\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true,\n          \"requires\": {\n            \"source-map\": \"~0.5.1\",\n            \"uglify-to-browserify\": \"~1.0.0\",\n            \"yargs\": \"~3.10.0\"\n          },\n          \"dependencies\": {\n            \"yargs\": {\n              \"version\": \"3.10.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"optional\": true,\n              \"requires\": {\n                \"camelcase\": \"^1.0.2\",\n                \"cliui\": \"^2.1.0\",\n                \"decamelize\": \"^1.0.0\",\n                \"window-size\": \"0.1.0\"\n              }\n            }\n          }\n        },\n        \"uglify-to-browserify\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"union-value\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"arr-union\": \"^3.1.0\",\n            \"get-value\": \"^2.0.6\",\n            \"is-extendable\": \"^0.1.1\",\n            \"set-value\": \"^0.4.3\"\n          },\n          \"dependencies\": {\n            \"extend-shallow\": {\n              \"version\": \"2.0.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"is-extendable\": \"^0.1.0\"\n              }\n            },\n            \"set-value\": {\n              \"version\": \"0.4.3\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"extend-shallow\": \"^2.0.1\",\n                \"is-extendable\": \"^0.1.1\",\n                \"is-plain-object\": \"^2.0.1\",\n                \"to-object-path\": \"^0.3.0\"\n              }\n            }\n          }\n        },\n        \"unset-value\": {\n          \"version\": \"1.0.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"has-value\": \"^0.3.1\",\n            \"isobject\": \"^3.0.0\"\n          },\n          \"dependencies\": {\n            \"has-value\": {\n              \"version\": \"0.3.1\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"get-value\": \"^2.0.3\",\n                \"has-values\": \"^0.1.4\",\n                \"isobject\": \"^2.0.0\"\n              },\n              \"dependencies\": {\n                \"isobject\": {\n                  \"version\": \"2.1.0\",\n                  \"bundled\": true,\n                  \"dev\": true,\n                  \"requires\": {\n                    \"isarray\": \"1.0.0\"\n                  }\n                }\n              }\n            },\n            \"has-values\": {\n              \"version\": \"0.1.4\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"isobject\": {\n              \"version\": \"3.0.1\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"urix\": {\n          \"version\": \"0.1.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"use\": {\n          \"version\": \"3.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"kind-of\": \"^6.0.2\"\n          },\n          \"dependencies\": {\n            \"kind-of\": {\n              \"version\": \"6.0.2\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        },\n        \"validate-npm-package-license\": {\n          \"version\": \"3.0.3\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"spdx-correct\": \"^3.0.0\",\n            \"spdx-expression-parse\": \"^3.0.0\"\n          }\n        },\n        \"which\": {\n          \"version\": \"1.3.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"isexe\": \"^2.0.0\"\n          }\n        },\n        \"which-module\": {\n          \"version\": \"2.0.0\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"window-size\": {\n          \"version\": \"0.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"optional\": true\n        },\n        \"wordwrap\": {\n          \"version\": \"0.0.3\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"wrap-ansi\": {\n          \"version\": \"2.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"string-width\": \"^1.0.1\",\n            \"strip-ansi\": \"^3.0.1\"\n          },\n          \"dependencies\": {\n            \"is-fullwidth-code-point\": {\n              \"version\": \"1.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"number-is-nan\": \"^1.0.0\"\n              }\n            },\n            \"string-width\": {\n              \"version\": \"1.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"code-point-at\": \"^1.0.0\",\n                \"is-fullwidth-code-point\": \"^1.0.0\",\n                \"strip-ansi\": \"^3.0.0\"\n              }\n            }\n          }\n        },\n        \"wrappy\": {\n          \"version\": \"1.0.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"write-file-atomic\": {\n          \"version\": \"1.3.4\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"graceful-fs\": \"^4.1.11\",\n            \"imurmurhash\": \"^0.1.4\",\n            \"slide\": \"^1.1.5\"\n          }\n        },\n        \"y18n\": {\n          \"version\": \"3.2.1\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"yallist\": {\n          \"version\": \"2.1.2\",\n          \"bundled\": true,\n          \"dev\": true\n        },\n        \"yargs\": {\n          \"version\": \"11.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"cliui\": \"^4.0.0\",\n            \"decamelize\": \"^1.1.1\",\n            \"find-up\": \"^2.1.0\",\n            \"get-caller-file\": \"^1.0.1\",\n            \"os-locale\": \"^2.0.0\",\n            \"require-directory\": \"^2.1.1\",\n            \"require-main-filename\": \"^1.0.1\",\n            \"set-blocking\": \"^2.0.0\",\n            \"string-width\": \"^2.0.0\",\n            \"which-module\": \"^2.0.0\",\n            \"y18n\": \"^3.2.1\",\n            \"yargs-parser\": \"^9.0.2\"\n          },\n          \"dependencies\": {\n            \"ansi-regex\": {\n              \"version\": \"3.0.0\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"camelcase\": {\n              \"version\": \"4.1.0\",\n              \"bundled\": true,\n              \"dev\": true\n            },\n            \"cliui\": {\n              \"version\": \"4.1.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"string-width\": \"^2.1.1\",\n                \"strip-ansi\": \"^4.0.0\",\n                \"wrap-ansi\": \"^2.0.0\"\n              }\n            },\n            \"strip-ansi\": {\n              \"version\": \"4.0.0\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"ansi-regex\": \"^3.0.0\"\n              }\n            },\n            \"yargs-parser\": {\n              \"version\": \"9.0.2\",\n              \"bundled\": true,\n              \"dev\": true,\n              \"requires\": {\n                \"camelcase\": \"^4.1.0\"\n              }\n            }\n          }\n        },\n        \"yargs-parser\": {\n          \"version\": \"8.1.0\",\n          \"bundled\": true,\n          \"dev\": true,\n          \"requires\": {\n            \"camelcase\": \"^4.1.0\"\n          },\n          \"dependencies\": {\n            \"camelcase\": {\n              \"version\": \"4.1.0\",\n              \"bundled\": true,\n              \"dev\": true\n            }\n          }\n        }\n      }\n    },\n    \"object-assign\": {\n      \"version\": \"4.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz\",\n      \"integrity\": \"sha1-ejs9DpgGPUP0wD8uiubNUahog6A=\",\n      \"dev\": true\n    },\n    \"object.omit\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz\",\n      \"integrity\": \"sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"for-own\": \"^0.1.4\",\n        \"is-extendable\": \"^0.1.1\"\n      }\n    },\n    \"once\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/once/-/once-1.4.0.tgz\",\n      \"integrity\": \"sha1-WDsap3WWHUsROsF9nFC6753Xa9E=\",\n      \"dev\": true,\n      \"requires\": {\n        \"wrappy\": \"1\"\n      }\n    },\n    \"os-locale\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz\",\n      \"integrity\": \"sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=\",\n      \"dev\": true,\n      \"requires\": {\n        \"lcid\": \"^1.0.0\"\n      }\n    },\n    \"parse-glob\": {\n      \"version\": \"3.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz\",\n      \"integrity\": \"sha1-ssN2z7EfNVE7rdFz7wu246OIORw=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"glob-base\": \"^0.3.0\",\n        \"is-dotfile\": \"^1.0.0\",\n        \"is-extglob\": \"^1.0.0\",\n        \"is-glob\": \"^2.0.0\"\n      }\n    },\n    \"parse-json\": {\n      \"version\": \"2.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz\",\n      \"integrity\": \"sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=\",\n      \"dev\": true,\n      \"requires\": {\n        \"error-ex\": \"^1.2.0\"\n      }\n    },\n    \"path-exists\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz\",\n      \"integrity\": \"sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=\",\n      \"dev\": true,\n      \"requires\": {\n        \"pinkie-promise\": \"^2.0.0\"\n      }\n    },\n    \"path-is-absolute\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz\",\n      \"integrity\": \"sha1-F0uSaHNVNP+8es5r9TpanhtcX18=\",\n      \"dev\": true\n    },\n    \"path-type\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz\",\n      \"integrity\": \"sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=\",\n      \"dev\": true,\n      \"requires\": {\n        \"graceful-fs\": \"^4.1.2\",\n        \"pify\": \"^2.0.0\",\n        \"pinkie-promise\": \"^2.0.0\"\n      }\n    },\n    \"pify\": {\n      \"version\": \"2.3.0\",\n      \"resolved\": \"http://registry.npmjs.org/pify/-/pify-2.3.0.tgz\",\n      \"integrity\": \"sha1-7RQaasBDqEnqWISY59yosVMw6Qw=\",\n      \"dev\": true\n    },\n    \"pinkie\": {\n      \"version\": \"2.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz\",\n      \"integrity\": \"sha1-clVrgM+g1IqXToDnckjoDtT3+HA=\",\n      \"dev\": true\n    },\n    \"pinkie-promise\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz\",\n      \"integrity\": \"sha1-ITXW36ejWMBprJsXh3YogihFD/o=\",\n      \"dev\": true,\n      \"requires\": {\n        \"pinkie\": \"^2.0.0\"\n      }\n    },\n    \"postcss\": {\n      \"version\": \"5.2.6\",\n      \"resolved\": \"http://registry.npmjs.org/postcss/-/postcss-5.2.6.tgz\",\n      \"integrity\": \"sha1-olLNZ81SWFA18X6a0Ss1E3p73Z4=\",\n      \"dev\": true,\n      \"requires\": {\n        \"chalk\": \"^1.1.3\",\n        \"js-base64\": \"^2.1.9\",\n        \"source-map\": \"^0.5.6\",\n        \"supports-color\": \"^3.1.2\"\n      }\n    },\n    \"postcss-cli\": {\n      \"version\": \"2.6.0\",\n      \"resolved\": \"http://registry.npmjs.org/postcss-cli/-/postcss-cli-2.6.0.tgz\",\n      \"integrity\": \"sha1-8N45PKoCb8/BsUeYIpia9QjtUV0=\",\n      \"dev\": true,\n      \"requires\": {\n        \"chokidar\": \"^1.5.1\",\n        \"globby\": \"^4.1.0\",\n        \"mkdirp\": \"^0.5.1\",\n        \"neo-async\": \"^1.0.0\",\n        \"postcss\": \"^5.0.0\",\n        \"read-file-stdin\": \"^0.2.0\",\n        \"resolve\": \"^1.1.6\",\n        \"yargs\": \"^4.7.1\"\n      }\n    },\n    \"postcss-value-parser\": {\n      \"version\": \"3.3.0\",\n      \"resolved\": \"http://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz\",\n      \"integrity\": \"sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=\",\n      \"dev\": true\n    },\n    \"preserve\": {\n      \"version\": \"0.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz\",\n      \"integrity\": \"sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"process-nextick-args\": {\n      \"version\": \"1.0.7\",\n      \"resolved\": \"https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz\",\n      \"integrity\": \"sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"randomatic\": {\n      \"version\": \"1.1.6\",\n      \"resolved\": \"http://registry.npmjs.org/randomatic/-/randomatic-1.1.6.tgz\",\n      \"integrity\": \"sha1-EQ3Kv/OX6dz/fAeJzMCkmt8exbs=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"is-number\": \"^2.0.2\",\n        \"kind-of\": \"^3.0.2\"\n      }\n    },\n    \"read-file-stdin\": {\n      \"version\": \"0.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/read-file-stdin/-/read-file-stdin-0.2.1.tgz\",\n      \"integrity\": \"sha1-JezP86FTtoCa+ssj7hU4fbng7mE=\",\n      \"dev\": true,\n      \"requires\": {\n        \"gather-stream\": \"^1.0.0\"\n      }\n    },\n    \"read-pkg\": {\n      \"version\": \"1.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz\",\n      \"integrity\": \"sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=\",\n      \"dev\": true,\n      \"requires\": {\n        \"load-json-file\": \"^1.0.0\",\n        \"normalize-package-data\": \"^2.3.2\",\n        \"path-type\": \"^1.0.0\"\n      }\n    },\n    \"read-pkg-up\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz\",\n      \"integrity\": \"sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=\",\n      \"dev\": true,\n      \"requires\": {\n        \"find-up\": \"^1.0.0\",\n        \"read-pkg\": \"^1.0.0\"\n      }\n    },\n    \"readable-stream\": {\n      \"version\": \"2.2.2\",\n      \"resolved\": \"http://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz\",\n      \"integrity\": \"sha1-qeb+w8fdqF+LsbO6cChgRVb8gl4=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"buffer-shims\": \"^1.0.0\",\n        \"core-util-is\": \"~1.0.0\",\n        \"inherits\": \"~2.0.1\",\n        \"isarray\": \"~1.0.0\",\n        \"process-nextick-args\": \"~1.0.6\",\n        \"string_decoder\": \"~0.10.x\",\n        \"util-deprecate\": \"~1.0.1\"\n      }\n    },\n    \"readdirp\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"http://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz\",\n      \"integrity\": \"sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"graceful-fs\": \"^4.1.2\",\n        \"minimatch\": \"^3.0.2\",\n        \"readable-stream\": \"^2.0.2\",\n        \"set-immediate-shim\": \"^1.0.1\"\n      }\n    },\n    \"regex-cache\": {\n      \"version\": \"0.4.3\",\n      \"resolved\": \"https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz\",\n      \"integrity\": \"sha1-mxpsNdTQ3871cRrmUejp09cRQUU=\",\n      \"dev\": true,\n      \"optional\": true,\n      \"requires\": {\n        \"is-equal-shallow\": \"^0.1.3\",\n        \"is-primitive\": \"^2.0.0\"\n      }\n    },\n    \"repeat-element\": {\n      \"version\": \"1.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz\",\n      \"integrity\": \"sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=\",\n      \"dev\": true\n    },\n    \"repeat-string\": {\n      \"version\": \"1.6.1\",\n      \"resolved\": \"https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz\",\n      \"integrity\": \"sha1-jcrkcOHIirwtYA//Sndihtp15jc=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"require-directory\": {\n      \"version\": \"2.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz\",\n      \"integrity\": \"sha1-jGStX9MNqxyXbiNE/+f3kqam30I=\",\n      \"dev\": true\n    },\n    \"require-main-filename\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz\",\n      \"integrity\": \"sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=\",\n      \"dev\": true\n    },\n    \"resolve\": {\n      \"version\": \"1.1.7\",\n      \"resolved\": \"https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz\",\n      \"integrity\": \"sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=\",\n      \"dev\": true\n    },\n    \"semver\": {\n      \"version\": \"5.3.0\",\n      \"resolved\": \"http://registry.npmjs.org/semver/-/semver-5.3.0.tgz\",\n      \"integrity\": \"sha1-myzl094C0XxgEq0yaqa00M9U+U8=\",\n      \"dev\": true\n    },\n    \"set-blocking\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz\",\n      \"integrity\": \"sha1-BF+XgtARrppoA93TgrJDkrPYkPc=\",\n      \"dev\": true\n    },\n    \"set-immediate-shim\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz\",\n      \"integrity\": \"sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"source-map\": {\n      \"version\": \"0.5.6\",\n      \"resolved\": \"http://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz\",\n      \"integrity\": \"sha1-dc449SvwczxafwwRjYEzSiu19BI=\",\n      \"dev\": true\n    },\n    \"source-map-support\": {\n      \"version\": \"0.5.9\",\n      \"resolved\": \"https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz\",\n      \"integrity\": \"sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==\",\n      \"dev\": true,\n      \"requires\": {\n        \"buffer-from\": \"^1.0.0\",\n        \"source-map\": \"^0.6.0\"\n      },\n      \"dependencies\": {\n        \"source-map\": {\n          \"version\": \"0.6.1\",\n          \"resolved\": \"https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz\",\n          \"integrity\": \"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==\",\n          \"dev\": true\n        }\n      }\n    },\n    \"spdx-correct\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"http://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz\",\n      \"integrity\": \"sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=\",\n      \"dev\": true,\n      \"requires\": {\n        \"spdx-license-ids\": \"^1.0.2\"\n      }\n    },\n    \"spdx-expression-parse\": {\n      \"version\": \"1.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz\",\n      \"integrity\": \"sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=\",\n      \"dev\": true\n    },\n    \"spdx-license-ids\": {\n      \"version\": \"1.2.2\",\n      \"resolved\": \"http://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz\",\n      \"integrity\": \"sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=\",\n      \"dev\": true\n    },\n    \"string-width\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz\",\n      \"integrity\": \"sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=\",\n      \"dev\": true,\n      \"requires\": {\n        \"code-point-at\": \"^1.0.0\",\n        \"is-fullwidth-code-point\": \"^1.0.0\",\n        \"strip-ansi\": \"^3.0.0\"\n      }\n    },\n    \"string_decoder\": {\n      \"version\": \"0.10.31\",\n      \"resolved\": \"https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz\",\n      \"integrity\": \"sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"strip-ansi\": {\n      \"version\": \"3.0.1\",\n      \"resolved\": \"http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz\",\n      \"integrity\": \"sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=\",\n      \"dev\": true,\n      \"requires\": {\n        \"ansi-regex\": \"^2.0.0\"\n      }\n    },\n    \"strip-bom\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz\",\n      \"integrity\": \"sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=\",\n      \"dev\": true,\n      \"requires\": {\n        \"is-utf8\": \"^0.2.0\"\n      }\n    },\n    \"supports-color\": {\n      \"version\": \"3.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz\",\n      \"integrity\": \"sha1-cqJiiU2dQIuVbKBf83su2KbiotU=\",\n      \"dev\": true,\n      \"requires\": {\n        \"has-flag\": \"^1.0.0\"\n      }\n    },\n    \"util-deprecate\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz\",\n      \"integrity\": \"sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=\",\n      \"dev\": true,\n      \"optional\": true\n    },\n    \"validate-npm-package-license\": {\n      \"version\": \"3.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz\",\n      \"integrity\": \"sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=\",\n      \"dev\": true,\n      \"requires\": {\n        \"spdx-correct\": \"~1.0.0\",\n        \"spdx-expression-parse\": \"~1.0.0\"\n      }\n    },\n    \"which-module\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz\",\n      \"integrity\": \"sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=\",\n      \"dev\": true\n    },\n    \"window-size\": {\n      \"version\": \"0.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz\",\n      \"integrity\": \"sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=\",\n      \"dev\": true\n    },\n    \"wrap-ansi\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz\",\n      \"integrity\": \"sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=\",\n      \"dev\": true,\n      \"requires\": {\n        \"string-width\": \"^1.0.1\",\n        \"strip-ansi\": \"^3.0.1\"\n      }\n    },\n    \"wrappy\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz\",\n      \"integrity\": \"sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=\",\n      \"dev\": true\n    },\n    \"y18n\": {\n      \"version\": \"3.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz\",\n      \"integrity\": \"sha1-bRX7qITAhnnA136I53WegR4H+kE=\",\n      \"dev\": true\n    },\n    \"yargs\": {\n      \"version\": \"4.8.1\",\n      \"resolved\": \"http://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz\",\n      \"integrity\": \"sha1-wMQpJMpKqmsObaFznfshZDn53cA=\",\n      \"dev\": true,\n      \"requires\": {\n        \"cliui\": \"^3.2.0\",\n        \"decamelize\": \"^1.1.1\",\n        \"get-caller-file\": \"^1.0.1\",\n        \"lodash.assign\": \"^4.0.3\",\n        \"os-locale\": \"^1.4.0\",\n        \"read-pkg-up\": \"^1.0.1\",\n        \"require-directory\": \"^2.1.1\",\n        \"require-main-filename\": \"^1.0.1\",\n        \"set-blocking\": \"^2.0.0\",\n        \"string-width\": \"^1.0.1\",\n        \"which-module\": \"^1.0.0\",\n        \"window-size\": \"^0.2.0\",\n        \"y18n\": \"^3.2.1\",\n        \"yargs-parser\": \"^2.4.1\"\n      }\n    },\n    \"yargs-parser\": {\n      \"version\": \"2.4.1\",\n      \"resolved\": \"http://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz\",\n      \"integrity\": \"sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=\",\n      \"dev\": true,\n      \"requires\": {\n        \"camelcase\": \"^3.0.0\",\n        \"lodash.assign\": \"^4.0.6\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"zetawar\",\n  \"description\": \"A turn based strategy game and AI platform\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/Zetawar/zetawar.git\"\n  },\n  \"license\": \"MIT\",\n  \"version\": \"0.1.0\",\n  \"devDependencies\": {\n    \"autoprefixer\": \"~6\",\n    \"nyc\": \"^11.4.1\",\n    \"postcss-cli\": \"~2\",\n    \"source-map-support\": \"^0.5.9\"\n  },\n  \"dependencies\": {\n    \"highlight.js\": \"^9.13.1\",\n    \"react\": \"^16.7.0\",\n    \"react-dom\": \"^16.7.0\",\n    \"react-grid-layout\": \"^0.16.6\",\n    \"react-icons\": \"^2.2.7\",\n    \"reakit\": \"^0.11.2\"\n  }\n}\n"
  },
  {
    "path": "shadow-cljs.edn",
    "content": ";; shadow-cljs configuration\n{:dependencies [[nubank/workspaces \"1.0.3\"]]\n :source-paths [\"src/clj\" \"src/cljc\" \"src/cljs\"]\n :builds {:workspaces {:target     nubank.workspaces.shadow-cljs.target\n                       :ns-regexp  \"-(test|cards)$\"\n                       :output-dir \"assets/js/workspaces\"\n                       :asset-path \"/js/workspaces\"\n                       :devtools   {:http-root          \"assets\"\n                                    :http-port          8080\n                                    :http-resource-root \".\"\n                                    :preloads           []}}}}\n"
  },
  {
    "path": "site/pages/backers.markdown",
    "content": "---\nname: Backers\n---\n\nListed below are the [Zetawar Kickstarter](https://www.kickstarter.com/projects/311016908/zetawar/)\nbackers who chose to have their names listed and backed the project at or above the\n\"Virtual Fame\" level.\n\n## Sponsors\n\n<br>\n\n[![Awesome Forms](https://awesomeforms.com/assets/logotype-0e39fee9805478f7769f0e25cb16b6e2.png)](https://awesomeforms.com)\n\n<br>\n\n[![Bendyworks](../images/sponsors/bendyworks-logo.png)](http://www.bendyworks.com/)\n\n<br>\n\n[Rowdy Labs](http://www.rowdylabs.com)\n\n## Backers\n\n- -Toni-\n- Alex Eberts\n- Austin Haas\n- Ben and Anna Donovan\n- Benno Kittelmann\n- Bill Kratzer\n- Boris Kourtoukov\n- Brandon Mathis\n- Brent Hagany\n- Cameron Callahan\n- Carl T Kleihege\n- Chris Wasser\n- Coop\n- Dan Boykis\n- Dmitri Lobach\n- Dmitri Sotnikov\n- Herb Bruss\n- Igor \"McBell\" Makaruks\n- JD Bell\n- James Reeves\n- Jarrod Doherty\n- Jeroen Lamain\n- Joel Martin\n- Jon M Dugan\n- Jonas Winje\n- Jonas lowery\n- Jonathan Boston\n- Joshua Miller\n- Juraj Martinka\n- Kephren\n- Kephren Newton\n- Kevin D. Saunders\n- Kevin Hutson (@mrjabba)\n- Koji Yusa\n- Lambda Island\n- Lloyd Goss\n- Loosetoes\n- Luke Ehresman\n- Markku Rontu\n- Martin Clausen\n- Mewmoomoo\n- Michael Dowden\n- Nate Jones\n- Nathan Ehresman\n- Nathan Powell\n- Nikita Prokopov\n- Pasi Heikkinen\n- Phillip DiPuccio\n- Rob Balder\n- RubyGeek LLC\n- Stonecrusher\n- The Griesbach Family\n- Tianxiang Xiong\n- Toby Nance\n- Ty Goss\n- Walton Hoops\n- Wasserlasser\n- Zach Erbaugh\n- gtoast\n- jclaggett\n- kika\n- peonicles\n- scientific-coder\n"
  },
  {
    "path": "site/pages/creating-maps-and-scenarios.markdown",
    "content": "---\nname: Creating Maps and Scenarios\n---\n\nThis guide describes how to create maps and scenarios for Zetawar.\n\nFirst some explanation of terminology is needed. Games take place on maps, but\nmaps by themselves don't include units or bases. Scenarios describe the initial\nstate of units and bases on a map. This alows a single map to support multiple\nscenarios.\n\nThe map and scenario data are located in the Clojure maps in the 'maps' and\n'scenarios' vars in [src/cljs/zetawar/data.cljs](https://github.com/Zetawar/zetawar/blob/481bfa3e789683b8216c0495babcd2e32aa8e86a/src/cljs/zetawar/data.cljs).\nTo add your own maps and scenarios simply add new entries to those maps. For\nhelp understanding the data format, check out the map and scenario format [dev\ncards](/devcards/#!/zetawar.devcards.data_formats).\n\nWhile creating maps and and scenarios it's useful to be able to see what they\nlook like. To get a live preview while editing, add devcards for your scenarios\nto\n[src/cljs/zetawar/devcards/scenarios.cljs](https://github.com/Zetawar/zetawar/blob/481bfa3e789683b8216c0495babcd2e32aa8e86a/src/cljs/zetawar/devcards/scenarios.cljs). Then run \"boot dev\"\n(after [installing boot](https://github.com/boot-clj/boot#install) if necessary) and browse to <http://localhost:3000/devcards#!/zetawar.devcards.scenarios>.\n"
  },
  {
    "path": "site/pages/devcards.markdown",
    "content": ""
  },
  {
    "path": "site/pages/docs.markdown",
    "content": "---\nname: Documentation\n---\n\n## Customizing Zetawar\n\n- [Writing Bots](../writing-bots/)\n- [Creating Maps and Scenarios](../creating-maps-and-scenarios/)\n"
  },
  {
    "path": "site/pages/roadmap.markdown",
    "content": "---\nname: Roadmap\n---\n\nThe roadmap is now tracked in [GitHub issues](https://github.com/zetawar/zetawar/issues).\n"
  },
  {
    "path": "site/pages/writing-bots.markdown",
    "content": "---\nname: Writing Bots\n---\n\nZetawar was created in part to provide a fun, easy way to experiment with AI in\nClojure(Script). To that end, it provides a pluggable AI interface for writing\nbots that can play against humans and other bots.\n\nTo understand the bot interface, it's helpful to understand its design goals:\n\n1. Make it easy for anyone regardless of programming experience to create a\nworking bot.\n\n2. Given a working bot, it should be easy to improve it to make it more capable\nand intelligence.\n\n3. Supporting advanced AI algorithms is secondary to the first two goals.\n\nWith these goals in mind, let's see what it takes to make an AI bot. To do so,\nwe need to write 3 functions.\n\nThe first function, 'score-actor', takes 4 arguments, 'db', 'game', 'actor-ctx',\nand 'actor'. 'db' is a DataScript DB containing the current game state. 'game'\nis a DataScript entity representing the current game. 'actor-ctx' we'll ignore\nfor now, and 'actor' is a DataScript entity representing either a base or unit.\nSo, what does this function do? Given these arguments, it returns a number\nrepresenting the score for the given base or unit. The score describes how\ndesirable it is to perform an as yet undetermined action with the given base or\nunit. Higher numbers indicate higher desirability, lower numbers, lower\ndesirability. The AI interface uses these scores to determine which unit or base\nwill perform the next action.\n\nThe other two functions, 'base-score' and 'unit-score' are similar and handle\npicking an action to take with the selected base or unit. Each takes 5\narguments, 'db', 'game', 'base'/'unit', 'action-ctx', and 'action'. The 'db',\nand 'game' arguments are the same as those passed to 'actor-score'. 'action-ctx'\nis similar to 'actor-ctx'. We'll ignore it too for the moment. The 'base'/'unit'\nargument represents the base (passed to 'base-score') or unit (passed to\n'unit-score') DataScript entity chosen by the AI interface based on the output\nof 'actor-score'. 'action' is a map representing an action that can be taken by\nthe unit or base. Given those arguments the function returns a score\nrepresenting the desirability of the action passed to it. Again, higher numbers\nrepresent higher desirability and lower numbers represent lower desirability.\nSimilar to with 'chose-score', the AI interface uses these scores to pick an\naction for the given unit or base.\n\nThat's it. Those 3 functions are all you need to make a working bot. The\nsimplest possible AI you could create would return random numbers or constants.\nIt would choose random bases and units and execute random actions. Of course, it\nwould be very stupid, but it would work and provide a starting place for\nbuilding something more intelligent.\n\nSo, what's the deal with those '\\*-ctx' arguments? Well, I kind of lied earlier\nwhen I said there are only 3 functions to implement. There are actually 6, but\nyou only need to implement 3 to get something working. The other 3 functions\nhandle creating the '\\*-ctx' arguments.\n\nThe first function, 'mk-actor-ctx' takes 2 arguments, 'db' and 'game' and\nreturns a context object. The context can contain any data you think might be\nuseful in your score functions. The primary purpose of this function is\noptimization. Obviously since it takes subset of 'score-actor's arguments, the\nsame logic could be executed in that function. However, the actor score function\nis called once per actor when making a decision about which actor to chose,\nwhere as the context function is called only once per actor selection decision.\nSo, if you have costly calculations that would be the same for all actors, put\nthem in 'mk-actor-ctx'.\n\nThe second and third functions, 'mk-unit-action-ctx' and 'mk-unit-action-ctx'\neach take 4 arguments 'db', 'game', 'base'/'unit', and 'actor-ctx' and return a\ncontext object. They're analogous to the actor context function, but for\nactions. They're called once after the actor has been chosen, but before the\nactions are enumerated. Any calculation that would be different per actor, but\nthe same for all actions for that actor can be calculated in them. Anything that\nyou want to retain from the actor context should be merged into the returned\ncontext.\n\nNow that you have a basic understanding of the AI interface, you can start\nimplementing your own AI. The easiest way to do this is by customizing the\nplaceholder 'Custom AI' included with Zetawar. You can find it\nin\n[src/cljs/zetawar/players/ai/custom.cljs](https://github.com/Zetawar/zetawar/blob/481bfa3e789683b8216c0495babcd2e32aa8e86a/src/cljs/zetawar/players/ai/custom.cljs).\nTo get started playing with it, clone\nthe\n[code](https://github.com/Zetawar/zetawar),\n[install boot](https://github.com/boot-clj/boot#install), and run 'boot dev'.\nYou can now connect to '<http://localhost:3000>' to interact with a version of\nZetawar running using your local code base. Any changes to the code will be\nimmediately (within a few seconds) reflected in the browser. If you want to test\nout your AI in the user interface, click on the \"Configure faction\" icon next to\nthe faction you want your AI to play as. Then select \"Custom AI\" in the \"Player\ntype\" drop down. Congratulations, you're now playing against your own custom AI!\n"
  },
  {
    "path": "site/perun.base.dev-builds.edn",
    "content": "{\n :sentry-url \"https://c2df777d930c4862bf77c03db7167263@sentry.io/125725\"\n :sentry-environment \"development\"\n :site-title \"Zetawar Development Builds\"\n }\n"
  },
  {
    "path": "site/perun.base.dev.edn",
    "content": "{\n :site-title \"Zetawar Development\"\n }\n"
  },
  {
    "path": "site/perun.base.prod.edn",
    "content": "{\n :google-analytics-tracking-id \"UA-25415440-6\"\n :sentry-url \"https://c2df777d930c4862bf77c03db7167263@sentry.io/125725\"\n :sentry-environment \"production\"\n :site-title \"Zetawar\"\n }\n"
  },
  {
    "path": "site/perun.base.staging.edn",
    "content": "{\n :sentry-url \"https://c2df777d930c4862bf77c03db7167263@sentry.io/125725\"\n :sentry-environment \"staging\"\n :site-title \"Zetawar Staging\"\n }\n"
  },
  {
    "path": "site/perun.base.test.edn",
    "content": "{\n :site-title \"Zetawar Tests\"\n }\n"
  },
  {
    "path": "site/posts/2016-05-17-technology-choices.markdown",
    "content": "---\ndescription: Technology Choices\ndate-published: 2016-05-17\n---\n\nHello world! Welcome to the Zetawar blog!\n\nA few people have expressed interest in learning a bit more about the\ntechnology behind Zetawar, so here goes. In this first blog post I will\ndescribe the key libraries I'm using and my rationale for choosing them.\n\n### ClojureScript\n\nFirst off, it's important to note Zetawar is implemented entirely in\n[ClojureScript](https://github.com/clojure/clojurescript). However, explaining\nthat decision is more than I want to attempt in this post. Just take it as a\ngiven for now, and I'll try to dig into that more in the future.\n\n### Datascript\n\nThe Zetawar game and user interface state are stored in\n[Datascript](https://github.com/tonsky/datascript). Datascript is an in-memory\ndatabase that supports ClojureScript and Clojure and implements a large portion\nof the Datomic API. [Datomic](http://www.datomic.com/) in turn is a durable\nrelational database that supports ACID transactions, declarative queries, and\nhas a first class notion of time. Due to it being in-memory only, Datascript\ndoes not support durability. It also does not retain a history of all changes\nto the database like Datomic does, but it implements enough Datomic features to\nbe quite useful in its own right.\n\nI decided to use Datascript primarily for two reasons. First, Datascript\nprovides extremely flexible query functionality. It supports\n[Datalog](http://docs.datomic.com/query.html), which provides SQL like queries,\nDatomic's [pull API](http://docs.datomic.com/pull.html), which provides\nhierarchical data selection, and Datomic's [entity\nAPI](http://docs.datomic.com/entities.html), which provides a lazy map like\ninterface to database entities. This query flexibility means data can be\nstructured based on logical relationships between data elements rather than how\nthe data is accessed. Second, if in the future I want to make a version of the\ngame that has a server component that needs to run some of the game logic, it\nshould be relatively easy to port the game logic from Datascript to Datomic\nsince their APIs are so similar.\n\n### Reagent\n\nThe Zetawar user interface is rendered with\n[Reagent](https://reagent-project.github.io/). Given that I wanted to make\nZetawar web based (I'll discuss that decision more in a future post), the\ndeclarative React model on which Reagent is based is a natural fit. The game\nboard interface is a pure function of the game state and the performance\nrequirements of a turn based strategy game are not so great that rendering to\nthe DOM (as opposed to Canvas or WebGL) is likely to be a bottleneck.\n\nSo why Reagent and not [Om](https://github.com/omcljs/om),\n[Om.next](https://github.com/omcljs/om/wiki/Quick-Start-\\(om.next\\)), or\n[Rum](https://github.com/tonsky/rum)? Mostly it comes down to familiarity. I\nhave used Reagent on other projects and found it to be effective and easy to\nuse. It's also quite straightforward, with the addition of cursors in Reagent\n0.5, to mimic the single atom approach of Om if desired. Om.next and Rum both\nlook interesting, and I did seriously consider Om.next, but wasn't convinced\nthat it provided enough benefits over Reagent (for this project anyway) to\njustify both the risk of using such a new library as well as the extra time it\nwould take to get up to speed on it. I dismissed Rum on similar grounds. Though\nI do appreciate its minimalist design and hope to try it out on some future\nproject.\n\n### Posh\n\n[Posh](https://github.com/mpdairy/posh) is the glue that connects Reagent to\nDatascript. It provides a mechanism for specifying Datascript queries and pulls\nthat are only updated when data related to them has changed. Compared to the\nsimplest alternative of rerunning queries on every transaction, this provides a\nhuge performance boost.\n\nAs a new and not very well known library, it is likely the riskiest of these\nchoices. But so far, it has worked extremely well. The only issue I have\nencountered was a bad interaction with the new lazy-by-default reactions in\nReagent\n[0.6.0-alpha](https://reagent-project.github.io/news/news060-alpha.html). Posh\ncurrently assumes that only a single transaction will take place between each\nevaluation of its reactions. With the new lazy-by-default reactions, if\nmultiple transactions take place before the next render, some Posh queries may\nfail to run when related data changes. Thankfully, there is a simple\nworkaround, run Reagent's flush in a Datascript transaction listener so that\nall reactions are evaluated after every transaction. This destroys the\nperformance improvements possible with lazy transactions, but so far this\nhasn't been a significant issue. Also, it sounds like this problem will be\nfixed in the next version of Posh, so it may not even be an issue by the time\nthe final version of Reagent 0.6.0 is released.\n\n### Conclusion\n\nThat's it for now. In my next post I'll dig into how these libraries are tied\ntogether in Zetawar.\n"
  },
  {
    "path": "site/posts/2016-05-22-why-zetawar.markdown",
    "content": "---\ndescription: Why Zetawar?\ndate-published: 2016-05-22\n---\n\nIn my last post I promised to dig into the architecture of Zetawar. I'm still\nplanning to do that, but before I get too deep in the technical weeds I thought\nit might be a good idea to explain why Zetawar exists.\n\nThe story begins earlier this year when I wanted to play a round of Weewar (an\nonline turn based strategy game) with my wife. I hadn't played it in several\nyears, but remembered it as a fun game and was looking for something turn based\nthat wouldn't require us both to be available to play at exactly the same time.\nUnfortunately, I discovered EA had shut down Weewar. I also discovered another\ngame, Elite Command, that suffered a similar fate. And while I don't blame\neither EA or the author of Elite Command for shutting them down — these things\naren't free to maintain — it did seem like a shame that their respective player\ncommunities couldn't keep them running themselves.\n\nThat's when the idea for Zetawar was born. What if I made a similar web based\ngame that was open source (Zetawar isn't yet, but it will be) and didn't\nrequire an application server? It would have some limitations due to the lack\nof a server side communication channel, but it would be something that players\ncould invest in without worrying about a third party shutting it down.\n\nSo, I started writing it. Of course, once you start writing a game, it's\nimpossible to resist trying to implement your favorite features, so Zetawar has\na few extra goals beyond just being an open source, app server free,\nWeewar-like game.\n\nFirst, it will have a first class bot interface. In college I enjoyed competing\nin programming competitions and I think programming AI bots that can play\nagainst each other is one of the most fun ways to do that. I also believe\nbasing these contests on a fun game that students can enjoy playing with each\nother outside the programming competition can increase their interest and\nengagement.\n\nSecond, it's going to be as customizable as I can reasonably make it. Unit\nstats, terrain effects, tilesets, and even some of the game rules will be\nmodifiable by players. This will allow players to help me with things like\nbalancing unit stats and should keep the game interesting for a long time. It\nwill also allow the game to serve as a laboratory of sorts for people\ninterested in designing similar games. Want to make your own tactical strategy\ngame? Prototype it first in Zetawar!\n\nThird, Zetawar is currently and will continue to be written in ClojureScript.\nClojureScript is the most enjoyable language I've found for writing web\napplications, and I want to both continue working with it and promote its\nadoption. Hopefully Zetawar can serve as a fun example people can point to when\nthey talk about things written in ClojureScript and, once it's open sourced,\ncan be an interesting code base for other ClojureScript enthusiasts to learn\nfrom and contribute to.\n\nSo that's what I'm hoping to accomplish with Zetawar. If any of those goals\ninterest you, please follow [@ZetawarGame](https://twitter.com/ZetawarGame) to\nkeep updated on how things are progressing, and if you have any feedback either\nsend me a tweet or fill out the [feedback\nform](http://goo.gl/forms/RgTpkCYDBk).\n"
  },
  {
    "path": "site/posts/2017-02-07-from-callbacks-to-reified-events.md",
    "content": "---\ndescription: From Callbacks to Reified Events\ndate-published: 2017-02-07\n---\n\nHello again! It's time to dust off this blog and get some posts out.\n\nA lot has changed in Zetawar over the past few months. I've discussed some of\nthose changes in\nKickstarter\n[updates](https://www.kickstarter.com/projects/djwhitt/zetawar/updates), but\nonly at a high level. Here I'm going to dig into them in a bit more detail.\n\nFirst, let's take a look at the event system.\n\nIf you recall, the demo version of Zetawar used simple callback event handlers.\nWith that approach, calling an event handler looked like this:\n\n```clojure\n[:button {:on-click #(handlers/repair conn %)}\n \"Repair\"]])\n```\n\nAnd the event handlers looked like this:\n\n```clojure\n(defn repair [conn ev]\n  (let [db @conn\n         [q r] (first (d/q '[:find ?q ?r\n                            :where\n                            [?a :app/selected-q ?q]\n                            [?a :app/selected-r ?r]]\n                          db))]\n     (game/repair! conn (app/current-game-id db) q r)\n     (clear-selection conn nil)))\n```\n\nThe important things to notice here are that the handler takes a reference,\n'conn', and performs side effects. This approach has the virtue of being easy to\nimplement, hence it's use in the demo, but it also has several drawbacks.\nPerforming actions via side effects in the call to 'repair!' makes testing and\ninteractive development difficult. Passing in the connection is also problematic\nsince it means theoretically the value of the connection can change during the\nexecution of the function. Though, in practice, JavaScript's single threaded\nexecution model prevents this.\n\nSo how can we improve things? Thankfully,\nthe [re-frame framework](https://github.com/Day8/re-frame) has\nalready\n[solved this problem](https://github.com/Day8/re-frame/blob/master/docs/EffectfulHandlers.md).\nWe can just follow its lead. Instead of calling handlers directly, we'll\ndispatch event messages and modify the handlers to accept a DB value rather than\na connection reference. Also, instead of performing side effects directly, we'll\nreturn a data structure describing the side effects we want executed.\n\nWith the new approach, dispatching an event looks like this:\n\n```clojure\n[:button {:on-click #(dispatch [::events.ui/repair-selected])}\n \"Repair\"])\n```\n\nAnd handling an event looks like this:\n\n```clojure\n(defmethod router/handle-event ::repair-selected\n  [{:as handler-ctx :keys [db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [q r] (app/selected-hex db)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/repair-unit\n                  :action/faction-color cur-faction-color\n                  :action/q q\n                  :action/r r}]\n                [::clear-selection]]}))\n                \n(defmethod router/handle-event ::execute-action\n  [{:as handler-ctx :keys [db]} [_ action]]\n  (let [game (app/current-game db)]\n      ;; Irrelevant code omitted ...\n      {:tx     (game/action-tx db game action)\n       :notify [[:zetawar.players/apply-action :faction.color/all action]]})))\n      \n```\n\nBecause of changes to the AI system, the new code splits the event handler in\ntwo. The first handler extracts information about the current faction and\nselection from the DB and turns it into an 'execute-action' event which it\nreturns along with a 'clear-selection' event. For the sake of brevity, most of\nthe code has been omitted from the second handler, but the essentials are still\nthere. It takes a DB value and an 'action' map and returns a transaction and an\nAI notification. Taken together the returned data describes the actions\nperformed as side effects in the original handler.\n\nSo, now our handlers are pure functions. They take values as arguments and\nreturn values. This makes them much easier to execute interactively and test,\nbut there's still something missing. How do the returned values change the\napplication state? In the next post we'll examine this question in detail as we\nlook at the event router.\n"
  },
  {
    "path": "src/clj/zetawar/subs.clj",
    "content": "(ns zetawar.subs)\n\n;; TODO: check that tracks defined this way are only evaluated once per arg combination\n;; TODO: add support for doc strings\n;; TODO: name function '~name'-track-fn (or something similar)\n(defmacro deftrack [name params* & body]\n  `(def ~name\n     (partial r/track (fn ~name ~params*\n                        (do\n                          ;;(js/console.debug \"Running sub: \" '~name (rest ~params*))\n                          ~@body)))))\n"
  },
  {
    "path": "src/clj/zetawar/views/site.clj",
    "content": "(ns zetawar.views.site\n  (:require\n   [zetawar.views.common :refer [footer head navbar]]\n   [hiccup.page :refer [html5 include-js]]\n   [zetawar.site :as site])\n  (:import\n   [java.text SimpleDateFormat]\n   [java.util TimeZone]))\n\n(defn date-fmt [date]\n  (let [formater (SimpleDateFormat. \"MMMM dd, yyyy\")]\n    (.setTimeZone formater (TimeZone/getTimeZone \"GMT\"))\n    (.format formater date)))\n\n(defn render-index [data]\n  (let [{global-meta :meta} data]\n    (html5\n     (head data (:site-title global-meta))\n     [:body#app\n      [:div#main\n       (navbar \"Game\")\n       [:div.container.text-center\n        [:img {:src (site/prefix \"/images/spin-64.gif\")}]]\n       (footer)]\n      (include-js (site/prefix \"/js/main.js\"))])))\n\n(defn render-blog-index [data]\n  (let [{:keys [entries] global-meta :meta} data]\n    (html5\n     (head data (:site-title global-meta))\n     [:body#site\n      (navbar \"Blog\")\n      [:div.container\n       (into [:div#blog-posts]\n             (for [entry entries]\n               [:div.blog-post\n                [:div.blog-post-title (:description entry)]\n                [:div.blog-post-meta\n                 [:a {:href (:permalink entry)}\n                  (date-fmt (:date-published entry))]]\n                (:content entry)]))]\n      (footer)\n      (include-js (site/prefix \"/js/main.js\"))])))\n\n(defn render-blog-post [data]\n  (let [{:keys [entry] global-meta :meta} data]\n    (html5\n     (head data (:site-title global-meta))\n     [:body#site\n      (navbar \"Blog\")\n      [:div.container\n       [:div.blog-post\n        [:div.blog-post-title (:description entry)]\n        [:div.blog-post-meta\n         (date-fmt (:date-published entry))]\n        (:content entry)]]\n      (footer)\n      (include-js (site/prefix \"/js/main.js\"))])))\n\n(defn render-page [data]\n  (let [{:keys [entry] global-meta :meta} data]\n    (html5\n     (head data (:site-title global-meta))\n     [:body#site\n      (navbar (:name entry))\n      [:div.container\n       (:content entry)]\n      (footer)\n      (include-js (site/prefix \"/js/main.js\"))])))\n\n(defn render-devcards [data]\n  (let [{:keys [entry] global-meta :meta} data]\n    (html5\n     (head data \"Zetawar Devcards\")\n     [:body#devcards\n      (include-js (site/prefix \"/js/main.js\"))\n      (include-js (site/prefix \"/js/main/devcards.js\"))])))\n"
  },
  {
    "path": "src/cljc/zetawar/hex.cljc",
    "content": "(ns zetawar.hex\n  (:require\n   [zetawar.util :refer [abs]]))\n\n(def min-q 0)\n(def min-r 0)\n\n(def max-q 100)\n(def max-r 100)\n\n(defn east [q r]\n  [(inc q) r])\n\n(defn east-of? [q1 r1 q2 r2]\n  (= [q2 r2] (east q1 r1)))\n\n(defn west [q r]\n  [(dec q) r])\n\n(defn west-of? [q1 r1 q2 r2]\n  (= [q2 r2] (west q1 r1)))\n\n(defn northeast [q r]\n  (if (= (mod r 2) 0)\n    [q (dec r)]\n    [(inc q) (dec r)]))\n\n(defn northeast-of? [q1 r1 q2 r2]\n  (= [q2 r2] (northeast q1 r1)))\n\n(defn northwest [q r]\n  (if (= (mod r 2) 0)\n    [(dec q) (dec r)]\n    [q (dec r)]))\n\n(defn northwest-of? [q1 r1 q2 r2]\n  (= [q2 r2] (northwest q1 r1)))\n\n(defn southeast [q r]\n  (if (= (mod r 2) 0)\n    [q (inc r)]\n    [(inc q) (inc r)]))\n\n(defn southeast-of? [q1 r1 q2 r2]\n  (= [q2 r2] (southeast q1 r1)))\n\n(defn southwest [q r]\n  (if (= (mod r 2) 0)\n    [(dec q) (inc r)]\n    [q (inc r)]))\n\n(defn southwest-of? [q1 r1 q2 r2]\n  (= [q2 r2] (southwest q1 r1)))\n\n;; TODO: make this more elegant\n(defn opposite? [q1 r1 q2 r2 q3 r3]\n  (or (and (east-of? q1 r1 q2 r2) (east-of? q2 r2 q3 r3))\n      (and (west-of? q1 r1 q2 r2) (west-of? q2 r2 q3 r3))\n      (and (northwest-of? q1 r1 q2 r2) (northwest-of? q2 r2 q3 r3))\n      (and (southwest-of? q1 r1 q2 r2) (southwest-of? q2 r2 q3 r3))\n      (and (northeast-of? q1 r1 q2 r2) (northeast-of? q2 r2 q3 r3))\n      (and (southeast-of? q1 r1 q2 r2) (southeast-of? q2 r2 q3 r3))))\n\n(def adjacents\n  (memoize (fn adjacents [q r]\n             #{(east q r) (west q r)\n               (northeast q r) (northwest q r)\n               (southeast q r) (southwest q r)})))\n\n(defn adjacent? [q1 r1 q2 r2]\n  ((adjacents q1 r1) [q2 r2]))\n\n(defn offset->cube [q r]\n  (let [x (- q (/ (- r (mod r 2)) 2))\n        z r\n        y (- 0 x z)]\n    [x y z]))\n\n(defn distance [q1 r1 q2 r2]\n  (let [[x1 y1 z1] (offset->cube q1 r1)\n        [x2 y2 z2] (offset->cube q2 r2)]\n    (max (abs (- x1 x2))\n         (abs (- y1 y2))\n         (abs (- z1 z2)))))\n"
  },
  {
    "path": "src/cljc/zetawar/site.cljc",
    "content": "(ns zetawar.site\n  (:require\n   [clojure.string :as string])\n  #?(:cljs\n     (:require-macros\n      [zetawar.site :refer [env-prefix env-build env-build-timestamp]])))\n\n#?(:clj\n   (do\n\n     (defmacro env-prefix []\n       (or (System/getenv \"ZETAWAR_PREFIX\") \"\"))\n\n     (defmacro env-build []\n       (or (System/getenv \"ZETAWAR_BUILD\") \"\"))\n\n     (defmacro env-build-timestamp []\n       (or (System/getenv \"BUILD_TIMESTAMP\") \"\"))\n\n     )\n   )\n\n(def +prefix+ (env-prefix))\n\n(defn prefix [& url-parts]\n  (apply str +prefix+ url-parts))\n\n(def build (env-build))\n\n(def build-timestamp (env-build-timestamp))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Perun\n\n#?(:clj\n   (do\n\n     (defn slug-fn [filename]\n       (let [[year month day & parts] (string/split filename #\"[-\\.]\")\n             name-part (some->> parts\n                                drop-last\n                                not-empty\n                                (string/join \"-\")\n                                string/lower-case)]\n         (when (and year month day name-part)\n           (str year \"/\" month \"/\" day \"/\" name-part))))\n\n     (defn permalink-fn [{:keys [slug path filename] :as data}]\n       (if (string/starts-with? path \"posts\")\n         (str +prefix+ \"/blog/\" slug \"/\")\n         (str +prefix+ (string/replace filename #\"\\.markdown\" \"/\"))))\n\n     (defn devcards? [{:keys [path]}]\n       (= path \"pages/devcards.markdown\"))\n\n     (defn page? [{:keys [path]}]\n       (and (not (devcards? path))\n            (string/starts-with? path \"pages/\")))\n\n     (defn post? [{:keys [path]}]\n       (and (not (devcards? path))\n            (string/starts-with? path \"posts/\")))\n\n     )\n   )\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Devcards\n\n#?(:cljs\n   (do\n\n     (defn viewing-devcards?\n       \"Returns true if currently viewing devcards.\"\n       []\n       (re-matches #\".*/devcards.*\" js/window.location.href))\n\n     )\n   )\n"
  },
  {
    "path": "src/cljc/zetawar/util.cljc",
    "content": "(ns zetawar.util\n  #?(:cljs\n     (:require-macros [zetawar.util :refer [inspect]])))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Accessors\n\n(defn solo\n  \"Like first, but throws if more than one item.\"\n  [coll]\n  (assert (not (next coll)))\n  (first coll))\n\n(defn only\n  \"Like first, but throws unless exactly one item.\"\n  [coll]\n  (assert (not (next coll)))\n  (if-let [result (first coll)]\n    result\n    (assert false)))\n\n(defn ssolo\n  \"Same as (solo (solo coll)).\"\n  [coll]\n  (solo (solo coll)))\n\n(defn oonly\n  \"Same as (only (only coll)).\"\n  [coll]\n  (only (only coll)))\n\n(defn select-values\n  \"Returns a vector containing only those values who's key is in ks.\"\n  [m ks]\n  (reduce #(if-let [v (m %2)] (conj %1 v) %1) [] ks))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Math\n\n(defn abs [x]\n  (#?(:clj Math/abs :cljs js/Math.abs) x))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Debugging\n\n(defn log-inspect [expr result]\n  #?(:cljs (js/console.debug expr result)))\n\n\n(defn- inspect-1 [expr]\n  `(let [result# ~expr]\n     (zetawar.util/log-inspect '~expr result#)\n     result#))\n\n#?(:clj\n   (do\n\n     (defmacro inspect [& exprs]\n       `(do ~@(map inspect-1 exprs)))\n\n     (defmacro breakpoint []\n       '(do (js* \"debugger;\")\n            nil)) ; (prevent \"return debugger;\" in compiled javascript)\n\n     )\n   )\n"
  },
  {
    "path": "src/cljc/zetawar/views/common.cljc",
    "content": "(ns zetawar.views.common\n  #?@(:clj\n      [(:require\n        [clojure.java.io :as io]\n        [hiccup.page :refer [html5 include-css include-js]]\n        [zetawar.site :as site])]\n      :cljs\n      [(:require\n        [cljsjs.react-bootstrap]\n        [zetawar.site :as site])]))\n\n#?(:clj\n   (do\n\n     (defn ga [tracking-id]\n       [:script\n        (str \"(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\"\n             \"(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\"\n             \"m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\"\n             \"})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\"\n             \"ga('create', '\" tracking-id \"', 'auto');\"\n             \"ga('send', 'pageview');\")])\n\n     (defn sentry [sentry-url environment]\n       [[:script {:src \"https://cdn.ravenjs.com/3.9.1/raven.min.js\"}]\n        [:script (str \"Raven.config('\" sentry-url \"', {\"\n                      \"release: '\" site/build \"',\"\n                      \"environment: '\" environment \"',\"\n                      \"tags: {git_commit: '\" site/build \"'}\"\n                      \"}).install();\")]])\n\n     (defn head [{global-meta :meta :as data} title]\n       (into [:head\n              [:meta {:charset \"utf-8\"}]\n              [:meta {:http-equiv \"X-UA-Compatible\" :content \"IE=edge\"}]\n              [:meta {:name \"viewport\" :content \"width=device-width, initial-scale=1\"}]\n              [:title title]\n              (include-css (site/prefix \"/css/main.css\"))\n              (include-css (site/prefix \"/css/highlight/default.css\"))\n              (include-js (site/prefix \"/js/highlight.pack.js\"))\n              [:script \"hljs.initHighlightingOnLoad();\"]\n              (some-> (:google-analytics-tracking-id global-meta)\n                      ga)]\n             (some-> (:sentry-url global-meta)\n                     (sentry (:sentry-environment global-meta)))))\n\n     ))\n\n(def nav-links\n  [{:href (site/prefix \"/\")                            :title \"Game\"}\n   {:href (site/prefix \"/blog\")                        :title \"Blog\"}\n   {:href (site/prefix \"/docs\")                        :title \"Documentation\"}\n   {:href \"https://github.com/zetawar/zetawar/issues\"  :title \"Roadmap\"}\n   {:href (site/prefix \"/backers\")                     :title \"Backers\"}])\n\n(defn navbar\n  ([] (navbar nil))\n  ([active-title]\n   #?(\n      :clj\n      [:div#navbar-wrapper {:data-active-title active-title}\n       [:nav.navbar.navbar-inverse.navbar-fixed-top\n        [:div.container\n         [:div.navbar-header\n          [:a.navbar-brand {:href \"/\"}\n           [:img {:src (site/prefix \"/images/navbar-logo.svg\")}]\n           \"Zetawar\"]]\n         [:div#navbar-collapse.collapse.navbar-collapse\n          (into [:ul.nav.navbar-nav ]\n                (for [{:keys [href title]} nav-links]\n                  (if (= title active-title)\n                    [:li {:class \"active\"} [:a {:href href} title]]\n                    [:li [:a {:href href} title]])))]]]]\n      :cljs\n      [:> js/ReactBootstrap.Navbar {:fixed-top true :inverse true}\n       [:> js/ReactBootstrap.Navbar.Header\n        [:> js/ReactBootstrap.Navbar.Brand\n         [:a {:href \"/\"}\n          [:img {:src (site/prefix \"/images/navbar-logo.svg\")}]\n          \"Zetawar\"]]\n        [:> js/ReactBootstrap.Navbar.Toggle]]\n       [:> js/ReactBootstrap.Navbar.Collapse\n        (into [:> js/ReactBootstrap.Nav]\n              (map-indexed (fn [idx {:keys [href title]}]\n                             (let [active (= title active-title)]\n                               [:> js/ReactBootstrap.NavItem {:event-key idx :active active :href href}\n                                title]))\n                           nav-links))]]\n      )\n   ))\n\n(defn footer []\n  [:div.container\n   [:div#footer\n    [:p\n     \"Build: \"\n     (if (not-empty site/build)\n       [:a {:href (str \"/builds/\" site/build)} site/build]\n       \"DEV\")\n     (when (not-empty site/build-timestamp)\n       (str \" • \" site/build-timestamp))]\n    [:p\n     \"Follow \"\n     [:a {:href \"https://twitter.com/ZetawarGame\"} \"@ZetawarGame\"]\n     \" for updates. \"\n     \"Questions or comments? Send us some \"\n     [:a {:href \"http://goo.gl/forms/RgTpkCYDBk\"} \"feedback\"]\n     \".\"]\n    [:p\n     \"Copyright 2016 Arugaba LLC under the \"\n     [:a {:href \"https://github.com/Zetawar/zetawar/blob/master/LICENSE.txt\"}\n      \"MIT license\"]]\n    [:p\n     \"Artwork from \"\n     [:a {:href \"https://github.com/cvincent/elite-command\"} \"Elite Command\"]\n     \" Copyright 2015 Chris Vincent under \"\n     [:a {:href \"http://creativecommons.org/licenses/by/4.0/\"}\n      \"Creative Commons Attribution 4.0 International License\"]]]])\n"
  },
  {
    "path": "src/cljs/zetawar/app.cljs",
    "content": "(ns zetawar.app\n  (:require\n   [cognitect.transit :as transit]\n   [datascript.core :as d]\n   [zetawar.data :as data]\n   [zetawar.db :refer [e find-by qe qes qess]]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]\n   [zetawar.players :as players]\n   [zetawar.util :as util :refer [breakpoint inspect]]))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; DB Accessors\n\n(defn root [db]\n  (find-by db :app/game))\n\n(defn current-game [db]\n  (:app/game (root db)))\n\n(defn current-game-id [db]\n  (:game/id (current-game db)))\n\n(defn selected-hex [db]\n  (let [{:keys [app/selected-q app/selected-r]} (root db)]\n    [selected-q selected-r]))\n\n(defn targeted-hex [db]\n  (let [{:keys [app/targeted-q app/targeted-r]} (root db)]\n    [targeted-q targeted-r]))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Game setup\n\n(defn create-players! [{:as app-ctx :keys [conn players]}]\n  (let [factions (qess '[:find ?f\n                         :where\n                         [_  :app/game ?g]\n                         [?g :game/factions ?f]]\n                       @conn)]\n    ;; TODO: cleanup (relocate?) player stopping\n    ;; Stop existing players (when starting new games)\n    (when players\n      (doseq [[_ player] @players]\n        (when player\n          (players/stop player))))\n    (doseq [{:as faction :keys [faction/ai faction/color]} factions]\n      (let [player-type (if ai ::players/reference-ai ::players/human)\n            player (players/new-player app-ctx player-type color)]\n        (d/transact! conn [{:db/id (e faction)\n                            :faction/player-type player-type}])\n        (players/start player)\n        (swap! players assoc color player)))))\n\n;; TODO: take option map instead of being multiarity\n(defn start-new-game!\n  ([{:as app-ctx :keys [conn players]} scenario-id]\n   (start-new-game! app-ctx data/rulesets data/maps data/scenarios scenario-id))\n  ([{:as app-ctx :keys [conn players]} rulesets map-defs scenario-defs scenario-id]\n   (let [game (current-game @conn)]\n     (when game\n       (d/transact conn [[:db.fn/retractEntity (e game)]]))\n     (let [scenario-def (scenario-defs scenario-id)\n           game-id (game/load-scenario! conn rulesets map-defs scenario-def)\n           db @conn\n           app-eid (or (some-> (root db) e) -101)\n           game (game/game-by-id db game-id)\n           turn-stepping (= (game/faction-count db game)\n                            (game/ai-faction-count db game))]\n       (d/transact! conn [{:db/id app-eid\n                           :app/game [:game/id game-id]\n                           :app/hide-win-message false\n                           :app/ai-turn-stepping turn-stepping}])\n       ;; Skip player creation for tests\n       (when players\n         (create-players! app-ctx))))))\n\n;; TODO: take option map instead of being multiarity\n(defn load-game-state!\n  ([{:as app-ctx :keys [conn players]} game-state]\n   (load-game-state! app-ctx data/rulesets data/maps data/scenarios game-state))\n  ([{:as app-ctx :keys [conn players]} rulesets map-defs scenario-defs game-state]\n   (let [game-id (game/load-game-state! conn\n                                        rulesets\n                                        map-defs\n                                        scenario-defs\n                                        game-state)\n         db @conn\n         game (game/game-by-id db game-id)\n         turn-stepping (= (game/faction-count db game)\n                          (game/ai-faction-count db game))]\n     (d/transact! conn [{:db/id -1\n                         :app/game [:game/id game-id]\n                         :app/ai-turn-stepping turn-stepping}])\n     ;; Skip player creation for tests\n     (when players\n       (create-players! app-ctx)))))\n"
  },
  {
    "path": "src/cljs/zetawar/benchmarks.cljs",
    "content": "(ns zetawar.benchmarks\n  (:require\n   [clojure.string :as string]\n   [datascript.core :as d]\n   [reagent.core :as r]\n   [zetawar.app :as app]\n   [zetawar.data :as data]\n   [zetawar.db :as db :refer [qe]]\n   [zetawar.game :as game]\n   [zetawar.hex :as hex]\n   [zetawar.logging :as log]))\n\n(defn setup-conn []\n  (let [conn (d/create-conn db/schema)]\n    (app/start-new-game! {:conn conn} :sterlings-aruba-multiplayer)\n    conn))\n\n(defn run-benchmarks []\n  (let [suite (js/Benchmark.Suite.)\n        db @(setup-conn)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (-> suite\n        (.add \"valid-moves\"\n              (fn []\n                (game/valid-moves db game unit)))\n        (.on \"complete\"\n             (fn []\n               (this-as this\n                 (js/console.log (str \"valid-moves: \" (aget this 0))))))\n        (.run))))\n"
  },
  {
    "path": "src/cljs/zetawar/cli.cljs",
    "content": "(ns zetawar.cli\n  (:require\n   [cljs.nodejs :as nodejs]\n   [integrant.core :as ig]\n   [zetawar.app :as app]\n   [zetawar.db :as db]\n   [zetawar.events.game]\n   [zetawar.events.player]\n   [zetawar.game :as game]\n   [zetawar.players.ai.custom]\n   [zetawar.players.ai.reference]\n   [zetawar.players.human]\n   [zetawar.router :as router]\n   [zetawar.system.datascript]\n   [zetawar.system.game]\n   [zetawar.system.players]\n   [zetawar.system.router]))\n\n(def cli-game-config\n  {:zetawar.system/datascript {:schema     db/schema}\n   :zetawar.system/players    {}\n   :zetawar.system/router     {:datascript (ig/ref :zetawar.system/datascript)\n                               :players    (ig/ref :zetawar.system/players)}\n   :zetawar.system/game       {:datascript (ig/ref :zetawar.system/datascript)\n                               :router     (ig/ref :zetawar.system/router)\n                               :players    (ig/ref :zetawar.system/players)}})\n(nodejs/enable-util-print!)\n\n(defn end-turn [system]\n  (let [ev-chan (-> system :zetawar.system/router :ev-chan)\n        db @(-> system :zetawar.system/datascript :conn)\n        game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)]\n    (router/dispatch ev-chan [:zetawar.events.game/execute-action\n                              {:action/type :action.type/end-turn\n                               :action/faction-color cur-faction-color}])))\n\n;; (.install (js/require \"source-map-support\"))\n\n;; TODO: switch to cljs require\n(def readline (js/require \"readline\"))\n\n;; TODO: add dummy logger player\n;; TODO: add log level cli option\n;; TODO: add ability to disable turn stepping\n;; TODO: add ability to pass in scenario\n\n(defn ^:export -main [& args]\n  (let [system (ig/init cli-game-config)\n        ev-chan (-> system :zetawar.system/router :ev-chan)\n        conn (-> system :zetawar.system/datascript :conn)\n        rl (.createInterface readline #js {\"input\" js/process.stdin\n                                           \"output\" js/process.stdout\n                                           \"terminal\" false})]\n    (.on rl \"line\" (fn [line] (end-turn system)))\n    (println \"Hello from the Zetawar game runner!\")\n    (app/start-new-game! (:zetawar.system/game system) :sterlings-aruba-ai-vs-ai)\n    (router/dispatch ev-chan [:zetawar.events.player/send-game-state\n                              (->> @conn\n                                   app/current-game\n                                   game/current-faction-color)])))\n\n(set! *main-cli-fn* -main)\n"
  },
  {
    "path": "src/cljs/zetawar/core.cljs",
    "content": "(ns zetawar.core\n  (:require\n   [cljsjs.raven]\n   [integrant.core :as ig]\n   [reagent.core :as r]\n   [zetawar.app :as app]\n   [zetawar.benchmarks :as benchmarks]\n   [zetawar.db :as db]\n   [zetawar.events.game]\n   [zetawar.events.player]\n   [zetawar.events.ui]\n   [zetawar.game :as game]\n   [zetawar.js.game]\n   [zetawar.js.hex]\n   [zetawar.logging :as log]\n   [zetawar.players.ai.custom-js]\n   [zetawar.players.ai.custom]\n   [zetawar.players.ai.reference]\n   [zetawar.players.human]\n   [zetawar.router.reagent]\n   [zetawar.serialization :as serialization]\n   [zetawar.site :as site]\n   [zetawar.system :as system]\n   [zetawar.system.datascript]\n   [zetawar.system.game-views]\n   [zetawar.system.game]\n   [zetawar.system.players]\n   [zetawar.system.reagent]\n   [zetawar.system.router]\n   [zetawar.util :refer [breakpoint inspect]]\n   [zetawar.views :as views]\n   [zetawar.views.common :refer [navbar]]))\n\n(defonce system (atom nil))\n\n(defn run []\n  (let [body-id (-> js/document\n                    (.getElementsByTagName \"body\")\n                    (aget 0)\n                    .-id)]\n    (case body-id\n      \"app\"\n      (let [target (.getElementById js/document \"main\")\n            views-cfg (:zetawar.system/game-views @system)]\n        (r/render-component\n         [views/app-root views-cfg]\n         target))\n\n      \"site\"\n      (let [target (.getElementById js/document \"navbar-wrapper\")\n            active-navbar-title (.getAttribute target \"data-active-title\")]\n        (r/render-component\n         [navbar active-navbar-title]\n         target))\n\n      \"benchmarks\"\n      (benchmarks/run-benchmarks)\n\n      ;; Default\n      nil)))\n\n;; TODO: don't start game when viewing non-game site\n;; TODO: don't start game when running benchmarks\n(defn ^:export init []\n  (when-not (site/viewing-devcards?)\n    (reset! system (ig/init system/game-config))\n    (let [game-cfg (:zetawar.system/game @system)\n          encoded-game-state (some-> js/window.location.hash\n                                     not-empty\n                                     (subs 1))]\n      (if encoded-game-state\n        (serialization/load-encoded-game-state! game-cfg encoded-game-state)\n        (app/start-new-game! game-cfg :sterlings-aruba-multiplayer))\n      (set! (.-onload js/window) run))))\n\n(defn ^:export reload []\n  (when-not (site/viewing-devcards?)\n    (ig/suspend! @system)\n    (reset! system (ig/resume system/game-config @system)))\n  (run))\n"
  },
  {
    "path": "src/cljs/zetawar/data.cljs",
    "content": "(ns zetawar.data)\n\n(def dicts\n  {:en\n   {;; General\n    :save-button \"Save\"\n    :cancel-button \"Cancel\"\n    :close-button \"Close\"\n\n    ;; Game links\n    :copy-game-url-link \"Copy Link\"\n    :new-game-link \"New Game\"\n    :end-turn-link \"End Turn?\"\n\n    ;; Game labels\n    :credits-label \"Credits\"\n    :round-label \"Round\"\n\n    ;; Faction configuration\n    :configure-faction-tip \"Configure faction\"\n    :configure-faction-title-prefix \"Configure faction: \"\n    :player-type-label \"Player type\"\n\n    ;; Faction colors\n    :red-name \"Red\"\n    :blue-name \"Blue\"\n    :yellow-name \"Yellow\"\n    :pink-name \"Pink\"\n    :green-name \"Green\"\n    :orange-name \"Orange\"\n\n    ;; Armor types\n    :personnel-name \"Personnel\"\n    :armored-name \"Armored\"\n    :naval-name \"Naval\"\n    :air-name \"Air\"\n\n    ;; New game settings\n    :new-game-title \"Start a new game\"\n    :scenario-label \"Scenario\"\n    :start-button \"Start\"\n\n    ;; Unit and base actions\n    :move-unit-button \"Move\"\n    :build-unit-button \"Build\"\n    :attack-unit-button \"Attack\"\n    :repair-unit-button \"Repair\"\n    :field-repair-button \"Field Repair\"\n    :capture-base-button \"Capture\"\n\n    ;; Building units\n    :build-title \"Select a unit to build\"\n    :unit-cost-label \"Cost: \"\n    :armor-type-label \"Armor Type\"\n    :movement-label \"Move- ment\"\n    :armor-label \"Armor\"\n    :range-label \"Range\"\n    :attack-label \"Attack\"\n    :field-repair-label \"Field Repair?\"\n    :while-capturing-label \"While capturing: \"\n    :unit-cannot-capture-bases-label \"Unit cannot capture bases\"\n\n    ;; Win dialog\n    :win-title \"Congratulations! You won!\"\n    :win-body\n    (str \"Thanks for playing Zetawar! If you're interested in staying up-to-date \"\n         \"with Zetawar as it develops, please follow \"\n         \"<a href=\\\"https://twitter.com/ZetawarGame\\\">ZetawarGame</a> on Twitter.\")\n\n    ;; Gameplay tips\n    :select-unit-or-base-tip \"Select a unit or base.\"\n    :select-target-or-destination-tip \"Select a destination or target to move, attack, or repair.\"\n    :multiplayer-tip\n    (str \"To play multiplayer follow the instructions \"\n         \"<a href=\\\"https://www.kickstarter.com/projects/311016908/zetawar/posts/1608417\\\">\"\n         \"here</a>.\")\n\n    ;; Status information\n    :tile-coordinates-label \"X,Y (Tile Coordinates)\"\n    :terrain-effects-label \"Movement Cost, Attack Bonus, Armor Bonus\"\n    :movement-cost-label \"Movement Cost\"\n    :attack-bonus-label \"Attack Bonus\"\n    :armor-bonus-label \"Armor Bonus\"\n    :selected-label \"Selected: \"\n    :targeted-label \"Targeted: \"\n    :hover-tile-location \"Hover: \"\n\n    ;; Ending turn\n    :end-turn-alert \"Are you sure you want to end your turn? You still have available moves.\"\n    :end-turn-confirm \"Yes, end my turn\"}})\n\n\n;; TODO: remove redundant id keys (?)\n(def rulesets\n  {:zetawar\n   {:settings\n    {:ranged-attack-bonus   1\n     :adjacent-attack-bonus 1\n     :flanking-attack-bonus 2\n     :opposite-attack-bonus 3\n     :stochastic-damage     false\n     :self-repair           false\n     :move-through-friendly true}\n\n    :terrains\n    {:plains        {:description \"Plains\"\n                     :image \"tilesets/elite-command/terrains/plains.png\"}\n     :mountains     {:description \"Mountains\"\n                     :image \"tilesets/elite-command/terrains/mountains.png\"}\n     :woods         {:description \"Woods\"\n                     :image \"tilesets/elite-command/terrains/woods.png\"}\n     :desert        {:description \"Desert\"\n                     :image \"tilesets/elite-command/terrains/desert.png\"}\n     :tundra        {:description \"Tundra\"\n                     :image \"tilesets/elite-command/terrains/tundra.png\"}\n     :swamp         {:description \"Swamp\"\n                     :image \"tilesets/elite-command/terrains/swamp.png\"}\n     :ford          {:description \"Ford\"\n                     :image \"tilesets/elite-command/terrains/ford.png\"}\n     :shallow-water {:description \"Shallow Water\"\n                     :image \"tilesets/elite-command/terrains/shallow-water.png\"}\n     :deep-water    {:description \"Deep Water\"\n                     :image \"tilesets/elite-command/terrains/deep-water.png\"}\n     :base          {:description \"Base\"\n                     :image \"tilesets/elite-command/terrains/base-COLOR.png\"}\n     :seaport       {:description \"Seaport\"\n                     :image \"tilesets/elite-command/terrains/seaport-COLOR.png\"}\n     :airfield      {:description \"Airfield\"\n                     :image \"tilesets/elite-command/terrains/airfield-COLOR.png\"}}\n\n    :unit-state-maps\n    {:move-attack-once\n     {:start-state :start\n      :built-state :done\n      :states\n      {:start {:transitions\n               {:move-unit         :moved\n                :attack-unit       :done\n                :repair-unit       :done\n                :field-repair-unit :done\n                :capture-base      :done}}\n       :moved {:transitions\n               {:attack-unit       :done\n                :repair-unit       :done\n                :field-repair-unit :done\n                :capture-base      :done}}\n       :done  {}}}\n\n     :move-attack-twice\n     {:start-state :start\n      :built-state :done\n      :states\n      {:start              {:transitions\n                            {:move-unit    :moved-1-attacked-0\n                             :attack-unit  :moved-0-attacked-1\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :moved-0-attacked-1 {:transitions\n                            {:attack-unit  :done\n                             :capture-base :done}}\n       :moved-1-attacked-0 {:transitions\n                            {:attack-unit  :moved-1-attacked-1\n                             :capture-base :done}}\n       :moved-1-attacked-1 {:transitions\n                            {:attack-unit  :done\n                             :capture-base :done}}\n       :done               {}}}\n\n     :free-attack-once\n     {:start-state :start\n      :built-state :done\n      :states\n      {:start              {:transitions\n                            {:move-unit    :moved-1-attacked-0\n                             :attack-unit  :moved-0-attacked-1\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :moved-0-attacked-1 {:transitions\n                            {:move-unit    :done\n                             :capture-base :done}}\n       :moved-1-attacked-0 {:transitions\n                            {:attack-unit  :done\n                             :capture-base :done}}\n       :done               {}}}\n\n     :free-attack-twice\n     {:start-state :start\n      :built-state :done\n      :states\n      {:start              {:transitions\n                            {:move-unit    :moved-1-attacked-0\n                             :attack-unit  :moved-0-attacked-1\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :moved-0-attacked-1 {:transitions\n                            {:move-unit    :moved-1-attacked-1\n                             :attack-unit  :moved-0-attacked-2\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :moved-0-attacked-2 {:transitions\n                            {:move-unit    :done\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :moved-1-attacked-0 {:transitions\n                            {:attack-unit  :moved-1-attacked-1\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :moved-1-attacked-1 {:transitions\n                            {:attack-unit  :done\n                             :repair-unit  :done\n                             :capture-base :done}}\n       :done               {}}}\n\n     :exclusive\n     {:start-state :start\n      :built-state :done\n      :states\n      {:start {:transitions\n               {:move-unit   :done\n                :attack-unit :done\n                :repair-unit :done}}\n       :done  {}}}}\n\n    ;; TODO: check how repair amount works in Elite Command\n    ;; TODO: add :buildable-at (or -by?) => {<terrain type ids>...}\n    :units\n    {:infantry {:description \"Infantry\"\n                :cost 75\n                :movement 9\n                :can-capture true\n                :can-repair #{}\n                :buildable-at #{:base}\n                :min-range 1\n                :max-range 1\n                :armor-type :personnel\n                :armor 6\n                :capturing-armor 4\n                :repair 1\n                :state-map :move-attack-once\n                :image \"tilesets/elite-command/units/infantry-COLOR.png\"\n                :terrain-effects\n                {:plains    {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n                 :mountains {:movement-cost 6 :armor-bonus  5 :attack-bonus  2}\n                 :woods     {:movement-cost 4 :armor-bonus  3 :attack-bonus  2}\n                 :desert    {:movement-cost 4 :armor-bonus -1 :attack-bonus -1}\n                 :tundra    {:movement-cost 4 :armor-bonus -1 :attack-bonus -1}\n                 :swamp     {:movement-cost 6 :armor-bonus -2 :attack-bonus -2}\n                 :ford      {:movement-cost 5 :armor-bonus -1 :attack-bonus -1}\n                 :base      {:movement-cost 2 :armor-bonus  3 :attack-bonus  2}\n                 :seaport   {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                 :airfield  {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}\n                :attack-strengths\n                {:personnel 6\n                 :armored   3\n                 :naval     4\n                 :air       0}\n                :zoc\n                [:personnel :armored]}\n     :grenadier {:description \"Grenadier\"\n                 :cost 150\n                 :movement 9\n                 :can-capture true\n                 :can-repair #{}\n                 :buildable-at #{:base}\n                 :min-range 1\n                 :max-range 2\n                 :armor-type :personnel\n                 :armor 8\n                 :capturing-armor 6\n                 :repair 1\n                 :state-map :move-attack-once\n                 :image \"tilesets/elite-command/units/grenadier-COLOR.png\"\n                 :terrain-effects\n                 {:plains    {:movement-cost 4 :armor-bonus  0 :attack-bonus  0}\n                  :mountains {:movement-cost 9 :armor-bonus  5 :attack-bonus  2}\n                  :woods     {:movement-cost 4 :armor-bonus  3 :attack-bonus -1}\n                  :desert    {:movement-cost 5 :armor-bonus -1 :attack-bonus  0}\n                  :tundra    {:movement-cost 5 :armor-bonus -1 :attack-bonus  0}\n                  :swamp     {:movement-cost 9 :armor-bonus -2 :attack-bonus -2}\n                  :ford      {:movement-cost 9 :armor-bonus -1 :attack-bonus -1}\n                  :base      {:movement-cost 3 :armor-bonus  3 :attack-bonus -1}\n                  :seaport   {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n                  :airfield  {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}}\n                 :attack-strengths\n                 {:personnel 8\n                  :armored   9\n                  :naval     6\n                  :air       3}\n                 :zoc\n                 [:personnel :armored :naval]}\n     :mortar {:description \"Mortar\"\n              :cost 200\n              :movement 9\n              :can-capture true\n              :can-repair #{}\n              :buildable-at #{:base}\n              :min-range 2\n              :max-range 3\n              :armor-type :personnel\n              :armor 6\n              :capturing-armor 4\n              :repair 1\n              :state-map :move-attack-once\n              :image \"tilesets/elite-command/units/mortar-COLOR.png\"\n              :terrain-effects\n              {:plains    {:movement-cost 4 :armor-bonus  0 :attack-bonus  0}\n               :mountains {:movement-cost 9 :armor-bonus  5 :attack-bonus  3}\n               :woods     {:movement-cost 4 :armor-bonus  3 :attack-bonus -2}\n               :desert    {:movement-cost 5 :armor-bonus -1 :attack-bonus  0}\n               :tundra    {:movement-cost 5 :armor-bonus -1 :attack-bonus -1}\n               :swamp     {:movement-cost 9 :armor-bonus -2 :attack-bonus -2}\n               :ford      {:movement-cost 9 :armor-bonus -1 :attack-bonus -2}\n               :base      {:movement-cost 3 :armor-bonus  3 :attack-bonus -2}\n               :seaport   {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n               :airfield  {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}}\n              :attack-strengths\n              {:personnel 10\n               :armored   10\n               :naval     10\n               :air       0}}\n     :ranger {:description \"Ranger\"\n              :cost 200\n              :movement 9\n              :can-capture true\n              :can-repair #{}\n              :buildable-at #{:base}\n              :min-range 1\n              :max-range 1\n              :armor-type :personnel\n              :armor 9\n              :capturing-armor 7\n              :repair 1\n              :state-map :move-attack-once\n              :image \"tilesets/elite-command/units/ranger-COLOR.png\"\n              :terrain-effects\n              {:plains        {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n               :mountains     {:movement-cost 6 :armor-bonus  5 :attack-bonus  2}\n               :woods         {:movement-cost 3 :armor-bonus  4 :attack-bonus  2}\n               :desert        {:movement-cost 3 :armor-bonus -1 :attack-bonus -1}\n               :tundra        {:movement-cost 3 :armor-bonus -1 :attack-bonus -1}\n               :swamp         {:movement-cost 3 :armor-bonus -2 :attack-bonus -2}\n               :ford          {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n               :shallow-water {:movement-cost 6 :armor-bonus -2 :attack-bonus -2}\n               :base          {:movement-cost 2 :armor-bonus  3 :attack-bonus  2}\n               :seaport       {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n               :airfield      {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}\n              :attack-strengths\n              {:personnel 9\n               :armored   4\n               :naval     6\n               :air       0}\n              :zoc\n              [:personnel :armored]}\n     :sniper {:description \"Sniper\"\n              :cost 275\n              :movement 9\n              :can-capture true\n              :can-repair #{}\n              :buildable-at #{:base}\n              :min-range 2\n              :max-range 4\n              :armor-type :personnel\n              :armor 5\n              :capturing-armor 3\n              :repair 1\n              :state-map :move-attack-once\n              :image \"tilesets/elite-command/units/sniper-COLOR.png\"\n              :terrain-effects\n              {:plains    {:movement-cost 4 :armor-bonus  0 :attack-bonus  0}\n               :mountains {:movement-cost 9 :armor-bonus  5 :attack-bonus  4}\n               :woods     {:movement-cost 4 :armor-bonus  3 :attack-bonus -4}\n               :desert    {:movement-cost 5 :armor-bonus -1 :attack-bonus -1}\n               :tundra    {:movement-cost 5 :armor-bonus -1 :attack-bonus -1}\n               :swamp     {:movement-cost 9 :armor-bonus -2 :attack-bonus -3}\n               :ford      {:movement-cost 9 :armor-bonus -1 :attack-bonus -6}\n               :base      {:movement-cost 3 :armor-bonus  3 :attack-bonus  2}\n               :seaport   {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}}\n              :attack-strengths\n              {:personnel 8\n               :armored   0\n               :naval     0\n               :air       0}}\n     :medic {:description \"Medic\"\n             :cost 100\n             :movement 9\n             :can-capture true\n             :can-repair #{:personnel}\n             :buildable-at #{:base}\n             :min-range 1\n             :max-range 1\n             :armor-type :personnel\n             :armor 6\n             :capturing-armor 4\n             :repair 1\n             :state-map :move-attack-once\n             :image \"tilesets/elite-command/units/medic-COLOR.png\"\n             :terrain-effects\n             {:plains    {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n              :mountains {:movement-cost 6 :armor-bonus  5 :attack-bonus  2}\n              :woods     {:movement-cost 4 :armor-bonus  3 :attack-bonus  2}\n              :desert    {:movement-cost 4 :armor-bonus -1 :attack-bonus -1}\n              :tundra    {:movement-cost 4 :armor-bonus -1 :attack-bonus -1}\n              :swamp     {:movement-cost 6 :armor-bonus -2 :attack-bonus -2}\n              :ford      {:movement-cost 5 :armor-bonus -1 :attack-bonus -1}\n              :base      {:movement-cost 2 :armor-bonus  3 :attack-bonus  2}\n              :seaport   {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n              :airfield  {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}\n            :attack-strengths\n            {:personnel 5\n             :armored   2\n             :naval     3\n             :air       0}\n            :zoc\n            [:personnel :armored]}\n    :engineer {:description \"Engineer\"\n               :cost 200\n               :movement 9\n               :can-capture true\n               :can-repair #{:armored}\n               :buildable-at #{:base}\n               :min-range 1\n               :max-range 1\n               :armor-type :personnel\n               :armor 6\n               :capturing-armor 4\n               :repair 1\n               :state-map :move-attack-once\n               :image \"tilesets/elite-command/units/engineer-COLOR.png\"\n               :terrain-effects\n               {:plains        {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n                :mountains     {:movement-cost 6 :armor-bonus  5 :attack-bonus  2}\n                :woods         {:movement-cost 4 :armor-bonus  3 :attack-bonus  2}\n                :desert        {:movement-cost 4 :armor-bonus -1 :attack-bonus -1}\n                :tundra        {:movement-cost 4 :armor-bonus -1 :attack-bonus -1}\n                :swamp         {:movement-cost 6 :armor-bonus -2 :attack-bonus -2}\n                :ford          {:movement-cost 5 :armor-bonus -1 :attack-bonus -1}\n                :shallow-water {:movement-cost 7 :armor-bonus -2 :attack-bonus -2}\n                :base          {:movement-cost 2 :armor-bonus  3 :attack-bonus  2}\n                :seaport       {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                :airfield      {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}\n              :attack-strengths\n              {:personnel 5\n               :armored   2\n               :naval     3\n               :air       0}\n              :zoc\n              [:personnel :armored]}\n     ;; Armored\n     :humvee {:description \"Humvee\"\n              :cost 300\n              :movement 15\n              :can-capture false\n              :can-repair #{}\n              :buildable-at #{:base}\n              :min-range 1\n              :max-range 1\n              :armor-type :armored\n              :armor 8\n              :repair 1\n              :state-map :free-attack-twice\n              :image \"tilesets/elite-command/units/humvee-COLOR.png\"\n              :terrain-effects\n              {:plains   {:movement-cost 3  :armor-bonus  0 :attack-bonus  0}\n               :woods    {:movement-cost 6  :armor-bonus -2 :attack-bonus -2}\n               :desert   {:movement-cost 3  :armor-bonus  0 :attack-bonus  0}\n               :tundra   {:movement-cost 6  :armor-bonus  0 :attack-bonus  0}\n               :swamp    {:movement-cost 12 :armor-bonus -3 :attack-bonus -3}\n               :ford     {:movement-cost 12 :armor-bonus -1 :attack-bonus -1}\n               :base     {:movement-cost 2  :armor-bonus  0 :attack-bonus  0}\n               :seaport  {:movement-cost 2  :armor-bonus  0 :attack-bonus  0}\n               :airfield {:movement-cost 2  :armor-bonus  0 :attack-bonus  0}}\n              :attack-strengths\n              {:personnel 9\n               :armored   3\n               :naval     6\n               :air       0}\n              :zoc\n              [:personnel :armored]}\n     :tank {:description \"Tank\"\n            :cost 350\n            :movement 12\n            :can-capture false\n            :can-repair #{}\n            :buildable-at #{:base}\n            :min-range 1\n            :max-range 1\n            :armor-type :armored\n            :armor 12\n            :repair 1\n            :state-map :move-attack-once\n            :image \"tilesets/elite-command/units/tank-COLOR.png\"\n            :terrain-effects\n            {:plains   {:movement-cost 3 :armor-bonus  0 :attack-bonus 0}\n             :woods    {:movement-cost 6 :armor-bonus -3 :attack-bonus 0}\n             :desert   {:movement-cost 4 :armor-bonus  0 :attack-bonus 0}\n             :tundra   {:movement-cost 5 :armor-bonus  0 :attack-bonus 0}\n             :swamp    {:movement-cost 8 :armor-bonus -4 :attack-bonus 0}\n             :ford     {:movement-cost 8 :armor-bonus  0 :attack-bonus 0}\n             :base     {:movement-cost 2 :armor-bonus -2 :attack-bonus 0}\n             :seaport  {:movement-cost 2 :armor-bonus  0 :attack-bonus 0}\n             :airfield {:movement-cost 2 :armor-bonus  0 :attack-bonus 0}}\n            :attack-strengths\n            {:personnel 10\n             :armored   10\n             :naval     9\n             :air       0}\n            :zoc\n            [:personnel :armored]}\n     :mobile-flak {:description \"Mobile Flak\"\n                   :cost 350\n                   :movement 9\n                   :can-capture false\n                   :can-repair #{}\n                   :min-range 1\n                   :max-range 3\n                   :armor-type :armored\n                   :armor 8\n                   :repair 1\n                   :state-map :move-attack-twice\n                   :buildable-at #{:base :airfield}\n                   :image \"tilesets/elite-command/units/mobileflak-COLOR.png\"\n                   :terrain-effects\n                   {:plains   {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}\n                    :woods    {:movement-cost 6 :armor-bonus -2 :attack-bonus -3}\n                    :desert   {:movement-cost 6 :armor-bonus  0 :attack-bonus  0}\n                    :tundra   {:movement-cost 6 :armor-bonus -1 :attack-bonus  0}\n                    :swamp    {:movement-cost 6 :armor-bonus -3 :attack-bonus -2}\n                    :ford     {:movement-cost 6 :armor-bonus -2 :attack-bonus -1}\n                    :base     {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                    :seaport  {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                    :airfield {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}\n                   :attack-strengths\n                   {:personnel 0\n                    :armored   0\n                    :naval     0\n                    :air       14}\n                   :zoc\n                   [:air]}\n     :heavy-tank {:description \"Heavy Tank\"\n                  :cost 500\n                  :movement 12\n                  :can-capture false\n                  :can-repair #{}\n                  :buildable-at #{:base}\n                  :min-range 1\n                  :max-range 1\n                  :armor-type :armored\n                  :armor 15\n                  :repair 1\n                  :state-map :move-attack-once\n                  :image \"tilesets/elite-command/units/heavytank-COLOR.png\"\n                  :terrain-effects\n                  {:plains  {:movement-cost 4 :armor-bonus  0 :attack-bonus 0}\n                   :woods   {:movement-cost 6 :armor-bonus -3 :attack-bonus 0}\n                   :desert  {:movement-cost 5 :armor-bonus  0 :attack-bonus 0}\n                   :tundra  {:movement-cost 7 :armor-bonus  0 :attack-bonus 0}\n                   :swamp   {:movement-cost 8 :armor-bonus -4 :attack-bonus 0}\n                   :ford    {:movement-cost 8 :armor-bonus -2 :attack-bonus 0}\n                   :base    {:movement-cost 3 :armor-bonus -2 :attack-bonus 0}\n                   :seaport {:movement-cost 3 :armor-bonus  0 :attack-bonus 0}}\n                  :attack-strengths\n                  {:personnel 15\n                   :armored   12\n                   :naval     14\n                   :air       0}\n                  :zoc\n                  [:personnel :armored]}\n     :artillery {:description \"Artillery\"\n                 :cost 400\n                 :movement 8\n                 :can-capture false\n                 :can-repair #{}\n                 :buildable-at #{:base}\n                 :min-range 3\n                 :max-range 4\n                 :armor-type :armored\n                 :armor 6\n                 :repair 1\n                 :state-map :exclusive\n                 :image \"tilesets/elite-command/units/artillery-COLOR.png\"\n                 :terrain-effects\n                 {:plains  {:movement-cost 4 :armor-bonus  0 :attack-bonus  0}\n                  :woods   {:movement-cost 6 :armor-bonus -3 :attack-bonus -3}\n                  :desert  {:movement-cost 5 :armor-bonus  0 :attack-bonus  0}\n                  :tundra  {:movement-cost 5 :armor-bonus  0 :attack-bonus  0}\n                  :swamp   {:movement-cost 6 :armor-bonus -3 :attack-bonus -3}\n                  :ford    {:movement-cost 6 :armor-bonus -2 :attack-bonus  0}\n                  :base    {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                  :seaport {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}\n                 :attack-strengths\n                 {:personnel 12\n                  :armored   13\n                  :naval     14\n                  :air       0}}\n     :heavy-artillery {:description \"Heavy Artillery\"\n                       :cost 1250\n                       :movement 2\n                       :can-capture false\n                       :can-repair #{}\n                       :buildable-at #{:base}\n                       :min-range 4\n                       :max-range 6\n                       :armor-type :armored\n                       :armor 8\n                       :repair 1\n                       :state-map :exclusive\n                       :image \"tilesets/elite-command/units/heavyartillery-COLOR.png\"\n                       :terrain-effects\n                       {:plains  {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                        :woods   {:movement-cost 2 :armor-bonus -3 :attack-bonus -5}\n                        :desert  {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                        :tundra  {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}\n                        :swamp   {:movement-cost 2 :armor-bonus -3 :attack-bonus -5}\n                        :ford    {:movement-cost 2 :armor-bonus -2 :attack-bonus  0}\n                        :base    {:movement-cost 1 :armor-bonus  0 :attack-bonus  0}\n                        :seaport {:movement-cost 1 :armor-bonus  0 :attack-bonus  0}}\n                       :attack-strengths\n                       {:personnel 14\n                        :armored   15\n                        :naval     15\n                        :air       0}}\n     ;; Naval\n     :frigate {:description \"Frigate\"\n               :cost 300\n               :movement 8\n               :can-capture false\n               :can-repair #{}\n               :buildable-at #{:seaport}\n               :min-range 1\n               :max-range 1\n               :armor-type :naval\n               :armor 8\n               :repair 1\n               :state-map :free-attack-twice\n               :image \"tilesets/elite-command/units/frigate-COLOR.png\"\n               :terrain-effects\n               {:deep-water    {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}\n                :shallow-water {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}\n                :seaport       {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}}\n               :attack-strengths\n               {:personnel 9\n                :armored   9\n                :naval     8\n                :air       0}\n               :zoc\n               [:personnel :armored]}\n     :destroyer {:description \"Destroyer\"\n                 :cost 600\n                 :movement 5\n                 :can-capture false\n                 :can-repair #{}\n                 :buildable-at #{:seaport}\n                 :min-range 1\n                 :max-range 4\n                 :armor-type :naval\n                 :armor 12\n                 :repair 1\n                 :state-map :free-attack-once\n                 :image \"tilesets/elite-command/units/destroyer-COLOR.png\"\n                 :terrain-effects\n                 {:deep-water {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}\n                  :seaport    {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}}\n                 :attack-strengths\n                 {:personnel 10\n                  :armored   12\n                  :naval     8\n                  :air       0}\n                 :zoc\n                 [:personnel :armored]}\n     :cruiser {:description \"Cruiser\"\n               :cost 1000\n               :movement 4\n               :can-capture false\n               :can-repair #{}\n               :buildable-at #{:seaport}\n               :min-range 1\n               :max-range 3\n               :armor-type :naval\n               :armor 15\n               :repair 1\n               :state-map :move-attack-twice\n               :image \"tilesets/elite-command/units/cruiser-COLOR.png\"\n               :terrain-effects\n               {:deep-water {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}\n                :seaport    {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}}\n               :attack-strengths\n               {:personnel 14\n                :armored   16\n                :naval     12\n                :air       14}\n               :zoc\n               [:personnel :armored]}\n     ;; Air\n     :fighter {:description \"Fighter\"\n               :cost 450\n               :movement 24\n               :can-capture false\n               :can-repair #{}\n               :min-range 1\n               :max-range 1\n               :armor-type :air\n               :armor 12\n               :repair 1\n               :state-map :free-attack-once\n               :buildable-at #{:airfield}\n               :image \"tilesets/elite-command/units/fighter-COLOR.png\"\n               :terrain-effects\n               {:plains        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :mountains     {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :woods         {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :desert        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :tundra        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :swamp         {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :ford          {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :deep-water    {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :shallow-water {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :base          {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :seaport       {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :airfield      {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}}\n               :attack-strengths\n               {:personnel 4\n                :armored   6\n                :naval     8\n                :air       16}}\n     :bomber {:description \"Bomber\"\n              :cost 650\n              :movement 24\n              :can-capture false\n              :can-repair #{}\n              :min-range 1\n              :max-range 1\n              :armor-type :air\n              :armor 10\n              :repair 1\n              :state-map :free-attack-once\n              :buildable-at #{:airfield}\n              :image \"tilesets/elite-command/units/bomber-COLOR.png\"\n              :terrain-effects\n              {:plains        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :mountains     {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :woods         {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :desert        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :tundra        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :swamp         {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :ford          {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :deep-water    {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :shallow-water {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :base          {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :seaport       {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n               :airfield      {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}}\n              :attack-strengths\n              {:personnel 14\n               :armored   16\n               :naval     15\n               :air       4}}\n     :gunship {:description \"Gunship\"\n               :cost 350\n               :movement 15\n               :can-capture false\n               :can-repair #{}\n               :min-range 1\n               :max-range 1\n               :armor-type :air\n               :armor 8\n               :repair 1\n               :state-map :free-attack-once\n               :buildable-at #{:airfield}\n               :image \"tilesets/elite-command/units/gunship-COLOR.png\"\n               :terrain-effects\n               {:plains        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :mountains     {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :woods         {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :desert        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :tundra        {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :swamp         {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :ford          {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :deep-water    {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :shallow-water {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :base          {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :seaport       {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}\n                :airfield      {:movement-cost 3 :armor-bonus 0 :attack-bonus 0}}\n               :attack-strengths\n               {:personnel 18\n                :armored   10\n                :naval     9\n                :air       8}\n               :zoc\n               [:personnel :armored :naval]}}}})\n\n;; TODO: remove redundant id keys (?)\n(def maps\n  {:sterlings-aruba\n   {:id :sterlings-aruba\n    :description \"Sterling's Aruba\"\n    :created-by \"Weewar\"\n    :terrains\n    [;; Row 1\n     {:q 1 :r 0 :terrain-type :plains}\n     {:q 2 :r 0 :terrain-type :plains}\n     {:q 3 :r 0 :terrain-type :mountains}\n     {:q 4 :r 0 :terrain-type :deep-water}\n     {:q 5 :r 0 :terrain-type :deep-water}\n     {:q 6 :r 0 :terrain-type :deep-water}\n     ;; Row 2\n     {:q 0 :r 1 :terrain-type :plains}\n     {:q 1 :r 1 :terrain-type :mountains}\n     {:q 2 :r 1 :terrain-type :plains} ;; Multiplayer base location\n     {:q 3 :r 1 :terrain-type :plains}\n     {:q 4 :r 1 :terrain-type :mountains}\n     {:q 5 :r 1 :terrain-type :woods}\n     {:q 6 :r 1 :terrain-type :deep-water}\n     {:q 7 :r 1 :terrain-type :deep-water}\n     ;; Row 3\n     {:q 0 :r 2 :terrain-type :mountains}\n     {:q 1 :r 2 :terrain-type :plains} ;; Multiplayer base location\n     {:q 2 :r 2 :terrain-type :plains}\n     {:q 3 :r 2 :terrain-type :plains}\n     {:q 4 :r 2 :terrain-type :plains}\n     {:q 5 :r 2 :terrain-type :woods}\n     {:q 6 :r 2 :terrain-type :mountains}\n     {:q 7 :r 2 :terrain-type :mountains}\n     {:q 8 :r 2 :terrain-type :deep-water}\n     ;; Row 4\n     {:q 0 :r 3 :terrain-type :plains}\n     {:q 1 :r 3 :terrain-type :plains}\n     {:q 2 :r 3 :terrain-type :plains}\n     {:q 3 :r 3 :terrain-type :plains}\n     {:q 4 :r 3 :terrain-type :woods}\n     {:q 5 :r 3 :terrain-type :plains}\n     {:q 6 :r 3 :terrain-type :plains}\n     {:q 7 :r 3 :terrain-type :mountains}\n     {:q 8 :r 3 :terrain-type :deep-water}\n     ;; Row 5\n     {:q 0 :r 4 :terrain-type :deep-water}\n     {:q 1 :r 4 :terrain-type :mountains} ;; Multiplayer base location\n     {:q 2 :r 4 :terrain-type :plains}\n     {:q 3 :r 4 :terrain-type :plains}\n     {:q 4 :r 4 :terrain-type :plains}\n     {:q 5 :r 4 :terrain-type :plains}\n     {:q 6 :r 4 :terrain-type :plains}\n     {:q 7 :r 4 :terrain-type :plains}\n     {:q 8 :r 4 :terrain-type :deep-water}\n     ;; Row 6\n     {:q 0 :r 5 :terrain-type :deep-water}\n     {:q 1 :r 5 :terrain-type :plains}\n     {:q 2 :r 5 :terrain-type :plains}\n     {:q 3 :r 5 :terrain-type :woods}\n     {:q 4 :r 5 :terrain-type :plains}\n     {:q 5 :r 5 :terrain-type :plains}\n     {:q 6 :r 5 :terrain-type :plains}\n     {:q 7 :r 5 :terrain-type :plains}\n     {:q 8 :r 5 :terrain-type :deep-water}\n     ;; Row 7\n     {:q 0 :r 6 :terrain-type :deep-water}\n     {:q 1 :r 6 :terrain-type :mountains}\n     {:q 2 :r 6 :terrain-type :plains}\n     {:q 3 :r 6 :terrain-type :woods}\n     {:q 4 :r 6 :terrain-type :plains}\n     {:q 5 :r 6 :terrain-type :plains}\n     {:q 6 :r 6 :terrain-type :plains}\n     {:q 7 :r 6 :terrain-type :plains} ;; Multiplayer base location\n     {:q 8 :r 6 :terrain-type :mountains}\n     ;; Row 8\n     {:q 0 :r 7 :terrain-type :deep-water}\n     {:q 1 :r 7 :terrain-type :mountains}\n     {:q 2 :r 7 :terrain-type :woods}\n     {:q 3 :r 7 :terrain-type :mountains}\n     {:q 4 :r 7 :terrain-type :plains}\n     {:q 5 :r 7 :terrain-type :plains} ;; Multiplayer base location\n     {:q 6 :r 7 :terrain-type :mountains}\n     {:q 7 :r 7 :terrain-type :plains}\n     ;; Row 9\n     {:q 1 :r 8 :terrain-type :deep-water}\n     {:q 2 :r 8 :terrain-type :deep-water}\n     {:q 3 :r 8 :terrain-type :deep-water}\n     {:q 4 :r 8 :terrain-type :deep-water}\n     {:q 5 :r 8 :terrain-type :mountains} ;; Multiplayer base location\n     {:q 6 :r 8 :terrain-type :plains}\n     {:q 7 :r 8 :terrain-type :plains}]}\n\n   :city-sprawl\n   {:id :city-sprawl\n    :description \"City Sprawl\"\n    :created-by \"Weewar\"\n    :terrains\n    [;; Row 1\n     {:q 2 :r 0 :terrain-type :woods}\n     {:q 3 :r 0 :terrain-type :woods}\n     {:q 4 :r 0 :terrain-type :woods}\n     {:q 5 :r 0 :terrain-type :woods}\n     {:q 6 :r 0 :terrain-type :woods}\n     {:q 10 :r 0 :terrain-type :woods}\n     {:q 11 :r 0 :terrain-type :woods}\n     {:q 12 :r 0 :terrain-type :woods}\n     ;; Row 2\n     {:q 1 :r 1 :terrain-type :woods}\n     {:q 2 :r 1 :terrain-type :plains}\n     {:q 3 :r 1 :terrain-type :plains}\n     {:q 4 :r 1 :terrain-type :plains}\n     {:q 5 :r 1 :terrain-type :plains}\n     {:q 6 :r 1 :terrain-type :woods}\n     {:q 8 :r 1 :terrain-type :woods}\n     {:q 9 :r 1 :terrain-type :plains}\n     {:q 10 :r 1 :terrain-type :plains}\n     {:q 11 :r 1 :terrain-type :plains}\n     {:q 12 :r 1 :terrain-type :woods}\n     {:q 13 :r 1 :terrain-type :woods}\n     ;; Row 3\n     {:q 1 :r 2 :terrain-type :woods}\n     {:q 2 :r 2 :terrain-type :plains}\n     {:q 3 :r 2 :terrain-type :mountains}\n     {:q 4 :r 2 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 5 :r 2 :terrain-type :plains}\n     {:q 6 :r 2 :terrain-type :plains}\n     {:q 7 :r 2 :terrain-type :woods}\n     {:q 8 :r 2 :terrain-type :woods}\n     {:q 9 :r 2 :terrain-type :plains}\n     {:q 10 :r 2 :terrain-type :plains}\n     {:q 11 :r 2 :terrain-type :plains}\n     {:q 12 :r 2 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 13 :r 2 :terrain-type :plains}\n     {:q 14 :r 2 :terrain-type :woods}\n     ;; Row 4\n     {:q 0 :r 3 :terrain-type :plains}\n     {:q 1 :r 3 :terrain-type :plains}\n     {:q 2 :r 3 :terrain-type :mountains}\n     {:q 3 :r 3 :terrain-type :plains} ;; Multiplayer base location (red owned)\n     {:q 4 :r 3 :terrain-type :mountains}\n     {:q 5 :r 3 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 6 :r 3 :terrain-type :woods}\n     {:q 7 :r 3 :terrain-type :mountains}\n     {:q 8 :r 3 :terrain-type :mountains}\n     {:q 9 :r 3 :terrain-type :plains}\n     {:q 10 :r 3 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 11 :r 3 :terrain-type :desert}\n     {:q 12 :r 3 :terrain-type :woods}\n     {:q 13 :r 3 :terrain-type :plains}\n     {:q 14 :r 3 :terrain-type :woods}\n     ;; Row 5\n     {:q 0 :r 4 :terrain-type :woods}\n     {:q 1 :r 4 :terrain-type :plains}\n     {:q 2 :r 4 :terrain-type :plains} ;; Multiplayer base location (red)\n     {:q 3 :r 4 :terrain-type :desert}\n     {:q 4 :r 4 :terrain-type :plains} ;; Multiplayer base location (unknown)\n     {:q 5 :r 4 :terrain-type :desert}\n     {:q 6 :r 4 :terrain-type :plains}\n     {:q 7 :r 4 :terrain-type :woods}\n     {:q 8 :r 4 :terrain-type :mountains}\n     {:q 9 :r 4 :terrain-type :plains}\n     {:q 10 :r 4 :terrain-type :mountains}\n     {:q 11 :r 4 :terrain-type :desert}\n     {:q 12 :r 4 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 13 :r 4 :terrain-type :mountains}\n     {:q 14 :r 4 :terrain-type :plains}\n     ;; Row 6\n     {:q 0 :r 5 :terrain-type :woods}\n     {:q 1 :r 5 :terrain-type :plains}\n     {:q 2 :r 5 :terrain-type :plains}\n     {:q 3 :r 5 :terrain-type :plains}\n     {:q 4 :r 5 :terrain-type :woods}\n     {:q 5 :r 5 :terrain-type :plains}\n     {:q 6 :r 5 :terrain-type :plains}\n     {:q 7 :r 5 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 8 :r 5 :terrain-type :plains}\n     {:q 9 :r 5 :terrain-type :plains}\n     {:q 10 :r 5 :terrain-type :plains}\n     {:q 11 :r 5 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 12 :r 5 :terrain-type :desert}\n     {:q 13 :r 5 :terrain-type :plains} ;; Multiplayer base location (blue)\n     {:q 14 :r 5 :terrain-type :woods}\n     ;; Row 7\n     {:q 1 :r 6 :terrain-type :woods}\n     {:q 2 :r 6 :terrain-type :woods}\n     {:q 3 :r 6 :terrain-type :plains}\n     {:q 4 :r 6 :terrain-type :woods}\n     {:q 5 :r 6 :terrain-type :mountains}\n     {:q 6 :r 6 :terrain-type :plains}\n     {:q 7 :r 6 :terrain-type :plains}\n     {:q 8 :r 6 :terrain-type :woods}\n     {:q 9 :r 6 :terrain-type :plains}\n     {:q 10 :r 6 :terrain-type :mountains}\n     {:q 11 :r 6 :terrain-type :plains}\n     {:q 12 :r 6 :terrain-type :plains}\n     {:q 13 :r 6 :terrain-type :mountains}\n     {:q 14 :r 6 :terrain-type :plains}\n     ;; Row 8\n     {:q 1 :r 7 :terrain-type :woods}\n     {:q 2 :r 7 :terrain-type :woods}\n     {:q 3 :r 7 :terrain-type :plains}\n     {:q 4 :r 7 :terrain-type :plains}\n     {:q 5 :r 7 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 6 :r 7 :terrain-type :mountains}\n     {:q 7 :r 7 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 8 :r 7 :terrain-type :mountains}\n     {:q 9 :r 7 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 10 :r 7 :terrain-type :plains}\n     {:q 11 :r 7 :terrain-type :plains}\n     {:q 12 :r 7 :terrain-type :mountains}\n     {:q 13 :r 7 :terrain-type :plains}\n     {:q 14 :r 7 :terrain-type :woods}\n     ;; Row 9\n     {:q 1 :r 8 :terrain-type :woods}\n     {:q 2 :r 8 :terrain-type :woods}\n     {:q 3 :r 8 :terrain-type :plains}\n     {:q 4 :r 8 :terrain-type :mountains}\n     {:q 5 :r 8 :terrain-type :plains}\n     {:q 6 :r 8 :terrain-type :plains}\n     {:q 7 :r 8 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 8 :r 8 :terrain-type :plains}\n     {:q 9 :r 8 :terrain-type :plains}\n     {:q 10 :r 8 :terrain-type :mountains}\n     {:q 11 :r 8 :terrain-type :plains}\n     {:q 12 :r 8 :terrain-type :plains}\n     {:q 13 :r 8 :terrain-type :plains}\n     {:q 14 :r 8 :terrain-type :woods}\n     ;; Row 10\n     {:q 0 :r 9 :terrain-type :woods}\n     {:q 1 :r 9 :terrain-type :plains}\n     {:q 2 :r 9 :terrain-type :plains}\n     {:q 3 :r 9 :terrain-type :desert}\n     {:q 4 :r 9 :terrain-type :woods} ;; Multiplayer base location (unowned)\n     {:q 5 :r 9 :terrain-type :plains}\n     {:q 6 :r 9 :terrain-type :plains}\n     {:q 7 :r 9 :terrain-type :mountains}\n     {:q 8 :r 9 :terrain-type :plains}\n     {:q 9 :r 9 :terrain-type :desert}\n     {:q 10 :r 9 :terrain-type :woods}\n     {:q 11 :r 9 :terrain-type :woods}\n     {:q 12 :r 9 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 13 :r 9 :terrain-type :plains}\n     {:q 14 :r 9 :terrain-type :woods}\n     ;; Row 11\n     {:q 0 :r 10 :terrain-type :woods}\n     {:q 1 :r 10 :terrain-type :plains}\n     {:q 2 :r 10 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 3 :r 10 :terrain-type :desert}\n     {:q 4 :r 10 :terrain-type :desert}\n     {:q 5 :r 10 :terrain-type :plains}\n     {:q 6 :r 10 :terrain-type :plains}\n     {:q 7 :r 10 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 8 :r 10 :terrain-type :woods}\n     {:q 9 :r 10 :terrain-type :plains}\n     {:q 10 :r 10 :terrain-type :desert}\n     {:q 11 :r 10 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 12 :r 10 :terrain-type :desert}\n     {:q 13 :r 10 :terrain-type :plains}\n     {:q 14 :r 10 :terrain-type :plains}\n     ;; Row 12\n     {:q 0 :r 11 :terrain-type :plains}\n     {:q 1 :r 11 :terrain-type :plains}\n     {:q 2 :r 11 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 3 :r 11 :terrain-type :plains} ;; Multiplayer base location (yellow)\n     {:q 4 :r 11 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 5 :r 11 :terrain-type :plains}\n     {:q 6 :r 11 :terrain-type :mountains}\n     {:q 7 :r 11 :terrain-type :mountains}\n     {:q 8 :r 11 :terrain-type :woods}\n     {:q 9 :r 11 :terrain-type :plains} ;; Multiplayer base location (unowned)\n     {:q 10 :r 11 :terrain-type :mountains}\n     {:q 11 :r 11 :terrain-type :plains} ;; Multiplayer base location (pink)\n     {:q 12 :r 11 :terrain-type :plains}\n     {:q 13 :r 11 :terrain-type :plains}\n     {:q 14 :r 11 :terrain-type :woods}\n     ;; Row 13\n     {:q 0 :r 12 :terrain-type :woods}\n     {:q 1 :r 12 :terrain-type :woods}\n     {:q 2 :r 12 :terrain-type :woods}\n     {:q 3 :r 12 :terrain-type :mountains}\n     {:q 4 :r 12 :terrain-type :desert}\n     {:q 5 :r 12 :terrain-type :mountains}\n     {:q 6 :r 12 :terrain-type :plains}\n     {:q 7 :r 12 :terrain-type :plains}\n     {:q 8 :r 12 :terrain-type :plains}\n     {:q 9 :r 12 :terrain-type :plains}\n     {:q 10 :r 12 :terrain-type :plains}\n     {:q 11 :r 12 :terrain-type :desert}\n     {:q 12 :r 12 :terrain-type :plains}\n     {:q 13 :r 12 :terrain-type :woods}\n     {:q 14 :r 12 :terrain-type :woods}\n     ;; Row 14\n     {:q 1 :r 13 :terrain-type :woods}\n     {:q 2 :r 13 :terrain-type :plains}\n     {:q 3 :r 13 :terrain-type :plains} ;; Multiplayer base location (yellow)\n     {:q 4 :r 13 :terrain-type :plains}\n     {:q 5 :r 13 :terrain-type :woods}\n     {:q 6 :r 13 :terrain-type :woods}\n     {:q 7 :r 13 :terrain-type :woods}\n     {:q 8 :r 13 :terrain-type :woods}\n     {:q 9 :r 13 :terrain-type :plains}\n     {:q 10 :r 13 :terrain-type :plains} ;; Multiplayer base location (pink)\n     {:q 11 :r 13 :terrain-type :plains}\n     {:q 12 :r 13 :terrain-type :woods}\n     ;; Row 15\n     {:q 2 :r 14 :terrain-type :woods}\n     {:q 3 :r 14 :terrain-type :plains}\n     {:q 4 :r 14 :terrain-type :plains}\n     {:q 5 :r 14 :terrain-type :woods}\n     {:q 6 :r 14 :terrain-type :woods}\n     {:q 8 :r 14 :terrain-type :woods}\n     {:q 9 :r 14 :terrain-type :woods}\n     {:q 10 :r 14 :terrain-type :plains}\n     {:q 11 :r 14 :terrain-type :plains}\n     {:q 12 :r 14 :terrain-type :woods}]}\n\n   :duel\n   {:id :duel\n    :description \"Duel\"\n    :created-by \"Chris Vincent\"\n    :notes \"There's only one way to settle this.\"\n    :terrains\n    [{:q 0, :r 0, :terrain-type :deep-water}\n     {:q 1, :r 0, :terrain-type :deep-water}\n     {:q 2, :r 0, :terrain-type :deep-water}\n     {:q 3, :r 0, :terrain-type :deep-water}\n     {:q 4, :r 0, :terrain-type :deep-water}\n     {:q 5, :r 0, :terrain-type :deep-water}\n     {:q 6, :r 0, :terrain-type :deep-water}\n     {:q 7, :r 0, :terrain-type :deep-water}\n     {:q 8, :r 0, :terrain-type :deep-water}\n     {:q 9, :r 0, :terrain-type :deep-water}\n     {:q 10, :r 0, :terrain-type :deep-water}\n     {:q 11, :r 0, :terrain-type :deep-water}\n     {:q 12, :r 0, :terrain-type :deep-water}\n     {:q 13, :r 0, :terrain-type :deep-water}\n     {:q 14, :r 0, :terrain-type :deep-water}\n     {:q 15, :r 0, :terrain-type :deep-water}\n     {:q 16, :r 0, :terrain-type :deep-water}\n     {:q 17, :r 0, :terrain-type :deep-water}\n     {:q 18, :r 0, :terrain-type :deep-water}\n     {:q 19, :r 0, :terrain-type :deep-water}\n     {:q 20, :r 0, :terrain-type :deep-water}\n     {:q 21, :r 0, :terrain-type :deep-water}\n     {:q 22, :r 0, :terrain-type :deep-water}\n     {:q 23, :r 0, :terrain-type :deep-water}\n     {:q 0, :r 1, :terrain-type :deep-water}\n     {:q 1, :r 1, :terrain-type :deep-water}\n     {:q 2, :r 1, :terrain-type :deep-water}\n     {:q 3, :r 1, :terrain-type :deep-water}\n     {:q 4, :r 1, :terrain-type :deep-water}\n     {:q 5, :r 1, :terrain-type :deep-water}\n     {:q 6, :r 1, :terrain-type :deep-water}\n     {:q 7, :r 1, :terrain-type :deep-water}\n     {:q 8, :r 1, :terrain-type :deep-water}\n     {:q 9, :r 1, :terrain-type :deep-water}\n     {:q 10, :r 1, :terrain-type :deep-water}\n     {:q 11, :r 1, :terrain-type :deep-water}\n     {:q 12, :r 1, :terrain-type :deep-water}\n     {:q 13, :r 1, :terrain-type :deep-water}\n     {:q 14, :r 1, :terrain-type :deep-water}\n     {:q 15, :r 1, :terrain-type :deep-water}\n     {:q 16, :r 1, :terrain-type :deep-water}\n     {:q 17, :r 1, :terrain-type :deep-water}\n     {:q 18, :r 1, :terrain-type :deep-water}\n     {:q 19, :r 1, :terrain-type :deep-water}\n     {:q 20, :r 1, :terrain-type :deep-water}\n     {:q 21, :r 1, :terrain-type :deep-water}\n     {:q 22, :r 1, :terrain-type :deep-water}\n     {:q 0, :r 2, :terrain-type :deep-water}\n     {:q 1, :r 2, :terrain-type :deep-water}\n     {:q 2, :r 2, :terrain-type :deep-water}\n     {:q 3, :r 2, :terrain-type :deep-water}\n     {:q 4, :r 2, :terrain-type :deep-water}\n     {:q 5, :r 2, :terrain-type :deep-water}\n     {:q 6, :r 2, :terrain-type :deep-water}\n     {:q 7, :r 2, :terrain-type :deep-water}\n     {:q 8, :r 2, :terrain-type :deep-water}\n     {:q 9, :r 2, :terrain-type :deep-water}\n     {:q 10, :r 2, :terrain-type :deep-water}\n     {:q 11, :r 2, :terrain-type :deep-water}\n     {:q 12, :r 2, :terrain-type :shallow-water}\n     {:q 13, :r 2, :terrain-type :shallow-water}\n     {:q 14, :r 2, :terrain-type :shallow-water}\n     {:q 15, :r 2, :terrain-type :shallow-water}\n     {:q 16, :r 2, :terrain-type :shallow-water}\n     {:q 17, :r 2, :terrain-type :shallow-water}\n     {:q 18, :r 2, :terrain-type :shallow-water}\n     {:q 19, :r 2, :terrain-type :deep-water}\n     {:q 20, :r 2, :terrain-type :deep-water}\n     {:q 21, :r 2, :terrain-type :deep-water}\n     {:q 22, :r 2, :terrain-type :deep-water}\n     {:q 23, :r 2, :terrain-type :deep-water}\n     {:q 0, :r 3, :terrain-type :deep-water}\n     {:q 1, :r 3, :terrain-type :deep-water}\n     {:q 2, :r 3, :terrain-type :deep-water}\n     {:q 3, :r 3, :terrain-type :deep-water}\n     {:q 4, :r 3, :terrain-type :deep-water}\n     {:q 5, :r 3, :terrain-type :deep-water}\n     {:q 6, :r 3, :terrain-type :deep-water}\n     {:q 7, :r 3, :terrain-type :deep-water}\n     {:q 8, :r 3, :terrain-type :deep-water}\n     {:q 9, :r 3, :terrain-type :deep-water}\n     {:q 10, :r 3, :terrain-type :shallow-water}\n     {:q 11, :r 3, :terrain-type :shallow-water}\n     {:q 12, :r 3, :terrain-type :plains}\n     {:q 13, :r 3, :terrain-type :plains}\n     {:q 14, :r 3, :terrain-type :plains}\n     {:q 15, :r 3, :terrain-type :plains}\n     {:q 16, :r 3, :terrain-type :ford}\n     {:q 17, :r 3, :terrain-type :shallow-water}\n     {:q 18, :r 3, :terrain-type :shallow-water}\n     {:q 19, :r 3, :terrain-type :deep-water}\n     {:q 20, :r 3, :terrain-type :deep-water}\n     {:q 21, :r 3, :terrain-type :deep-water}\n     {:q 22, :r 3, :terrain-type :deep-water}\n     {:q 0, :r 4, :terrain-type :deep-water}\n     {:q 1, :r 4, :terrain-type :deep-water}\n     {:q 2, :r 4, :terrain-type :deep-water}\n     {:q 3, :r 4, :terrain-type :deep-water}\n     {:q 4, :r 4, :terrain-type :deep-water}\n     {:q 5, :r 4, :terrain-type :shallow-water}\n     {:q 6, :r 4, :terrain-type :shallow-water}\n     {:q 7, :r 4, :terrain-type :shallow-water}\n     {:q 8, :r 4, :terrain-type :shallow-water}\n     {:q 9, :r 4, :terrain-type :shallow-water}\n     {:q 10, :r 4, :terrain-type :shallow-water}\n     {:q 11, :r 4, :terrain-type :woods}\n     {:q 12, :r 4, :terrain-type :plains}\n     {:q 13, :r 4, :terrain-type :plains}\n     {:q 14, :r 4, :terrain-type :plains}\n     {:q 15, :r 4, :terrain-type :plains}\n     {:q 16, :r 4, :terrain-type :plains}\n     {:q 17, :r 4, :terrain-type :ford}\n     {:q 18, :r 4, :terrain-type :swamp}\n     {:q 19, :r 4, :terrain-type :shallow-water}\n     {:q 20, :r 4, :terrain-type :deep-water}\n     {:q 21, :r 4, :terrain-type :deep-water}\n     {:q 22, :r 4, :terrain-type :deep-water}\n     {:q 23, :r 4, :terrain-type :deep-water}\n     {:q 0, :r 5, :terrain-type :deep-water}\n     {:q 1, :r 5, :terrain-type :deep-water}\n     {:q 2, :r 5, :terrain-type :deep-water}\n     {:q 3, :r 5, :terrain-type :deep-water}\n     {:q 4, :r 5, :terrain-type :shallow-water}\n     {:q 5, :r 5, :terrain-type :plains}\n     {:q 6, :r 5, :terrain-type :plains}\n     {:q 7, :r 5, :terrain-type :plains}\n     {:q 8, :r 5, :terrain-type :woods}\n     {:q 9, :r 5, :terrain-type :woods}\n     {:q 10, :r 5, :terrain-type :mountains}\n     {:q 11, :r 5, :terrain-type :woods}\n     {:q 12, :r 5, :terrain-type :plains}\n     {:q 13, :r 5, :terrain-type :plains}\n     {:q 14, :r 5, :terrain-type :plains}\n     {:q 15, :r 5, :terrain-type :plains}\n     {:q 16, :r 5, :terrain-type :swamp}\n     {:q 17, :r 5, :terrain-type :ford}\n     {:q 18, :r 5, :terrain-type :woods}\n     {:q 19, :r 5, :terrain-type :shallow-water}\n     {:q 20, :r 5, :terrain-type :deep-water}\n     {:q 21, :r 5, :terrain-type :deep-water}\n     {:q 22, :r 5, :terrain-type :deep-water}\n     {:q 0, :r 6, :terrain-type :deep-water}\n     {:q 1, :r 6, :terrain-type :deep-water}\n     {:q 2, :r 6, :terrain-type :deep-water}\n     {:q 3, :r 6, :terrain-type :deep-water}\n     {:q 4, :r 6, :terrain-type :shallow-water}\n     {:q 5, :r 6, :terrain-type :plains}\n     {:q 6, :r 6, :terrain-type :plains}\n     {:q 7, :r 6, :terrain-type :plains}\n     {:q 8, :r 6, :terrain-type :woods}\n     {:q 9, :r 6, :terrain-type :woods}\n     {:q 10, :r 6, :terrain-type :plains}\n     {:q 11, :r 6, :terrain-type :plains}\n     {:q 12, :r 6, :terrain-type :shallow-water}\n     {:q 13, :r 6, :terrain-type :shallow-water}\n     {:q 14, :r 6, :terrain-type :plains}\n     {:q 15, :r 6, :terrain-type :plains}\n     {:q 16, :r 6, :terrain-type :woods}\n     {:q 17, :r 6, :terrain-type :swamp}\n     {:q 18, :r 6, :terrain-type :swamp}\n     {:q 19, :r 6, :terrain-type :woods}\n     {:q 20, :r 6, :terrain-type :shallow-water}\n     {:q 21, :r 6, :terrain-type :deep-water}\n     {:q 22, :r 6, :terrain-type :deep-water}\n     {:q 23, :r 6, :terrain-type :deep-water}\n     {:q 0, :r 7, :terrain-type :deep-water}\n     {:q 1, :r 7, :terrain-type :deep-water}\n     {:q 2, :r 7, :terrain-type :deep-water}\n     {:q 3, :r 7, :terrain-type :deep-water}\n     {:q 4, :r 7, :terrain-type :deep-water}\n     {:q 5, :r 7, :terrain-type :plains}\n     {:q 6, :r 7, :terrain-type :plains}\n     {:q 7, :r 7, :terrain-type :woods}\n     {:q 8, :r 7, :terrain-type :mountains}\n     {:q 9, :r 7, :terrain-type :plains}\n     {:q 10, :r 7, :terrain-type :plains}\n     {:q 11, :r 7, :terrain-type :shallow-water}\n     {:q 12, :r 7, :terrain-type :swamp}\n     {:q 13, :r 7, :terrain-type :plains}\n     {:q 14, :r 7, :terrain-type :shallow-water}\n     {:q 15, :r 7, :terrain-type :woods}\n     {:q 16, :r 7, :terrain-type :woods}\n     {:q 17, :r 7, :terrain-type :woods}\n     {:q 18, :r 7, :terrain-type :swamp}\n     {:q 19, :r 7, :terrain-type :woods}\n     {:q 20, :r 7, :terrain-type :shallow-water}\n     {:q 21, :r 7, :terrain-type :deep-water}\n     {:q 22, :r 7, :terrain-type :deep-water}\n     {:q 0, :r 8, :terrain-type :deep-water}\n     {:q 1, :r 8, :terrain-type :deep-water}\n     {:q 2, :r 8, :terrain-type :deep-water}\n     {:q 3, :r 8, :terrain-type :deep-water}\n     {:q 4, :r 8, :terrain-type :deep-water}\n     {:q 5, :r 8, :terrain-type :mountains}\n     {:q 6, :r 8, :terrain-type :mountains}\n     {:q 7, :r 8, :terrain-type :mountains}\n     {:q 8, :r 8, :terrain-type :plains}\n     {:q 9, :r 8, :terrain-type :plains}\n     {:q 10, :r 8, :terrain-type :plains}\n     {:q 11, :r 8, :terrain-type :plains}\n     {:q 12, :r 8, :terrain-type :shallow-water}\n     {:q 13, :r 8, :terrain-type :shallow-water}\n     {:q 14, :r 8, :terrain-type :swamp}\n     {:q 15, :r 8, :terrain-type :shallow-water}\n     {:q 16, :r 8, :terrain-type :woods}\n     {:q 17, :r 8, :terrain-type :woods}\n     {:q 18, :r 8, :terrain-type :woods}\n     {:q 19, :r 8, :terrain-type :swamp}\n     {:q 20, :r 8, :terrain-type :woods}\n     {:q 21, :r 8, :terrain-type :shallow-water}\n     {:q 22, :r 8, :terrain-type :deep-water}\n     {:q 23, :r 8, :terrain-type :deep-water}\n     {:q 0, :r 9, :terrain-type :deep-water}\n     {:q 1, :r 9, :terrain-type :deep-water}\n     {:q 2, :r 9, :terrain-type :deep-water}\n     {:q 3, :r 9, :terrain-type :deep-water}\n     {:q 4, :r 9, :terrain-type :mountains}\n     {:q 5, :r 9, :terrain-type :mountains}\n     {:q 6, :r 9, :terrain-type :plains}\n     {:q 7, :r 9, :terrain-type :plains}\n     {:q 8, :r 9, :terrain-type :plains}\n     {:q 9, :r 9, :terrain-type :woods}\n     {:q 10, :r 9, :terrain-type :plains}\n     {:q 11, :r 9, :terrain-type :plains}\n     {:q 12, :r 9, :terrain-type :shallow-water}\n     {:q 13, :r 9, :terrain-type :shallow-water}\n     {:q 14, :r 9, :terrain-type :shallow-water}\n     {:q 15, :r 9, :terrain-type :mountains}\n     {:q 16, :r 9, :terrain-type :mountains}\n     {:q 17, :r 9, :terrain-type :mountains}\n     {:q 18, :r 9, :terrain-type :swamp}\n     {:q 19, :r 9, :terrain-type :woods}\n     {:q 20, :r 9, :terrain-type :shallow-water}\n     {:q 21, :r 9, :terrain-type :deep-water}\n     {:q 22, :r 9, :terrain-type :deep-water}\n     {:q 0, :r 10, :terrain-type :deep-water}\n     {:q 1, :r 10, :terrain-type :deep-water}\n     {:q 2, :r 10, :terrain-type :deep-water}\n     {:q 3, :r 10, :terrain-type :deep-water}\n     {:q 4, :r 10, :terrain-type :shallow-water}\n     {:q 5, :r 10, :terrain-type :mountains}\n     {:q 6, :r 10, :terrain-type :plains}\n     {:q 7, :r 10, :terrain-type :plains}\n     {:q 8, :r 10, :terrain-type :plains}\n     {:q 9, :r 10, :terrain-type :woods}\n     {:q 10, :r 10, :terrain-type :woods}\n     {:q 11, :r 10, :terrain-type :plains}\n     {:q 12, :r 10, :terrain-type :plains}\n     {:q 13, :r 10, :terrain-type :plains}\n     {:q 14, :r 10, :terrain-type :mountains}\n     {:q 15, :r 10, :terrain-type :mountains}\n     {:q 16, :r 10, :terrain-type :mountains}\n     {:q 17, :r 10, :terrain-type :mountains}\n     {:q 18, :r 10, :terrain-type :swamp}\n     {:q 19, :r 10, :terrain-type :woods}\n     {:q 20, :r 10, :terrain-type :shallow-water}\n     {:q 21, :r 10, :terrain-type :deep-water}\n     {:q 22, :r 10, :terrain-type :deep-water}\n     {:q 23, :r 10, :terrain-type :deep-water}\n     {:q 0, :r 11, :terrain-type :deep-water}\n     {:q 1, :r 11, :terrain-type :deep-water}\n     {:q 2, :r 11, :terrain-type :deep-water}\n     {:q 3, :r 11, :terrain-type :shallow-water}\n     {:q 4, :r 11, :terrain-type :woods}\n     {:q 5, :r 11, :terrain-type :plains}\n     {:q 6, :r 11, :terrain-type :plains}\n     {:q 7, :r 11, :terrain-type :plains}\n     {:q 8, :r 11, :terrain-type :plains}\n     {:q 9, :r 11, :terrain-type :woods}\n     {:q 10, :r 11, :terrain-type :plains}\n     {:q 11, :r 11, :terrain-type :plains}\n     {:q 12, :r 11, :terrain-type :plains}\n     {:q 13, :r 11, :terrain-type :woods}\n     {:q 14, :r 11, :terrain-type :plains}\n     {:q 15, :r 11, :terrain-type :plains}\n     {:q 16, :r 11, :terrain-type :plains}\n     {:q 17, :r 11, :terrain-type :plains}\n     {:q 18, :r 11, :terrain-type :woods}\n     {:q 19, :r 11, :terrain-type :shallow-water}\n     {:q 20, :r 11, :terrain-type :deep-water}\n     {:q 21, :r 11, :terrain-type :deep-water}\n     {:q 22, :r 11, :terrain-type :deep-water}\n     {:q 0, :r 12, :terrain-type :deep-water}\n     {:q 1, :r 12, :terrain-type :deep-water}\n     {:q 2, :r 12, :terrain-type :deep-water}\n     {:q 3, :r 12, :terrain-type :shallow-water}\n     {:q 4, :r 12, :terrain-type :woods}\n     {:q 5, :r 12, :terrain-type :swamp}\n     {:q 6, :r 12, :terrain-type :mountains}\n     {:q 7, :r 12, :terrain-type :mountains}\n     {:q 8, :r 12, :terrain-type :mountains}\n     {:q 9, :r 12, :terrain-type :mountains}\n     {:q 10, :r 12, :terrain-type :plains}\n     {:q 11, :r 12, :terrain-type :plains}\n     {:q 12, :r 12, :terrain-type :plains}\n     {:q 13, :r 12, :terrain-type :woods}\n     {:q 14, :r 12, :terrain-type :plains}\n     {:q 15, :r 12, :terrain-type :plains}\n     {:q 16, :r 12, :terrain-type :plains}\n     {:q 17, :r 12, :terrain-type :plains}\n     {:q 18, :r 12, :terrain-type :mountains}\n     {:q 19, :r 12, :terrain-type :shallow-water}\n     {:q 20, :r 12, :terrain-type :deep-water}\n     {:q 21, :r 12, :terrain-type :deep-water}\n     {:q 22, :r 12, :terrain-type :deep-water}\n     {:q 23, :r 12, :terrain-type :deep-water}\n     {:q 0, :r 13, :terrain-type :deep-water}\n     {:q 1, :r 13, :terrain-type :deep-water}\n     {:q 2, :r 13, :terrain-type :shallow-water}\n     {:q 3, :r 13, :terrain-type :woods}\n     {:q 4, :r 13, :terrain-type :swamp}\n     {:q 5, :r 13, :terrain-type :mountains}\n     {:q 6, :r 13, :terrain-type :mountains}\n     {:q 7, :r 13, :terrain-type :mountains}\n     {:q 8, :r 13, :terrain-type :shallow-water}\n     {:q 9, :r 13, :terrain-type :shallow-water}\n     {:q 10, :r 13, :terrain-type :shallow-water}\n     {:q 11, :r 13, :terrain-type :plains}\n     {:q 12, :r 13, :terrain-type :plains}\n     {:q 13, :r 13, :terrain-type :woods}\n     {:q 14, :r 13, :terrain-type :plains}\n     {:q 15, :r 13, :terrain-type :plains}\n     {:q 16, :r 13, :terrain-type :plains}\n     {:q 17, :r 13, :terrain-type :mountains}\n     {:q 18, :r 13, :terrain-type :mountains}\n     {:q 19, :r 13, :terrain-type :deep-water}\n     {:q 20, :r 13, :terrain-type :deep-water}\n     {:q 21, :r 13, :terrain-type :deep-water}\n     {:q 22, :r 13, :terrain-type :deep-water}\n     {:q 0, :r 14, :terrain-type :deep-water}\n     {:q 1, :r 14, :terrain-type :deep-water}\n     {:q 2, :r 14, :terrain-type :shallow-water}\n     {:q 3, :r 14, :terrain-type :woods}\n     {:q 4, :r 14, :terrain-type :swamp}\n     {:q 5, :r 14, :terrain-type :woods}\n     {:q 6, :r 14, :terrain-type :woods}\n     {:q 7, :r 14, :terrain-type :woods}\n     {:q 8, :r 14, :terrain-type :shallow-water}\n     {:q 9, :r 14, :terrain-type :swamp}\n     {:q 10, :r 14, :terrain-type :shallow-water}\n     {:q 11, :r 14, :terrain-type :shallow-water}\n     {:q 12, :r 14, :terrain-type :plains}\n     {:q 13, :r 14, :terrain-type :plains}\n     {:q 14, :r 14, :terrain-type :plains}\n     {:q 15, :r 14, :terrain-type :plains}\n     {:q 16, :r 14, :terrain-type :mountains}\n     {:q 17, :r 14, :terrain-type :mountains}\n     {:q 18, :r 14, :terrain-type :mountains}\n     {:q 19, :r 14, :terrain-type :deep-water}\n     {:q 20, :r 14, :terrain-type :deep-water}\n     {:q 21, :r 14, :terrain-type :deep-water}\n     {:q 22, :r 14, :terrain-type :deep-water}\n     {:q 23, :r 14, :terrain-type :deep-water}\n     {:q 0, :r 15, :terrain-type :deep-water}\n     {:q 1, :r 15, :terrain-type :deep-water}\n     {:q 2, :r 15, :terrain-type :shallow-water}\n     {:q 3, :r 15, :terrain-type :woods}\n     {:q 4, :r 15, :terrain-type :swamp}\n     {:q 5, :r 15, :terrain-type :woods}\n     {:q 6, :r 15, :terrain-type :woods}\n     {:q 7, :r 15, :terrain-type :woods}\n     {:q 8, :r 15, :terrain-type :shallow-water}\n     {:q 9, :r 15, :terrain-type :plains}\n     {:q 10, :r 15, :terrain-type :swamp}\n     {:q 11, :r 15, :terrain-type :shallow-water}\n     {:q 12, :r 15, :terrain-type :plains}\n     {:q 13, :r 15, :terrain-type :plains}\n     {:q 14, :r 15, :terrain-type :mountains}\n     {:q 15, :r 15, :terrain-type :woods}\n     {:q 16, :r 15, :terrain-type :plains}\n     {:q 17, :r 15, :terrain-type :plains}\n     {:q 18, :r 15, :terrain-type :plains}\n     {:q 19, :r 15, :terrain-type :deep-water}\n     {:q 20, :r 15, :terrain-type :deep-water}\n     {:q 21, :r 15, :terrain-type :deep-water}\n     {:q 22, :r 15, :terrain-type :deep-water}\n     {:q 0, :r 16, :terrain-type :deep-water}\n     {:q 1, :r 16, :terrain-type :deep-water}\n     {:q 2, :r 16, :terrain-type :deep-water}\n     {:q 3, :r 16, :terrain-type :shallow-water}\n     {:q 4, :r 16, :terrain-type :woods}\n     {:q 5, :r 16, :terrain-type :swamp}\n     {:q 6, :r 16, :terrain-type :swamp}\n     {:q 7, :r 16, :terrain-type :woods}\n     {:q 8, :r 16, :terrain-type :plains}\n     {:q 9, :r 16, :terrain-type :plains}\n     {:q 10, :r 16, :terrain-type :shallow-water}\n     {:q 11, :r 16, :terrain-type :shallow-water}\n     {:q 12, :r 16, :terrain-type :plains}\n     {:q 13, :r 16, :terrain-type :plains}\n     {:q 14, :r 16, :terrain-type :woods}\n     {:q 15, :r 16, :terrain-type :woods}\n     {:q 16, :r 16, :terrain-type :plains}\n     {:q 17, :r 16, :terrain-type :plains}\n     {:q 18, :r 16, :terrain-type :plains}\n     {:q 19, :r 16, :terrain-type :shallow-water}\n     {:q 20, :r 16, :terrain-type :deep-water}\n     {:q 21, :r 16, :terrain-type :deep-water}\n     {:q 22, :r 16, :terrain-type :deep-water}\n     {:q 23, :r 16, :terrain-type :deep-water}\n     {:q 0, :r 17, :terrain-type :deep-water}\n     {:q 1, :r 17, :terrain-type :deep-water}\n     {:q 2, :r 17, :terrain-type :deep-water}\n     {:q 3, :r 17, :terrain-type :shallow-water}\n     {:q 4, :r 17, :terrain-type :woods}\n     {:q 5, :r 17, :terrain-type :ford}\n     {:q 6, :r 17, :terrain-type :swamp}\n     {:q 7, :r 17, :terrain-type :plains}\n     {:q 8, :r 17, :terrain-type :plains}\n     {:q 9, :r 17, :terrain-type :plains}\n     {:q 10, :r 17, :terrain-type :plains}\n     {:q 11, :r 17, :terrain-type :woods}\n     {:q 12, :r 17, :terrain-type :mountains}\n     {:q 13, :r 17, :terrain-type :woods}\n     {:q 14, :r 17, :terrain-type :woods}\n     {:q 15, :r 17, :terrain-type :plains}\n     {:q 16, :r 17, :terrain-type :plains}\n     {:q 17, :r 17, :terrain-type :plains}\n     {:q 18, :r 17, :terrain-type :shallow-water}\n     {:q 19, :r 17, :terrain-type :deep-water}\n     {:q 20, :r 17, :terrain-type :deep-water}\n     {:q 21, :r 17, :terrain-type :deep-water}\n     {:q 22, :r 17, :terrain-type :deep-water}\n     {:q 0, :r 18, :terrain-type :deep-water}\n     {:q 1, :r 18, :terrain-type :deep-water}\n     {:q 2, :r 18, :terrain-type :deep-water}\n     {:q 3, :r 18, :terrain-type :deep-water}\n     {:q 4, :r 18, :terrain-type :shallow-water}\n     {:q 5, :r 18, :terrain-type :swamp}\n     {:q 6, :r 18, :terrain-type :ford}\n     {:q 7, :r 18, :terrain-type :plains}\n     {:q 8, :r 18, :terrain-type :plains}\n     {:q 9, :r 18, :terrain-type :plains}\n     {:q 10, :r 18, :terrain-type :plains}\n     {:q 11, :r 18, :terrain-type :plains}\n     {:q 12, :r 18, :terrain-type :woods}\n     {:q 13, :r 18, :terrain-type :shallow-water}\n     {:q 14, :r 18, :terrain-type :shallow-water}\n     {:q 15, :r 18, :terrain-type :shallow-water}\n     {:q 16, :r 18, :terrain-type :shallow-water}\n     {:q 17, :r 18, :terrain-type :shallow-water}\n     {:q 18, :r 18, :terrain-type :shallow-water}\n     {:q 19, :r 18, :terrain-type :deep-water}\n     {:q 20, :r 18, :terrain-type :deep-water}\n     {:q 21, :r 18, :terrain-type :deep-water}\n     {:q 22, :r 18, :terrain-type :deep-water}\n     {:q 23, :r 18, :terrain-type :deep-water}\n     {:q 0, :r 19, :terrain-type :deep-water}\n     {:q 1, :r 19, :terrain-type :deep-water}\n     {:q 2, :r 19, :terrain-type :deep-water}\n     {:q 3, :r 19, :terrain-type :deep-water}\n     {:q 4, :r 19, :terrain-type :shallow-water}\n     {:q 5, :r 19, :terrain-type :shallow-water}\n     {:q 6, :r 19, :terrain-type :ford}\n     {:q 7, :r 19, :terrain-type :plains}\n     {:q 8, :r 19, :terrain-type :plains}\n     {:q 9, :r 19, :terrain-type :plains}\n     {:q 10, :r 19, :terrain-type :plains}\n     {:q 11, :r 19, :terrain-type :shallow-water}\n     {:q 12, :r 19, :terrain-type :shallow-water}\n     {:q 13, :r 19, :terrain-type :deep-water}\n     {:q 14, :r 19, :terrain-type :deep-water}\n     {:q 15, :r 19, :terrain-type :deep-water}\n     {:q 16, :r 19, :terrain-type :deep-water}\n     {:q 17, :r 19, :terrain-type :deep-water}\n     {:q 18, :r 19, :terrain-type :deep-water}\n     {:q 19, :r 19, :terrain-type :deep-water}\n     {:q 20, :r 19, :terrain-type :deep-water}\n     {:q 21, :r 19, :terrain-type :deep-water}\n     {:q 22, :r 19, :terrain-type :deep-water}\n     {:q 0, :r 20, :terrain-type :deep-water}\n     {:q 1, :r 20, :terrain-type :deep-water}\n     {:q 2, :r 20, :terrain-type :deep-water}\n     {:q 3, :r 20, :terrain-type :deep-water}\n     {:q 4, :r 20, :terrain-type :deep-water}\n     {:q 5, :r 20, :terrain-type :shallow-water}\n     {:q 6, :r 20, :terrain-type :shallow-water}\n     {:q 7, :r 20, :terrain-type :shallow-water}\n     {:q 8, :r 20, :terrain-type :shallow-water}\n     {:q 9, :r 20, :terrain-type :shallow-water}\n     {:q 10, :r 20, :terrain-type :shallow-water}\n     {:q 11, :r 20, :terrain-type :shallow-water}\n     {:q 12, :r 20, :terrain-type :deep-water}\n     {:q 13, :r 20, :terrain-type :deep-water}\n     {:q 14, :r 20, :terrain-type :deep-water}\n     {:q 15, :r 20, :terrain-type :deep-water}\n     {:q 16, :r 20, :terrain-type :deep-water}\n     {:q 17, :r 20, :terrain-type :deep-water}\n     {:q 18, :r 20, :terrain-type :deep-water}\n     {:q 19, :r 20, :terrain-type :deep-water}\n     {:q 20, :r 20, :terrain-type :deep-water}\n     {:q 21, :r 20, :terrain-type :deep-water}\n     {:q 22, :r 20, :terrain-type :deep-water}\n     {:q 23, :r 20, :terrain-type :deep-water}\n     {:q 0, :r 21, :terrain-type :deep-water}\n     {:q 1, :r 21, :terrain-type :deep-water}\n     {:q 2, :r 21, :terrain-type :deep-water}\n     {:q 3, :r 21, :terrain-type :deep-water}\n     {:q 4, :r 21, :terrain-type :deep-water}\n     {:q 5, :r 21, :terrain-type :deep-water}\n     {:q 6, :r 21, :terrain-type :deep-water}\n     {:q 7, :r 21, :terrain-type :deep-water}\n     {:q 8, :r 21, :terrain-type :deep-water}\n     {:q 9, :r 21, :terrain-type :deep-water}\n     {:q 10, :r 21, :terrain-type :deep-water}\n     {:q 11, :r 21, :terrain-type :deep-water}\n     {:q 12, :r 21, :terrain-type :deep-water}\n     {:q 13, :r 21, :terrain-type :deep-water}\n     {:q 14, :r 21, :terrain-type :deep-water}\n     {:q 15, :r 21, :terrain-type :deep-water}\n     {:q 16, :r 21, :terrain-type :deep-water}\n     {:q 17, :r 21, :terrain-type :deep-water}\n     {:q 18, :r 21, :terrain-type :deep-water}\n     {:q 19, :r 21, :terrain-type :deep-water}\n     {:q 20, :r 21, :terrain-type :deep-water}\n     {:q 21, :r 21, :terrain-type :deep-water}\n     {:q 22, :r 21, :terrain-type :deep-water}\n     {:q 0, :r 22, :terrain-type :deep-water}\n     {:q 1, :r 22, :terrain-type :deep-water}\n     {:q 2, :r 22, :terrain-type :deep-water}\n     {:q 3, :r 22, :terrain-type :deep-water}\n     {:q 4, :r 22, :terrain-type :deep-water}\n     {:q 5, :r 22, :terrain-type :deep-water}\n     {:q 6, :r 22, :terrain-type :deep-water}\n     {:q 7, :r 22, :terrain-type :deep-water}\n     {:q 8, :r 22, :terrain-type :deep-water}\n     {:q 9, :r 22, :terrain-type :deep-water}\n     {:q 10, :r 22, :terrain-type :deep-water}\n     {:q 11, :r 22, :terrain-type :deep-water}\n     {:q 12, :r 22, :terrain-type :deep-water}\n     {:q 13, :r 22, :terrain-type :deep-water}\n     {:q 14, :r 22, :terrain-type :deep-water}\n     {:q 15, :r 22, :terrain-type :deep-water}\n     {:q 16, :r 22, :terrain-type :deep-water}\n     {:q 17, :r 22, :terrain-type :deep-water}\n     {:q 18, :r 22, :terrain-type :deep-water}\n     {:q 19, :r 22, :terrain-type :deep-water}\n     {:q 20, :r 22, :terrain-type :deep-water}\n     {:q 21, :r 22, :terrain-type :deep-water}\n     {:q 22, :r 22, :terrain-type :deep-water}\n     {:q 23, :r 22, :terrain-type :deep-water}]}})\n\n;; TODO: add support for :allowed-unit-types\n(def scenarios\n  {:sterlings-aruba-multiplayer\n   {:id :sterlings-aruba-multiplayer\n    :description \"Sterling's Aruba Multiplayer\"\n    :created-by \"Weewar\"\n    :ruleset-id :zetawar\n    :map-id :sterlings-aruba\n    :max-count-per-unit 10\n    :credits-per-base 100\n    :bases\n    [{:q 1 :r 2 :base-type :base}\n     {:q 1 :r 4 :base-type :seaport}\n     {:q 2 :r 1 :base-type :base}\n     {:q 2 :r 4 :base-type :airfield}\n     {:q 5 :r 7 :base-type :base}\n     {:q 5 :r 8 :base-type :seaport}\n     {:q 6 :r 5 :base-type :airfield}\n     {:q 7 :r 6 :base-type :base}]\n    :factions\n    [{:color :blue\n      :credits 300\n      :ai false\n      :bases [{:q 1 :r 2}]\n      :units [{:q 2 :r 2 :unit-type :infantry}]}\n     {:color :red\n      :credits 300\n      :ai true\n      :bases [{:q 7 :r 6}]\n      :units [{:q 7 :r 7 :unit-type :infantry}\n              {:q 7 :r 8 :unit-type :infantry}]}]}\n\n   :sterlings-aruba-ai-vs-ai\n   {:id :sterlings-aruba-ai-vs-ai\n    :description \"Sterling's Aruba AI vs AI\"\n    :created-by \"Weewar\"\n    :ruleset-id :zetawar\n    :map-id :sterlings-aruba\n    :max-count-per-unit 10\n    :credits-per-base 100\n    :bases\n    [{:q 1 :r 2 :base-type :base}\n     {:q 1 :r 4 :base-type :seaport}\n     {:q 2 :r 1 :base-type :base}\n     {:q 2 :r 4 :base-type :airfield}\n     {:q 5 :r 7 :base-type :base}\n     {:q 5 :r 8 :base-type :seaport}\n     {:q 6 :r 5 :base-type :airfield}\n     {:q 7 :r 6 :base-type :base}]\n    :factions\n    [{:color :blue\n      :credits 300\n      :ai true\n      :bases [{:q 1 :r 2}]\n      :units [{:q 2 :r 2 :unit-type :infantry}]}\n     {:color :red\n      :credits 300\n      :ai true\n      :bases [{:q 7 :r 6}]\n      :units [{:q 7 :r 7 :unit-type :infantry}\n              {:q 7 :r 8 :unit-type :infantry}]}]}\n\n   :city-sprawl-multiplayer\n   {:id :city-sprawl-multiplayer\n    :description \"City Sprawl Multiplayer\"\n    :created-by \"Weewar\"\n    :ruleset-id :zetawar\n    :map-id :city-sprawl\n    :max-count-per-unit 10\n    :credits-per-base 50\n    :bases\n    [{:q 4  :r 2 :base-type :base}\n     {:q 12 :r 2 :base-type :base}\n     {:q 3  :r 3 :base-type :base}\n     {:q 5  :r 3 :base-type :base}\n     {:q 10 :r 3 :base-type :base}\n     {:q 2  :r 4 :base-type :base}\n     {:q 4  :r 4 :base-type :base}\n     {:q 12 :r 4 :base-type :base}\n     {:q 7  :r 5 :base-type :base}\n     {:q 11 :r 5 :base-type :base}\n     {:q 13 :r 5 :base-type :base}\n     {:q 5  :r 7 :base-type :base}\n     {:q 7  :r 7 :base-type :base}\n     {:q 9  :r 7 :base-type :base}\n     {:q 7  :r 8 :base-type :base}\n     {:q 12 :r 9 :base-type :base}\n     {:q 2  :r 10 :base-type :base}\n     {:q 7  :r 10 :base-type :base}\n     {:q 11 :r 10 :base-type :base}\n     {:q 2  :r 11 :base-type :base}\n     {:q 3  :r 11 :base-type :base}\n     {:q 4  :r 11 :base-type :base}\n     {:q 9  :r 11 :base-type :base}\n     {:q 11 :r 11 :base-type :base}\n     {:q 3  :r 13 :base-type :base}\n     {:q 10 :r 13 :base-type :base}]\n    ;; TODO: determine best faction order\n    :factions\n    [{:color :red\n      :credits 300\n      :ai false\n      :bases [{:q 3  :r 3}\n              {:q 2  :r 4}]\n      :units [{:q 3 :r 2 :unit-type :infantry}\n              {:q 2 :r 3 :unit-type :infantry}]}\n     {:color :blue\n      :credits 300\n      :ai true\n      :bases [{:q 12 :r 4}\n              {:q 13 :r 5}]\n      :units [{:q 12 :r 3 :unit-type :infantry}\n              {:q 13 :r 4 :unit-type :infantry}]}\n     {:color :yellow\n      :credits 300\n      :ai true\n      :bases [{:q 3 :r 11}\n              {:q 3 :r 13}]\n      :units [{:q 2 :r 12 :unit-type :infantry}\n              {:q 3 :r 12 :unit-type :infantry}]}\n     {:color :pink\n      :credits 300\n      :ai true\n      :bases [{:q 11 :r 11}\n              {:q 10 :r 13}]\n      :units [{:q 10 :r 11 :unit-type :infantry}\n              {:q 11 :r 12 :unit-type :infantry}]}]}\n\n   :duel-multiplayer\n   {:id :duel-multiplayer\n    :description \"Duel Multiplayer\"\n    :created-by \"Chris Vincent\"\n    :notes \"There's only one way to settle this.\"\n    :ruleset-id :zetawar\n    :map-id :duel\n    :max-count-per-unit 10\n    :credits-per-base 100\n    :bases\n    [{:q 4, :r 7, :base-type :seaport}\n     {:q 5, :r 6, :base-type :base}\n     {:q 5, :r 19, :base-type :base}\n     {:q 6, :r 5, :base-type :base}\n     {:q 8, :r 18, :base-type :airfield}\n     {:q 9, :r 10, :base-type :base}\n     {:q 10, :r 14, :base-type :base}\n     {:q 10, :r 18, :base-type :base}\n     {:q 13, :r 4, :base-type :base}\n     {:q 13, :r 8, :base-type :base}\n     {:q 14, :r 12, :base-type :base}\n     {:q 15, :r 4, :base-type :airfield}\n     {:q 16, :r 17, :base-type :base}\n     {:q 17, :r 3, :base-type :base}\n     {:q 18, :r 15, :base-type :seaport}\n     {:q 18, :r 16, :base-type :base}]\n    :factions\n    [{:color :red\n      :credits 300\n      :ai false\n      :bases [{:q 4, :r 7}\n              {:q 5, :r 6}\n              {:q 6, :r 5}]\n      :units [{:q 4, :r 8, :unit-type :cruiser}\n              {:q 5, :r 5, :unit-type :infantry}]}\n     {:color :blue\n      :credits 300\n      :ai true\n      :bases [{:q 16, :r 17}\n              {:q 18, :r 15}\n              {:q 18, :r 16}]\n      :units [{:q 17, :r 17, :unit-type :infantry}\n              {:q 19, :r 14, :unit-type :cruiser}]}]}})\n"
  },
  {
    "path": "src/cljs/zetawar/db.cljs",
    "content": "(ns zetawar.db\n  (:require\n   [datascript.core :as d]\n   [zetawar.logging :as log]\n   [zetawar.util :refer [breakpoint inspect solo ssolo]]))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Schema\n\n;; TODO: add base-type (for shipyards, airfields, etc.)\n(def schema\n  {;; App\n   :app/game                        {:db/valueType   :db.type/ref}\n   :app/configuring-faction         {:db/valueType   :db.type/ref}\n\n   ;; Game\n   :game/id                         {:db/unique      :db.unique/identity}\n   :game/terrain-types              {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :game/unit-types                 {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :game/unit-state-maps            {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :game/unit-states                {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :game/map                        {:db/valueType   :db.type/ref\n                                     :db/isComponent true}\n   :game/factions                   {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :game/starting-faction           {:db/valueType   :db.type/ref}\n   :game/current-faction            {:db/valueType   :db.type/ref}\n\n   ;; Faction\n   :faction/next-faction            {:db/valueType   :db.type/ref}\n   :faction/units                   {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true\n                                     :db/index       true}\n\n   ;; Unit\n   :unit/game-pos-idx               {:db/unique      :db.unique/identity}\n   :unit/type                       {:db/valueType   :db.type/ref}\n   :unit/attacked-from              {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many}\n   :unit/state                      {:db/valueType   :db.type/ref}\n   :unit/terrain                    {:db/valueType   :db.type/ref}\n\n   ;; Unit Type\n   :unit-type/game-id-idx           {:db/unique      :db.unique/identity}\n   :unit-type/state-map             {:db/valueType   :db.type/ref}\n   :unit-type/zoc-armor-types       {:db/cardinality :db.cardinality/many}\n   :unit-type/can-repair            {:db/cardinality :db.cardinality/many}\n   :unit-type/strengths             {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n\n   ;; Unit State Maps\n   :unit-state-map/game-id-idx      {:db/unique      :db.unique/identity}\n   :unit-state-map/states           {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :unit-state-map/built-state      {:db/valueType   :db.type/ref}\n   :unit-state-map/start-state      {:db/valueType   :db.type/ref}\n\n   ;; Unit State\n   :unit-state/game-id-idx          {:db/unique      :db.unique/identity}\n   :unit-state/transitions          {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n\n   ;; Unit State Transitions\n   :unit-state-transition/new-state {:db/valueType   :db.type/ref}\n\n   ;; Unit State\n   ;; - unit-state-map\n   ;;   - unit-state-map/id\n   ;;   - unit-state-map/states\n   ;;   - unit-state-map/just-built-state\n   ;;   - unit-state-map/start-turn-state\n   ;; - unit-state\n   ;;   - unit-state/name\n   ;;   - unit-state/transititons\n   ;; - unit-state-transition\n   ;;   - unit-state-transition/action-type (-id?)\n   ;;   - unit-state-transition/new-state\n\n   ;; Map\n   :map/terrains                    {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n\n   ;; Terrain\n   :terrain/game-pos-idx            {:db/unique      :db.unique/identity}\n   :terrain/owner                   {:db/valueType   :db.type/ref}\n   :terrain/type                    {:db/valueType   :db.type/ref}\n\n   ;; Terrain type\n   :terrain-type/game-id-idx        {:db/unique      :db.unique/identity}\n   :terrain-type/effects            {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many\n                                     :db/isComponent true}\n   :terrain-type/can-build          {:db/valueType   :db.type/ref\n                                     :db/cardinality :db.cardinality/many}\n\n   ;; Terrain effects\n   :terrain-effect/unit-type        {:db/valueType   :db.type/ref\n                                     :db/index       true}\n   })\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Utils\n\n(def prev-temp-id (atom -1000000))\n\n(defn next-temp-id []\n  (swap! prev-temp-id dec))\n\n(defn qe\n  \"Returns the single entity returned by a query.\"\n  [query db & args]\n  (when-let [result (-> (apply d/q query db args) ssolo)]\n    (d/entity db result)))\n\n(defn find-by\n  \"Returns the unique entity identified by either attr (for singleton\n  entities) or attr and val.\"\n  ([db attr]\n   (qe '[:find ?e\n         :in $ ?attr\n         :where [?e ?attr]]\n       db attr))\n  ([db attr val]\n   (qe '[:find ?e\n         :in $ ?attr ?val\n         :where [?e ?attr ?val]]\n       db attr val)))\n\n(defn qes\n  \"Returns the entities returned by a query, assuming that\n  all :find results are entity ids.\"\n  [query db & args]\n  (->> (apply d/q query db args)\n       (mapv (fn [items]\n               (mapv (partial d/entity db) items)))))\n\n(defn qess\n  \"Return a sequence of entities returned by a query, assuming\n  that each :find result contains a single entity id.\"\n  [query db & args]\n  (->> (apply d/q query db args)\n       (mapv #(d/entity db (solo %)))))\n\n(defn find-all-by\n  \"Returns all entities possessing attr.\"\n  [db attr]\n  (qess '[:find ?e\n          :in $ ?attr\n          :where [?e ?attr]]\n        db attr))\n\n(defprotocol Eid\n  \"A protocol for retrieving an object's entity id.\"\n  (e [_] \"identifying id for a value\"))\n\n(extend-protocol Eid\n  number\n  (e [n] n)\n\n  cljs.core.PersistentHashMap\n  (e [ent] (:db/id ent))\n\n  cljs.core.PersistentArrayMap\n  (e [ent] (:db/id ent))\n\n  datascript.impl.entity.Entity\n  (e [ent] (:db/id ent)))\n"
  },
  {
    "path": "src/cljs/zetawar/dev.cljs",
    "content": "(ns zetawar.dev\n  (:require\n   [clojure.spec.test :as spec.test]\n   [devtools.core :as devtools]\n   [zetawar.game-test]\n   [zetawar.subs-test]\n   [zetawar.devcards]))\n\n(enable-console-print!)\n\n(devtools/install!)\n\n(spec.test/instrument)\n"
  },
  {
    "path": "src/cljs/zetawar/devcards/data_formats.cljs",
    "content": "(ns zetawar.devcards.data-formats\n  (:require\n   [clojure.spec :as s]\n   [devcards.core :as dc :include-macros true]\n   [integrant.core :as ig]\n   [reagent.core :as r]\n   [zetawar.app :as app]\n   [zetawar.data :as data]\n   [zetawar.doc :as doc]\n   [zetawar.subs :as subs]\n   [zetawar.system :as system]\n   [zetawar.util :refer [breakpoint inspect]]\n   [zetawar.views :as views])\n  (:require-macros\n   [devcards.core :refer [defcard defcard-rg]]))\n\n(def maps\n  {:simple-map\n   {:id :simple-map\n    :description \"Simple Map\"\n    :terrains\n    [;; Row 1\n     {:q 0\n      :r 0\n      :terrain-type :plains}\n     {:q 1\n      :r 0\n      :terrain-type :plains}\n     {:q 2\n      :r 0\n      :terrain-type :plains}]}})\n\n(def scenarios\n  {:simple-scenario\n   {:id :simple-scenario\n    :description \"Simple Scenario\"\n    :ruleset-id :zetawar\n    :map-id :simple-map\n    :max-count-per-unit 10\n    :credits-per-base 100\n    :bases\n    [{:q 0 :r 0}\n     {:q 2 :r 0}]\n    :factions\n    [{:color :blue\n      :credits 100\n      :ai false\n      :bases [{:q 0 :r 0}]\n      :units [{:q 0\n               :r 0\n               :unit-type :infantry}]}\n     {:color :red\n      :credits 100\n      :ai true\n      :bases [{:q 2 :r 0}]\n      :units [{:q 2\n               :r 0\n               :unit-type :infantry}]}]}})\n\n(defcard map-data-example\n  (let [{:keys [simple-map]} maps]\n    (if (s/valid? :game/map simple-map)\n      simple-map\n      (s/explain :game/map simple-map))))\n\n(defcard-rg map-attribute-descriptions\n  [:div\n   [:h3 \"Map attributes\"]\n   [:table.table\n    [:thead\n     [:tr\n      [:th.col-sm-2 \"Key\"]\n      [:th.col-sm-3 \"Description\"]\n      [:th.col-sm-5 \"Spec\"]]]\n    (let [ks [:game.map/description\n              :game.map/terrains]]\n      (into [:tbody]\n            (for [k ks]\n              [:tr\n               [:td.col-sm-2 (name k)]\n               [:td.col-sm-3 (doc/key-descriptions k)]\n               [:td.col-sm-5 (pr-str (s/describe k))]])))]\n   [:h3 \"Terrain attributes\"]\n   [:table.table\n    [:thead\n     [:tr\n      [:th.col-sm-2 \"Key\"]\n      [:th.col-sm-3 \"Description\"]\n      [:th.col-sm-5 \"Spec\"]]]\n    (let [ks [:game.map.terrain/q\n              :game.map.terrain/r\n              :game.map.terrain/terrain-type]]\n      (into [:tbody]\n            (for [k ks]\n              [:tr\n               [:td.col-sm-2 (name k)]\n               [:td.col-sm-3 (doc/key-descriptions k)]\n               [:td.col-sm-5 (pr-str (s/describe k))]])))]])\n\n(defcard-rg map-and-scenario-example\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)\n        views-cfg (:zetawar.system/game-views system)\n        conn (:conn views-cfg)]\n    (app/start-new-game! game-cfg\n                         data/rulesets\n                         maps\n                         scenarios\n                         :simple-scenario)\n    [views/board views-cfg]))\n\n(defcard simple-scenario-example-data\n  (:simple-scenario scenarios))\n"
  },
  {
    "path": "src/cljs/zetawar/devcards/game_specs.cljs",
    "content": "(ns zetawar.devcards.game-specs\n  (:require\n   [clojure.spec :as s]\n   [clojure.test.check.generators]\n   [devcards.core :as dc :include-macros true]\n   [zetawar.game.spec :as game.spec])\n  (:require-macros\n   [devcards.core :refer [defcard defcard-rg]]))\n\n(defcard game-map-spec\n  (first\n   (s/exercise :game/map 1)))\n"
  },
  {
    "path": "src/cljs/zetawar/devcards/maps_and_scenarios.cljs",
    "content": "(ns zetawar.devcards.maps-and-scenarios\n  (:require\n   [devcards.core :as dc :include-macros true]\n   [integrant.core :as ig]\n   [reagent.core :as r]\n   [zetawar.app :as app]\n   [zetawar.system :as system]\n   [zetawar.views :as views])\n  (:require-macros\n   [devcards.core :refer [defcard defcard-rg]]))\n\n(defn init-scenario [scenario-id]\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)]\n    (app/start-new-game! game-cfg scenario-id)\n    system))\n\n(defcard-rg sterlings-aruba-multiplayer-card\n  (let [views-cfg (-> (init-scenario :sterlings-aruba-multiplayer)\n                      :zetawar.system/game-views)]\n    [views/board views-cfg]))\n\n(defcard-rg city-sprawl-multiplayer-card\n  (let [views-cfg (-> (init-scenario :city-sprawl-multiplayer)\n                      :zetawar.system/game-views)]\n    [views/board views-cfg]))\n"
  },
  {
    "path": "src/cljs/zetawar/devcards/selection_and_target.cljs",
    "content": "(ns zetawar.devcards.selection-and-target\n  (:require\n   [datascript.core :as d]\n   [devcards.core :as dc :include-macros true]\n   [integrant.core :as ig]\n   [reagent.core :as r]\n   [zetawar.app :as app]\n   [zetawar.data :as data]\n   [zetawar.db :refer [e]]\n   [zetawar.game :as game]\n   [zetawar.subs :as subs]\n   [zetawar.system :as system]\n   [zetawar.util :refer [breakpoint inspect]]\n   [zetawar.views :as views])\n  (:require-macros\n   [devcards.core :refer [defcard defcard-rg]]))\n\n(defcard-rg unit-selected\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)\n        views-cfg (:zetawar.system/game-views system)\n        conn (:conn views-cfg)]\n    (app/start-new-game! game-cfg :sterlings-aruba-multiplayer)\n    (d/transact! conn [{:db/id (-> @conn app/root e)\n                        :app/selected-q 2\n                        :app/selected-r 2}])\n    [views/game-interface views-cfg]))\n\n(defcard-rg moved-unit-selected\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)\n        views-cfg (:zetawar.system/game-views system)\n        conn (:conn views-cfg)]\n    (app/start-new-game! game-cfg :sterlings-aruba-multiplayer)\n    (let [db @conn\n          game (app/current-game db)\n          unit (game/unit-at db game 2 2)\n          done-state (game/unit-state-by-id db game :unit-state.id/move-attack_moved)]\n      (d/transact! conn [{:db/id (-> db app/root e)\n                          :app/selected-q 2\n                          :app/selected-r 2}\n                         {:db/id (e unit)\n                          :unit/move-count 1\n                          :unit/state (e done-state)}]))\n    [views/game-interface views-cfg]))\n\n(defcard-rg moved-unit-with-attacks\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)\n        views-cfg (:zetawar.system/game-views system)\n        conn (:conn views-cfg)]\n    (app/start-new-game! game-cfg :sterlings-aruba-multiplayer)\n    (let [db @conn\n          game (app/current-game db)\n          unit (game/unit-at db game 2 2)\n          done-state (game/unit-state-by-id db game :unit-state.id/move-attack_moved)]\n      (d/transact! conn (into (game/teleport-tx db game 2 2 6 8)\n                              [{:db/id (-> db app/root e)\n                                :app/selected-q 6\n                                :app/selected-r 8}\n                               {:db/id (e unit)\n                                :unit/move-count 1\n                                :unit/state (e done-state)}])))\n    [views/game-interface views-cfg]))\n\n(defcard-rg targeted-enemy\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)\n        views-cfg (:zetawar.system/game-views system)\n        conn (:conn views-cfg)]\n    (app/start-new-game! game-cfg :sterlings-aruba-multiplayer)\n    (let [game (app/current-game @conn)\n          unit (game/unit-at @conn game 2 2)]\n      (d/transact! conn (into (game/teleport-tx @conn game 2 2 6 8)\n                              [{:db/id (-> @conn app/root e)\n                                :app/selected-q 6\n                                :app/selected-r 8\n                                :app/targeted-q 7\n                                :app/targeted-r 8}])))\n    [views/game-interface views-cfg]))\n\n;; TODO: add targeted-friend\n\n(defcard-rg base-selected\n  (let [system (ig/init system/game-config)\n        game-cfg (:zetawar.system/game system)\n        views-cfg (:zetawar.system/game-views system)\n        conn (:conn views-cfg)]\n    (app/start-new-game! game-cfg :sterlings-aruba-multiplayer)\n    (d/transact! conn [{:db/id (-> @conn app/root e)\n                        :app/selected-q 1\n                        :app/selected-r 2}])\n    [views/game-interface views-cfg]))\n"
  },
  {
    "path": "src/cljs/zetawar/devcards.cljs",
    "content": "(ns zetawar.devcards\n  (:require\n   [devcards.core :as devcards :include-macros true]\n   [zetawar.devcards.data-formats]\n   [zetawar.devcards.game-specs]\n   [zetawar.devcards.maps-and-scenarios]\n   [zetawar.devcards.selection-and-target]\n   [zetawar.site :as site]))\n\n(when (site/viewing-devcards?)\n  (devcards/start-devcard-ui!))\n"
  },
  {
    "path": "src/cljs/zetawar/doc.cljs",
    "content": "(ns zetawar.doc)\n\n(def key-descriptions\n  {:game.map/description \"A string describing the map\"\n   :game.map/terrains \"A vector of terrains\"\n   :game.map.terrain/q \"Terrain tile column index\"\n   :game.map.terrain/r \"Terrain tile row index\"\n   :game.map.terrain/terrain-type \"Terrain type keyword\"})\n"
  },
  {
    "path": "src/cljs/zetawar/events/game.cljs",
    "content": "(ns zetawar.events.game\n  (:require\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]\n   [zetawar.router :as router]))\n\n;; TODO: simplify\n(defmethod router/handle-event ::execute-action\n  [{:as handler-ctx :keys [db]} [_ action]]\n  (let [game (app/current-game db)]\n    (case (:action/type action)\n      :action.type/attack-unit\n      (let [{:keys [action/attacker-q action/attacker-r\n                    action/defender-q action/defender-r]} action\n            [attacker-damage defender-damage] (game/battle-damage db game\n                                                                  attacker-q attacker-r\n                                                                  defender-q defender-r)\n            action (merge action {:action/attacker-damage attacker-damage\n                                  :action/defender-damage defender-damage})]\n        {:tx       (game/action-tx db game action)\n         :dispatch [[:zetawar.events.ui/hide-copy-link]]\n         :notify   [[:zetawar.players/apply-action :faction.color/all action]]})\n\n      :action.type/end-turn\n      (let [game (app/current-game db)\n            next-faction-color (game/next-faction-color game)]\n        {:tx       (game/action-tx db game action)\n         :dispatch (cond-> [[:zetawar.events.ui/set-url-game-state]]\n                     (<= 2 (game/human-faction-count db game))\n                     (into [[:zetawar.events.ui/show-copy-link]]))\n         :notify   [[:zetawar.players/apply-action :faction.color/all action]\n                    [:zetawar.players/start-turn next-faction-color]]})\n\n      {:tx       (game/action-tx db game action)\n       :dispatch [[:zetawar.events.ui/hide-copy-link]]\n       :notify   [[:zetawar.players/apply-action :faction.color/all action]]})))\n"
  },
  {
    "path": "src/cljs/zetawar/events/player.cljs",
    "content": "(ns zetawar.events.player\n  (:require\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]\n   [zetawar.router :as router]))\n\n(defmethod router/handle-event ::send-game-state\n  [{:as handler-ctx :keys [db]} [_ faction-color]]\n  (let [game (app/current-game db)\n        game-state (game/get-game-state db game :full)]\n    {:notify [[:zetawar.players/update-game-state faction-color game-state]]}))\n\n;; TODO: add middleware for actions?\n\n(defmethod router/handle-event ::execute-action\n  [{:as handler-ctx :keys [db]} [_ action]]\n  (let [app (app/root db)\n        game (app/current-game db)\n        faction-color (game/current-faction-color game)\n        action (assoc action :action/faction-color faction-color)]\n    (when-not (and (= (:action/type action) :action.type/end-turn)\n                   (:app/ai-turn-stepping app))\n      {:dispatch [[:zetawar.events.game/execute-action action]]})))\n"
  },
  {
    "path": "src/cljs/zetawar/events/ui.cljs",
    "content": "(ns zetawar.events.ui\n  (:require\n   [cljsjs.clipboard]\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.db :refer [e qe qes qess]]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]\n   [zetawar.players :as players]\n   [zetawar.router :as router]\n   [zetawar.serialization :as serialization]\n   [zetawar.util :refer [breakpoint inspect only oonly]]))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Global\n\n(defmethod router/handle-event ::alert\n  [{:as handler-ctx :keys [db]} [_ message]]\n  (let [app (app/root db)]\n    {:tx [{:db/id (e app)\n           :app/alert-type :success\n           :app/alert-message message}]}))\n\n(defmethod router/handle-event ::hide-alert\n  [{:as handler-ctx :keys [db]} [_]]\n  (let [app (app/root db)]\n    {:tx [[:db/retract (e app) :app/alert-message (:app/alert-message app)]\n          [:db/retract (e app) :app/alert-type (:app/alert-type app)]]}))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Selection\n\n;; New click logic:\n;; - no selection?\n;;   - owned unit on tile?\n;;     - select unit\n;;   - owned base?\n;;     - select base\n;; - already this tile selected?\n;;   - deselect\n;; - selection set, but this tile not selected?\n;;   - selected tile a unit?\n;;     - tile contains enemy in range?\n;;       - set target to enemy\n;;     - tile is a valid move destination?\n;;       - set target to terrain\n\n(defmethod router/handle-event ::select-hex\n  [{:as handler-ctx :keys [db]} [_ ev-q ev-r]]\n  (let [app (app/root db)\n        game (app/current-game db)\n        [selected-q selected-r] (app/selected-hex db)\n        [targeted-q targeted-r] (app/targeted-hex db)\n        unit (game/unit-at db game ev-q ev-r)\n        terrain (game/terrain-at db game ev-q ev-r)\n        selected-unit (game/unit-at db game selected-q selected-r)\n        selected-terrain (game/terrain-at db game selected-q selected-r)\n        targeted-unit (game/unit-at db game targeted-q targeted-r)\n        targeted-terrain (game/terrain-at db game targeted-q targeted-r)]\n    {:tx (cond\n           ;; selecting selected tile\n           (and (= ev-q selected-q) (= ev-r selected-r))\n           (cond-> []\n             (and selected-q selected-r)\n             (conj [:db/retract (e app) :app/selected-q selected-q]\n                   [:db/retract (e app) :app/selected-r selected-r])\n\n             (and targeted-q targeted-r)\n             (conj [:db/retract (e app) :app/targeted-q targeted-q]\n                   [:db/retract (e app) :app/targeted-r targeted-r]))\n\n           ;; selecting targeted tile\n           (and (= ev-q targeted-q) (= ev-r targeted-r))\n           [[:db/retract (e app) :app/targeted-q targeted-q]\n            [:db/retract (e app) :app/targeted-r targeted-r]]\n\n           ;; selecting in range enemy unit with unit selected\n           (and unit\n                selected-unit\n                (not (game/unit-current? db game unit))\n                (and (game/can-attack? db game selected-unit)\n                     (game/in-range? db selected-unit unit)))\n           [{:db/id (e app)\n             :app/targeted-q ev-q\n             :app/targeted-r ev-r}]\n\n           ;; selecting valid destination terrain with unit selected\n           (and terrain\n                selected-unit\n                (game/can-move? db game selected-unit)\n                (game/valid-destination? db game selected-unit ev-q ev-r))\n           [{:db/id (e app)\n             :app/targeted-q ev-q\n             :app/targeted-r ev-r}]\n\n           ;; selecting friendly unit with unit or terrain selected\n           (and unit\n                (or selected-unit selected-terrain)\n                (game/can-field-repair? db game selected-unit)\n                (game/repairable? db game unit)\n                (game/in-range? db selected-unit unit)\n                (game/has-repairable-armor-type? db game selected-unit unit))\n           [{:db/id (e app)\n             :app/targeted-q ev-q\n             :app/targeted-r ev-r}]\n\n           ;; selecting owned base with no unit selected\n           (and terrain\n                (not unit)\n                (not selected-unit)\n                (game/current-base? db game terrain))\n           (cond-> [{:db/id (e app)\n                     :app/selected-q ev-q\n                     :app/selected-r ev-r}]\n             (and targeted-q targeted-r)\n             (conj [:db/retract (e app) :app/targeted-q targeted-q]\n                   [:db/retract (e app) :app/targeted-r targeted-r]))\n\n           ;; selecting unselected friendly unit\n           (and unit\n                (or (game/can-move? db game unit)\n                    (game/can-repair? db game unit)\n                    (and\n                     (game/can-attack? db game unit)\n                     (not= 0 (count (game/enemies-in-range db game unit))))\n                    (and\n                     (game/can-field-repair? db game unit)\n                     (not= 0 (count (game/friends-in-range db game unit))))\n                    (game/can-capture? db game unit terrain)))\n           (cond-> [{:db/id (e app)\n                     :app/selected-q ev-q\n                     :app/selected-r ev-r}]\n             (and targeted-q targeted-r)\n             (conj [:db/retract (e app) :app/targeted-q targeted-q]\n                   [:db/retract (e app) :app/targeted-r targeted-r])))}))\n\n(defmethod router/handle-event ::clear-selection\n  [{:as handler-ctx :keys [db]} _]\n  (let [app (app/root db)\n        [selected-q selected-r] (app/selected-hex db)\n        [targeted-q targeted-r] (app/targeted-hex db)]\n    {:tx (cond-> []\n           (and selected-q selected-r)\n           (conj [:db/retract (e app) :app/selected-q selected-q]\n                 [:db/retract (e app) :app/selected-r selected-r])\n\n           (and targeted-q targeted-r)\n           (conj [:db/retract (e app) :app/targeted-q targeted-q]\n                 [:db/retract (e app) :app/targeted-r targeted-r]))}))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Unit and base actions\n\n(defmethod router/handle-event ::move-selected-unit\n  [{:as handler-ctx :keys [db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [from-q from-r] (app/selected-hex db)\n        [to-q to-r] (app/targeted-hex db)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/move-unit\n                  :action/faction-color cur-faction-color\n                  :action/from-q from-q\n                  :action/from-r from-r\n                  :action/to-q to-q\n                  :action/to-r to-r}]\n                [::move-selection from-q from-r to-q to-r]]}))\n\n;; TODO: replace with move selection to target\n(defmethod router/handle-event ::move-selection\n  [{:as handler-ctx :keys [db]} [_ from-q from-r to-q to-r]]\n  (let [app (app/root db)\n        game (app/current-game db)\n        unit (game/unit-at db game to-q to-r)\n        terrain (game/base-at db game to-q to-r)]\n    (if (or (and (game/can-attack? db game unit)\n                 (not-empty (game/enemies-in-range db game unit)))\n            (and (game/can-field-repair? db game unit)\n                 (not-empty (game/repairable-friends-in-range db game unit)))\n            (game/can-capture? db game unit terrain))\n      {:tx       [[:db/add (e app) :app/selected-q to-q]\n                  [:db/add (e app) :app/selected-r to-r]\n                  [:db/retract (e app) :app/targeted-q to-q]\n                  [:db/retract (e app) :app/targeted-r to-r]]}\n      {:dispatch [[::clear-selection]]})))\n\n(defmethod router/handle-event ::attack-targeted\n  [{:as handler-ctx :keys [db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [attacker-q attacker-r] (app/selected-hex db)\n        [defender-q defender-r] (app/targeted-hex db)\n        [attacker-damage defender-damage] (game/battle-damage db game\n                                                              attacker-q attacker-r\n                                                              defender-q defender-r)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/attack-unit\n                  :action/faction-color cur-faction-color\n                  :action/attacker-q attacker-q\n                  :action/attacker-r attacker-r\n                  :action/defender-q defender-q\n                  :action/defender-r defender-r\n                  :action/attacker-damage attacker-damage\n                  :action/defender-damage defender-damage}]\n                [::clear-selection]]}))\n\n(defmethod router/handle-event ::repair-selected\n  [{:as handler-ctx :keys [db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [q r] (app/selected-hex db)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/repair-unit\n                  :action/faction-color cur-faction-color\n                  :action/q q\n                  :action/r r}]\n                [::clear-selection]]}))\n\n(defmethod router/handle-event ::repair-targeted\n  [{:as handler-ctx :keys [db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [repairer-q repairer-r] (app/selected-hex db)\n        [target-q target-r] (app/targeted-hex db)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/field-repair-unit\n                  :action/faction-color cur-faction-color\n                  :action/repairer-q repairer-q\n                  :action/repairer-r repairer-r\n                  :action/target-q target-q\n                  :action/target-r target-r}]\n                [::clear-selection]]}))\n\n(defmethod router/handle-event ::capture-selected\n  [{:as handler-ctx :keys [db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [q r] (app/selected-hex db)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/capture-base\n                  :action/faction-color cur-faction-color\n                  :action/q q\n                  :action/r r}]\n                [::clear-selection]]}))\n\n(defmethod router/handle-event ::build-unit\n  [{:as handler-ctx :keys [db]} [_ unit-type-id]]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)\n        [q r] (app/selected-hex db)]\n    {:dispatch [[:zetawar.events.game/execute-action\n                 {:action/type :action.type/build-unit\n                  :action/faction-color cur-faction-color\n                  :action/q q\n                  :action/r r\n                  :action/unit-type-id unit-type-id}]\n                [::clear-selection]]}))\n\n(defmethod router/handle-event ::end-turn\n  [{:as handler-ctx :keys [ev-chan notify-chan conn db]} _]\n  (let [game (app/current-game db)\n        cur-faction-color (game/current-faction-color game)]\n    {:dispatch [[::clear-selection]\n                [:zetawar.events.game/execute-action\n                 {:action/type :action.type/end-turn\n                  :action/faction-color cur-faction-color}]]}))\n\n(defmethod router/handle-event ::set-url-game-state\n  [{:as handler-ctx :keys [ev-chan conn db]} _]\n  (serialization/set-url-game-state! @conn))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Copy link\n\n(defmethod router/handle-event ::show-copy-link\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/show-copy-link true]]}))\n\n(defmethod router/handle-event ::hide-copy-link\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)\n        {:keys [app/show-copy-link]} app]\n    (when-not (nil? show-copy-link)\n      {:tx [[:db/retract (e app) :app/show-copy-link show-copy-link]]})))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Unit picker\n\n(defmethod router/handle-event ::show-unit-picker\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/picking-unit true]]}))\n\n(defmethod router/handle-event ::hide-unit-picker\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/picking-unit false]]}))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Win message\n\n(defmethod router/handle-event ::hide-win-message\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/hide-win-message true]]}))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Faction configuration\n\n(defmethod router/handle-event ::configure-faction\n  [{:as handler-ctx :keys [ev-chan db]} [_ faction]]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/configuring-faction (e faction)]]}))\n\n(defmethod router/handle-event ::hide-faction-settings\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)\n        faction-eid (-> app :app/configuring-faction e)]\n    {:tx [[:db/retract (e app) :app/configuring-faction faction-eid]]}))\n\n;; TODO: find a way to make player swapping nicer (maybe put in router?)\n;; TODO: cleanup return value construction\n(defmethod router/handle-event ::set-faction-player-type\n  [{:as handler-ctx :keys [ev-chan conn db players]} [_ faction player-type-id]]\n  (let [{:as app :keys [ai-turn-stepping]} (app/root db)\n        {:keys [game/factions game/current-faction]} (app/current-game db)\n        {:keys [faction/color]} faction\n        other-factions (remove #(= (e faction) (e %)) factions)\n        {:keys [ai]} (players/player-types-by-id player-type-id)\n        tx [{:db/id (e faction)\n             :faction/ai ai\n             :faction/player-type player-type-id}]\n        cur-player (color @players)\n        new-player (players/new-player handler-ctx player-type-id color)\n        notify (when (and ai (= (e faction) (e current-faction)))\n                 [[:zetawar.players/start-turn color]])]\n    (players/stop cur-player)\n    (players/start new-player)\n    (swap! players assoc color new-player)\n    (if (and ai (= (count other-factions)\n                   (count (filter :faction/ai other-factions))))\n      {:tx (conj tx [:db/add (e app) :app/ai-turn-stepping (not ai-turn-stepping)])\n       :dispatch [[::alert \"AI enabled for all factions. Enabling turn stepping.\"]]\n       :notify notify}\n      {:tx (conj tx [:db/add (e app) :app/ai-turn-stepping false])\n       :notify notify})))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; New game\n\n(defmethod router/handle-event ::show-new-game-settings\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/configuring-new-game true]]}))\n\n(defmethod router/handle-event ::hide-new-game-settings\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/configuring-new-game false]]}))\n\n(defmethod router/handle-event ::start-new-game\n  [{:as handler-ctx :keys [ev-chan conn]} [_ scenario-id]]\n  (app/start-new-game! handler-ctx scenario-id))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Tile coordinates\n\n(defmethod router/handle-event ::hover-hex-enter\n  [{:as handler-ctx :keys [ev-chan db]} [_ q r]]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/hover-q q]\n          [:db/add (e app) :app/hover-r r]]}))\n\n(defmethod router/handle-event ::hover-hex-leave\n  [{:as handler-ctx :keys [ev-chan db]} [_ q r]]\n  (let [app (app/root db)]\n      {:tx [[:db/retract (e app) :app/hover-q q]\n            [:db/retract (e app) :app/hover-r r]]}))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; End turn alert\n\n(defmethod router/handle-event ::show-end-turn-alert\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/end-turn-alert true]]}))\n\n(defmethod router/handle-event ::hide-end-turn-alert\n  [{:as handler-ctx :keys [ev-chan db]} _]\n  (let [app (app/root db)]\n    {:tx [[:db/add (e app) :app/end-turn-alert false]]}))\n"
  },
  {
    "path": "src/cljs/zetawar/game/spec.cljs",
    "content": "(ns zetawar.game.spec\n  (:require\n   [clojure.spec :as s]))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Maps\n\n(s/def :game.map/description string?)\n(s/def :game.map.terrain/q (s/and int? #(>= % 0)))\n(s/def :game.map.terrain/r (s/and int? #(>= % 0)))\n(s/def :game.map.terrain/terrain-type keyword?)\n\n(s/def :game.map/terrain\n  (s/keys :req-un [:game.map.terrain/q\n                   :game.map.terrain/r\n                   :game.map.terrain/terrain-type]))\n\n(s/def :game.map/terrains\n  (s/coll-of :game.map/terrain))\n\n(s/def :game/map\n  (s/keys :req-un [:game.map/description\n                   :game.map/terrains]))\n"
  },
  {
    "path": "src/cljs/zetawar/game.cljs",
    "content": "(ns zetawar.game\n  (:require\n   [clojure.string :as string]\n   [datascript.core :as d]\n   [zetawar.data :as data]\n   [zetawar.db :as db :refer [e find-by qe qes qess]]\n   [zetawar.hex :as hex]\n   [zetawar.logging :as log]\n   [zetawar.util :refer [breakpoint inspect oonly only]]))\n\n;; TODO: improve exception data\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Util\n\n;; TODO: benchmark vs bit shifting and with/without memoization\n(def game-pos-idx\n  (memoize\n   (fn game-pos-idx [game q r]\n     (if (and game q r)\n       (+ r (* 1000 (+ (* (e game) 1000) q)))\n       -1))))\n\n(defn game-id-idx [game-or-game-id id]\n  (let [game-id (if (:db/id game-or-game-id)\n                  (:game/id game-or-game-id)\n                  game-or-game-id)]\n    (-> id\n        str\n        (string/split #\":\")\n        (nth 1)\n        (str \"-\" game-id)\n        keyword)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Game\n\n(defn game-by-id [db game-id]\n  (find-by db :game/id game-id))\n\n(defn current-faction-color [game]\n  (get-in game [:game/current-faction :faction/color]))\n\n(defn next-faction-color [game]\n  (get-in game [:game/current-faction :faction/next-faction :faction/color]))\n\n(defn game-ex [message game]\n  (ex-info message (select-keys game [:game/self-repair])))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Factions\n\n(defn to-faction-color [color]\n  (->> color\n       name\n       (keyword 'faction.color)))\n\n(defn faction-by-color [db game color]\n  (qe '[:find ?f\n        :in $ ?g ?color\n        :where\n        [?g :game/factions ?f]\n        [?f :faction/color ?color]]\n      db (e game) (to-faction-color color)))\n\n(defn faction-count [db game]\n  (-> (d/q '[:find (count ?f)\n             :in $ ?g\n             :where\n             [?g :game/factions ?f]]\n           db (e game))\n      ffirst\n      (or 0)))\n\n(defn ai-faction-count [db game]\n  (-> (d/q '[:find (count ?f)\n             :in $ ?g\n             :where\n             [?g :game/factions ?f]\n             [?f :faction/ai true]]\n           db (e game))\n      ffirst\n      (or 0)))\n\n(defn human-faction-count [db game]\n  (- (faction-count db game)\n     (ai-faction-count db game)))\n\n(defn faction-bases [db faction]\n  (qess '[:find ?t\n          :in $ ?f\n          :where\n          [?t :terrain/owner ?f]]\n        db (e faction)))\n\n(defn faction-base-count [db faction]\n  (-> (d/q '[:find (count ?b)\n             :in $ ?f\n             :where\n             [?b :terrain/owner ?f]]\n           db (e faction))\n      ffirst\n      (or 0)))\n\n(defn faction-base-being-captured-count [db faction]\n  (-> (d/q '[:find (count ?b)\n             :in $ ?f\n             :where\n             [?b :terrain/owner ?f]\n             [(not= ?ef f)]\n             [?ef :faction/units ?u]\n             [?u :unit/terrain ?b]\n             [?u :unit/capturing true]]\n           db (e faction))\n      ffirst\n      (or 0)))\n\n(defn enemy-base-count [db faction]\n  (-> (d/q '[:find (count ?b)\n             :in $ ?f\n             :where\n             [?b :terrain/owner ?ef]\n             [(not= ?ef ?f)]]\n           db (e faction))\n      ffirst\n      (or 0)))\n\n(defn faction-unit-count [db faction]\n  (-> (d/q '[:find (count ?u)\n             :in $ ?f\n             :where\n             [?f :faction/units ?u]]\n           db (e faction))\n      ffirst\n      (or 0)))\n\n(defn enemy-unit-count [db faction]\n  (-> (d/q '[:find (count ?u)\n             :in $ ?f\n             :where\n             [?f :faction/units ?u]\n             [(not= ?f ?cf)]]\n           db (e faction))\n      ffirst\n      (or 0)))\n\n(defn faction-won? [db faction]\n  (and (= 0 enemy-base-count db faction)\n       (= 0 enemy-unit-count db faction)))\n\n(defn income [db game faction]\n  (let [base-count (faction-base-count db faction)\n        captured-count (faction-base-being-captured-count db faction)\n        {:keys [game/credits-per-base]} game]\n    (* (- base-count captured-count) credits-per-base)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Terrain\n\n(defn to-terrain-type-id [terrain-type-name]\n  (->> terrain-type-name\n       name\n       (keyword 'terrain-type.id)))\n\n(defn terrain-type-by-id [db game terrain-type-id]\n  (qe '[:find ?tt\n        :in $ ?g ?tt-id\n        :where\n        [?g  :game/terrain-types ?tt]\n        [?tt :terrain-type/id ?tt-id]]\n      db (e game) terrain-type-id))\n\n(defn terrain? [x]\n  (contains? x :terrain/type))\n\n(defn base? [x]\n  (not (empty? (get-in x [:terrain/type :terrain-type/can-build]))))\n\n(defn terrain-hex [terrain]\n  [(:terrain/q terrain)\n   (:terrain/r terrain)])\n\n(defn terrain-at [db game q r]\n  (->> (game-pos-idx game q r)\n       (d/datoms db :avet :terrain/game-pos-idx)\n       first\n       :e\n       (d/entity db)))\n\n(defn checked-terrain-at [db game q r]\n  (let [terrain (terrain-at db game q r)]\n    (when-not terrain\n      (throw (ex-info \"No terrain at specified coordinates\"\n                      {:q q :r r})))\n    terrain))\n\n(defn base-at [db game q r]\n  (let [terrain (terrain-at db game q r)]\n    (when (base? terrain)\n      terrain)))\n\n(defn checked-base-at [db game q r]\n  (let [base (base-at db game q r)]\n    (when-not base\n      (throw (ex-info \"No base at specified coordinates\"\n                      {:q q :r r})))\n    base))\n\n(defn check-base-current [db game base]\n  (let [cur-faction (:game/current-faction game)\n        base-faction (:terrain/owner base)]\n    (when (not= cur-faction base-faction)\n      (throw (ex-info \"Base is not owned by the current faction\"\n                      {:current-faction (:faction/color cur-faction)\n                       })))))\n\n(defn current-base? [db game x]\n  (when (base? x)\n    (let [cur-faction (:game/current-faction game)\n          base-faction (:terrain/owner x)]\n      (= cur-faction base-faction))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Units\n\n(defn to-unit-type-id [unit-type-name]\n  (->> unit-type-name\n       name\n       (keyword 'unit-type.id)))\n\n(defn to-armor-type [armor-type-name]\n  (->> armor-type-name\n       name\n       (keyword 'unit-type.armor-type)))\n\n(defn to-buildable-at [buildable-at-name]\n  (->> buildable-at-name\n       name\n       (keyword 'unit-type.buildable-at)))\n\n(defn unit? [x]\n  (contains? x :unit/type))\n\n(defn unit-hex [unit]\n  [(:unit/q unit)\n   (:unit/r unit)])\n\n(defn unit-color [unit]\n  (get-in unit [:faction/_units :faction/color]))\n\n(defn unit-at [db game q r]\n  (->> (game-pos-idx game q r)\n       (d/datoms db :avet :unit/game-pos-idx)\n       first\n       :e\n       (d/entity db)))\n\n(defn checked-unit-at [db game q r]\n  (let [unit (unit-at db game q r)]\n    (when-not unit\n      (throw (ex-info \"Unit does not exist at specified coordinates\"\n                      {:q q :r r})))\n    unit))\n\n(defn unit-faction [db unit]\n  (:faction/_units unit))\n\n(defn check-unit-current [db game unit]\n  (let [cur-faction (:game/current-faction game)\n        u-faction (unit-faction db unit)]\n    (when (not= (e cur-faction) (e u-faction))\n      (throw (ex-info \"Unit is not a member of the current faction\"\n                      {:current-faction (:faction/color cur-faction)\n                       :unit-faction (:faction/color u-faction)})))))\n\n(defn unit-current? [db game unit]\n  (try\n    (check-unit-current db game unit)\n    true\n    (catch :default ex\n      false)))\n\n(defn on-base? [db game unit]\n  (base? (terrain-at db game (:unit/q unit) (:unit/r unit))))\n\n(defn on-owned-base? [db game unit]\n  (let [{:keys [unit/q unit/r]} unit\n        terrain (terrain-at db game q r)]\n    (and (base? terrain)\n         (= (some-> terrain :terrain/owner e)\n            (e (unit-faction db unit))))))\n\n(defn on-capturable-base? [db game unit]\n  (let [{:keys [unit/q unit/r]} unit\n        terrain (terrain-at db game q r)]\n    (and (base? terrain)\n         (not= (some-> terrain :terrain/owner e)\n               (e (unit-faction db unit))))))\n\n(defn unit-ex [message unit]\n  (ex-info message (select-keys unit [:unit/q :unit/r])))\n\n(defn unit-terrain-effects [db unit terrain]\n  (only (d/q '[:find ?mc ?at ?ar\n               :in $ ?u ?t\n               :where\n               [?u  :unit/type ?ut]\n               [?t  :terrain/type ?tt]\n               [?tt :terrain-type/effects ?e]\n               [?e  :terrain-effect/unit-type ?ut]\n               [?e  :terrain-effect/attack-bonus ?at]\n               [?e  :terrain-effect/armor-bonus ?ar]\n               [?e  :terrain-effect/movement-cost ?mc]]\n             db (e unit) (e terrain))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Unit States\n\n(defn to-unit-state-map-id [state-map-name]\n  (->> state-map-name\n       name\n       (keyword 'unit-state-map.id)))\n\n(defn unit-state-map-by-id [db game unit-state-map-id]\n  (qe '[:find ?usm\n        :in $ ?g ?usm-id\n        :where\n        [?g   :game/unit-state-maps ?usm]\n        [?usm :unit-state-map/id ?usm-id]]\n      db (e game) unit-state-map-id))\n\n(defn to-unit-state-id\n  ([unit-state-name]\n   (->> unit-state-name\n        name\n        (keyword 'unit-state.id)))\n  ([state-map-name state-name]\n   (to-unit-state-id (str (name state-map-name)\n                          \"_\"\n                          (name state-name)))))\n\n(defn unit-state-by-id [db game unit-state-id]\n  (qe '[:find ?us\n        :in $ ?g ?us-id\n        :where\n        [?g  :game/unit-states ?us]\n        [?us :unit-state/id ?us-id]]\n      db (e game) unit-state-id))\n\n(defn to-action-type [action-type-name]\n  (->> action-type-name name (keyword 'action.type)))\n\n(defn start-state [unit-or-unit-type]\n  (let [unit-type (if (unit? unit-or-unit-type)\n                    (:unit/type unit-or-unit-type)\n                    unit-or-unit-type)]\n    (get-in unit-type [:unit-type/state-map\n                       :unit-state-map/start-state])))\n\n(defn built-state [unit-or-unit-type]\n  (let [unit-type (if (unit? unit-or-unit-type)\n                    (:unit/type unit-or-unit-type)\n                    unit-or-unit-type)]\n    (get-in unit-type [:unit-type/state-map\n                       :unit-state-map/built-state])))\n\n;; Note: (->> (e unit) (d/entity db) ...) is to support UI subs\n(defn next-state [db unit action]\n  (let [transition-map (->> (e unit)\n                            (d/entity db)\n                            :unit/state\n                            :unit-state/transitions\n                            (map (juxt :unit-state-transition/action-type\n                                       :unit-state-transition/new-state))\n                            (into {}))]\n    (transition-map action)))\n\n(defn checked-next-state [db unit action]\n  (let [new-state (next-state db unit action)]\n    (when-not new-state\n      (throw (ex-info \"No state transition from current state found for action\"\n                      {:current-state (get-in unit [:unit/state :unit-state/id])\n                       :action action})))\n    new-state))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Movement\n\n(defn valid-moves [db game unit]\n  (let [start [(:unit/q unit) (:unit/r unit)]\n        u-faction-eid (e (unit-faction db unit))\n        unit-type-eid (e (:unit/type unit))\n        armor-type (-> unit :unit/type :unit-type/armor-type)\n        unit-movement (get-in unit [:unit/type :unit-type/movement])\n        unit-at (memoize #(unit-at db game %1 %2))\n        terrain-type->cost (into {} (d/q '[:find ?tt ?mc\n                                           :in $ ?ut\n                                           :where\n                                           [?tt :terrain-type/effects ?e]\n                                           [?e  :terrain-effect/unit-type ?ut]\n                                           [?e  :terrain-effect/movement-cost ?mc]]\n                                         db unit-type-eid))\n        terrain-cost-at (memoize (fn terrain-cost-at [q r]\n                                   (some-> (terrain-at db game q r)\n                                           :terrain/type\n                                           e\n                                           terrain-type->cost)))\n        adjacent-costs (memoize (fn adjacent-costs [q r]\n                                  (into []\n                                        (keep #(when-let [cost (apply terrain-cost-at %)]\n                                                 (conj % cost)))\n                                        (hex/adjacents q r))))\n        enemy-at? (memoize (fn enemy-at? [q r]\n                             (some-> (unit-at q r)\n                                     :faction/_units\n                                     e\n                                     (not= u-faction-eid))))\n        zoc-enemy-at? (memoize (fn zoc-enemy-at? [q r]\n                                 (when-let [other-unit (unit-at q r)]\n                                   (let [ou-faction-eid (-> other-unit :faction/_units e)\n                                         zoc-armor-types (-> other-unit :unit/type :unit-type/zoc-armor-types)]\n                                     (and (not= u-faction-eid ou-faction-eid)\n                                          (contains? zoc-armor-types armor-type))))))\n        adjacent-zoc-enemy? (memoize (fn adjacent-enemy? [q r]\n                                       (some #(apply zoc-enemy-at? %) (hex/adjacents q r))))\n        ;; frontier = {[q r] [cost path], ...}\n        ;; moves = {[q r] [cost path], ...}\n        expand-frontier (fn expand-frontier [frontier moves]\n                          (loop [[[[q r] [frontier-cost path]] & remaining-frontier] frontier new-frontier {}]\n                            (if frontier-cost\n                              (let [remaining-movement (- unit-movement frontier-cost)\n                                    terrain-costs (adjacent-costs q r)\n                                    new-moves (into {}\n                                                    (keep (fn [[q r terrain-cost]]\n                                                            (when (and (or (:game/move-through-friendly game)\n                                                                           (not (unit-at q r)))\n                                                                       (not (enemy-at? q r)))\n                                                              (let [terrain-cost (if (adjacent-zoc-enemy? q r) ; check in zoc\n                                                                                   (max terrain-cost remaining-movement)\n                                                                                   terrain-cost)\n                                                                    new-move-cost (+ frontier-cost terrain-cost)]\n                                                                (when (and (<= new-move-cost (moves [q r] unit-movement))\n                                                                           (<= new-move-cost (new-frontier [q r] unit-movement)))\n                                                                  [[q r] [new-move-cost (conj path [q r])]])))))\n                                                    terrain-costs)]\n                                (recur remaining-frontier (conj new-frontier new-moves)))\n                              new-frontier)))]\n    (loop [frontier {start [0 []]} moves {start [0 []]}]\n      (let [new-frontier (expand-frontier frontier moves)\n            new-moves (conj moves new-frontier)]\n        (if (empty? new-frontier)\n          (into #{}\n                (comp (remove #(apply unit-at (first %))) ; remove occupied destinations\n                      (map (fn [[dest [cost path]]] {:from start :to dest :cost cost :path path})))\n                (dissoc moves start))\n          (recur new-frontier new-moves))))))\n\n;; TODO: implement valid-move?\n\n(defn valid-destinations [db game unit]\n  (into #{}\n        (map :to)\n        (valid-moves db game unit)))\n\n(defn valid-destination? [db game unit q r]\n  (contains? (valid-destinations db game unit) [q r]))\n\n(defn check-valid-destination [db game unit q r]\n  (when-not (valid-destination? db game unit q r)\n    (throw (ex-info \"Specified destination is not a valid move\"\n                    {:q q :r r}))))\n\n(defn check-can-move [db game unit]\n  (check-unit-current db game unit)\n  (when (:unit/capturing unit)\n    (throw (unit-ex \"Unit cannot move while capturing\" unit)))\n  (checked-next-state db unit :action.type/move-unit))\n\n(defn can-move? [db game unit]\n  (try\n    (check-can-move db game unit)\n    true\n    (catch :default ex\n      false)))\n\n(defn teleport-tx [db game from-q from-r to-q to-r]\n  (let [unit (checked-unit-at db game from-q from-r)\n        terrain (checked-terrain-at db game to-q to-r)]\n    [{:db/id (e unit)\n      :unit/game-pos-idx (game-pos-idx game to-q to-r)\n      :unit/q to-q\n      :unit/r to-r\n      :unit/terrain (e terrain)}]))\n\n;; TODO: check move is valid\n(defn move-tx\n  \"Returns a transaction that updates the unit's location and move count.\"\n  ([db game unit to-terrain]\n   (let [new-move-count (inc (:unit/move-count unit 0))\n         [to-q to-r] (terrain-hex to-terrain)\n         new-state (check-can-move db game unit)]\n     [{:db/id (e unit)\n       :unit/game-pos-idx (game-pos-idx game to-q to-r)\n       :unit/q to-q\n       :unit/r to-r\n       :unit/terrain (e to-terrain)\n       :unit/move-count new-move-count\n       :unit/state (e new-state)}]))\n  ([db game from-q from-r to-q to-r]\n   (let [unit (checked-unit-at db game from-q from-r)\n         terrain (checked-terrain-at db game to-q to-r)]\n     (move-tx db game unit terrain))))\n\n(defn move! [conn game-id from-q from-r to-q to-r]\n  (let [db @conn\n        game (game-by-id db game-id)]\n    (d/transact! conn (move-tx db game from-q from-r to-q to-r))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Attack\n\n(defn check-can-attack [db game unit]\n  (check-unit-current db game unit)\n  (when (:unit/capturing unit)\n    (throw (unit-ex \"Unit cannot attack while capturing\" unit)))\n  (checked-next-state db unit :action.type/attack-unit))\n\n(defn can-attack? [db game unit]\n  (try\n    (check-can-attack db game unit)\n    true\n    (catch :default ex\n      false)))\n\n(defn check-in-range [db attacker defender]\n  (let [distance (hex/distance (:unit/q attacker) (:unit/r attacker)\n                               (:unit/q defender) (:unit/r defender))\n        min-range (get-in attacker [:unit/type :unit-type/min-range])\n        max-range (get-in attacker [:unit/type :unit-type/max-range])]\n    (when (or (< distance min-range)\n              (> distance max-range))\n      ;; TODO: add defender details to exception\n      (throw (unit-ex \"Targeted unit is not in range\" attacker)))))\n\n(defn in-range? [db attacker defender]\n  (try\n    (check-in-range db attacker defender)\n    true\n    (catch :default ex\n      false)))\n\n(defn attack-damage [db game attacker defender attacker-terrain defender-terrain]\n  (let [defender-armor-type (get-in defender [:unit/type :unit-type/armor-type])\n        [attacker-q attacker-r] (unit-hex attacker)\n        [defender-q defender-r] (unit-hex defender)\n        attack-strength (oonly (d/q '[:find ?s\n                                      :in $ ?u ?at\n                                      :where\n                                      [?u  :unit/type ?ut]\n                                      [?ut :unit-type/strengths ?us]\n                                      [?us :unit-strength/armor-type ?at]\n                                      [?us :unit-strength/attack ?s]]\n                                    db (e attacker) defender-armor-type))\n        armor (if (:unit/capturing defender)\n                (get-in defender [:unit/type :unit-type/capturing-armor])\n                (get-in defender [:unit/type :unit-type/armor]))\n        attack-bonus (oonly (d/q '[:find ?a\n                                   :in $ ?u ?t\n                                   :where\n                                   [?u  :unit/type ?ut]\n                                   [?t  :terrain/type ?tt]\n                                   [?tt :terrain-type/effects ?e]\n                                   [?e  :terrain-effect/unit-type ?ut]\n                                   [?e  :terrain-effect/attack-bonus ?a]]\n                                 db (e attacker) (e attacker-terrain)))\n        armor-bonus (oonly (d/q '[:find ?d\n                                  :in $ ?u ?t\n                                  :where\n                                  [?u  :unit/type ?ut]\n                                  [?t  :terrain/type ?tt]\n                                  [?tt :terrain-type/effects ?e ]\n                                  [?e  :terrain-effect/unit-type ?ut]\n                                  [?e  :terrain-effect/armor-bonus ?d]]\n                                db (e defender) (e defender-terrain)))\n        attack-hexes (into #{} (map terrain-hex) (:unit/attacked-from defender))\n        ranged-attack-hexes (into #{}\n                                  (filter #(> (apply hex/distance defender-q defender-r %) 1))\n                                  attack-hexes)\n        adjacent-attack-hexes (into #{}\n                                    (filter #(and (apply hex/adjacent? attacker-q attacker-r %)\n                                                  (apply hex/adjacent? defender-q defender-r %)))\n                                    attack-hexes)\n        opposite-attack-hexes (into #{}\n                                    (filter #(apply hex/opposite? attacker-q attacker-r defender-q defender-r %))\n                                    attack-hexes)\n        flanking-attack-hexes (clojure.set/difference attack-hexes\n                                                      ranged-attack-hexes\n                                                      adjacent-attack-hexes\n                                                      opposite-attack-hexes)\n        gang-up-bonus (+ (* (count ranged-attack-hexes)\n                            (:game/ranged-attack-bonus game))\n                         (* (count adjacent-attack-hexes)\n                            (:game/adjacent-attack-bonus game))\n                         (* (count flanking-attack-hexes)\n                            (:game/flanking-attack-bonus game))\n                         (* (count opposite-attack-hexes)\n                            (:game/opposite-attack-bonus game)))]\n    (let [p (-> (+ 0.5 (* 0.05 (+ (- (+ attack-strength attack-bonus)\n                                     (+ armor armor-bonus))\n                                  gang-up-bonus)))\n                (max 0)\n                (min 1))]\n      (js/Math.round\n       (if (:game/stochastic-damage game)\n         (let [hits (->> (repeatedly #(rand))\n                         (take (* 6 (:unit/count attacker)))\n                         (filter #(< % p))\n                         count)]\n           (quot hits 6))\n         (* (:unit/count attacker) p))))))\n\n(defn battle-damage\n  ([db game attacker defender]\n   (let [attacker-terrain (:unit/terrain attacker)\n         defender-terrain (:unit/terrain defender)\n         attack-count (:unit/attack-count attacker 0)\n         defender-damage (attack-damage db game attacker defender attacker-terrain defender-terrain)\n         attacker-damage (if (in-range? db defender attacker)\n                           (attack-damage db game defender attacker defender-terrain attacker-terrain)\n                           0)]\n     [(min (:unit/count attacker) attacker-damage)\n      (min (:unit/count defender) defender-damage)]))\n  ([db game attacker-q attacker-r defender-q defender-r]\n   (let [attacker (checked-unit-at db game attacker-q attacker-r)\n         defender (checked-unit-at db game defender-q defender-r)]\n     (battle-damage db game attacker defender))))\n\n(defn battle-tx\n  ([db game attacker defender attacker-damage defender-damage]\n   (let [new-state (check-can-attack db game attacker)]\n     (check-in-range db attacker defender)\n     (let [attacker-terrain (:unit/terrain attacker)\n           defender-terrain (:unit/terrain defender)\n           attack-count (:unit/attack-count attacker 0)\n           attacker-count (:unit/count attacker)\n           defender-count (:unit/count defender)]\n       (cond-> []\n         (> defender-count defender-damage)\n         (conj {:db/id (e defender)\n                :unit/count (- defender-count defender-damage)\n                :unit/attacked-count (inc (:unit/attacked-count defender))\n                :unit/attacked-from (e attacker-terrain)})\n\n         (= defender-count defender-damage)\n         (conj [:db.fn/retractEntity (e defender)])\n\n         (> attacker-count attacker-damage)\n         (conj {:db/id (e attacker)\n                :unit/count (- attacker-count attacker-damage)\n                :unit/attack-count (inc attack-count)\n                :unit/state (e new-state)})\n\n         (= attacker-count attacker-damage)\n         (conj [:db.fn/retractEntity (e attacker)])))))\n  ([db game attacker-q attacker-r defender-q defender-r attacker-damage defender-damage]\n   (let [attacker (checked-unit-at db game attacker-q attacker-r)\n         defender (checked-unit-at db game defender-q defender-r)]\n     (battle-tx db game attacker defender attacker-damage defender-damage))))\n\n;; TODO: remove attack-tx (superceded by battle-tx)\n(defn attack-tx\n  ([db game attacker defender]\n   (check-can-attack db game attacker)\n   (check-in-range db attacker defender)\n   (apply battle-tx db game attacker defender (battle-damage db game attacker defender)))\n  ([db game attacker-q attacker-r defender-q defender-r]\n   (let [attacker (checked-unit-at db game attacker-q attacker-r)\n         defender (checked-unit-at db game defender-q defender-r)]\n     (attack-tx db game attacker defender))))\n\n(defn attack! [conn game-id attacker-q attacker-r defender-q defender-r]\n  (let [db @conn\n        game (game-by-id db game-id)\n        tx (attack-tx db game attacker-q attacker-r defender-q defender-r)]\n    (d/transact! conn tx)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Repair\n\n(defn check-repairable [db game unit]\n  (when (>= (:unit/count unit) (:game/max-count-per-unit game))\n    (throw (unit-ex \"Unit is already fully repaired\" unit)))\n  unit)\n\n(defn repairable? [db game unit]\n  (try\n    (check-repairable db game unit)\n    true\n    (catch :default ex\n      false)))\n\n(defn check-can-repair [db game unit]\n  (check-unit-current db game unit)\n  (when-not (or (:game/self-repair game) (on-owned-base? db game unit))\n    (throw (game-ex \"Unit self repair is not allowed\" game)))\n  (when (:unit/capturing unit)\n    (throw (unit-ex \"Unit cannot make repairs while capturing\" unit)))\n  (check-repairable db game unit)\n  (checked-next-state db unit :action.type/repair-unit))\n\n(defn can-repair? [db game unit]\n  (try\n    (check-can-repair db game unit)\n    true\n    (catch :default ex\n      false)))\n\n(defn repair-tx\n  \"Returns a transaction that increments unit count and sets the unit repaired\n  flag to true.\"\n  ([db game unit]\n   (let [new-state (check-can-repair db game unit)]\n     [{:db/id (e unit)\n       :unit/count (min (:game/max-count-per-unit game)\n                        (+ (:unit/count unit)\n                           (get-in unit [:unit/type :unit-type/repair])))\n       :unit/repaired true\n       :unit/state (e new-state)}]))\n  ([db game q r]\n   (let [unit (checked-unit-at db game q r)]\n     (repair-tx db game unit))))\n\n(defn repair! [conn game-id q r]\n  (let [db @conn\n        game (game-by-id db game-id)\n        tx (repair-tx db game q r)]\n    (d/transact! conn tx)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Field Repair\n\n(defn check-can-field-repair [db game unit]\n  (check-unit-current db game unit)\n  (when (:unit/capturing unit)\n    (throw (unit-ex \"Unit cannot make repairs while capturing\" unit)))\n  (when (empty? (get-in unit [:unit/type :unit-type/can-repair]))\n    (throw (unit-ex \"Unit cannot repair other units\" unit)))\n  (checked-next-state db unit :action.type/field-repair-unit))\n\n(defn can-field-repair? [db game unit]\n  (try\n    (check-can-field-repair db game unit)\n    true\n    (catch :default ex\n      false)))\n\n(defn check-has-repairable-armor-type [db game repairer target]\n  (let [possible-repair-types (get-in repairer [:unit/type :unit-type/can-repair])\n        goal-repair-type (get-in target [:unit/type :unit-type/armor-type])]\n    (when-not (some #{goal-repair-type} possible-repair-types)\n      (throw (unit-ex \"Armor types are not compatible\" repairer)))\n    repairer))\n\n(defn has-repairable-armor-type? [db game repairer target]\n  (try\n    (check-has-repairable-armor-type db game repairer target)\n    true\n    (catch :default ex\n      false)))\n\n(defn field-repair-tx\n  ([db game repairer target]\n   (let [new-state (check-can-field-repair db game repairer)]\n     (check-in-range db repairer target)\n     (check-repairable db game target)\n     (check-has-repairable-armor-type db game repairer target)\n     [{:db/id (e target)\n       :unit/count (min (:game/max-count-per-unit game)\n                        (+ (:unit/count target)\n                           (get-in repairer [:unit/type :unit-type/repair])))}\n      {:db/id (e repairer)\n       :unit/state (e new-state)}]))\n  ([db game q1 r1 q2 r2]\n   (let [repairer (checked-unit-at db game q1 r1)\n         target (checked-unit-at db game q2 r2)]\n     (field-repair-tx db game repairer target))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Capture\n\n;; TODO: add can-capture? that only checks if unit type can capture\n\n;; TODO: rename to check-can-capture\n(defn check-capturable [db game unit terrain]\n  (check-unit-current db game unit)\n  (when-not (-> unit :unit/type :unit-type/can-capture)\n    (throw (unit-ex \"Unit does not have the ability to capture\" unit)))\n  (when-not (and terrain (base? terrain))\n    (throw (unit-ex \"Unit unit is not on a base\" unit)))\n  (when (:unit/capturing unit)\n    (throw (unit-ex \"Unit is already caturing\" unit)))\n  (when (= (e (unit-faction db unit)) (some-> terrain :terrain/owner e))\n    ;; TODO: add more exception info\n    (throw (ex-info \"Base is already owned by current faction\"\n                    {:q (:terrain/q terrain)\n                     :r (:terrain/r terrain)})))\n  (checked-next-state db unit :action.type/capture-base))\n\n;; TODO: rename to can-capture-terrain? (?)\n(defn can-capture? [db game unit terrain]\n  (try\n    (check-capturable db game unit terrain)\n    true\n    (catch :default ex\n      false)))\n\n(defn capture-tx\n  \"Returns a transaction that sets the unit capturing flag and capture round.\"\n  ([db game unit]\n   (let [base (base-at db game (:unit/q unit) (:unit/r unit))\n         round (:game/round game)\n         new-state (check-capturable db game unit base)]\n     [{:db/id (e unit)\n       :unit/capturing true\n       :unit/capture-round (inc round)\n       :unit/state (e new-state)}]))\n  ([db game q r]\n   (let [unit (checked-unit-at db game q r)]\n     (capture-tx db game unit))))\n\n(defn capture! [conn game-id q r]\n  (let [db @conn\n        game (game-by-id db game-id)\n        tx (capture-tx db game q r)]\n    (d/transact! conn tx)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Build\n\n;; TODO: implement check-can-build\n\n(defn check-unoccupied [db game q r]\n  (let [unit (unit-at db game q r)]\n    (when unit\n      ;; TODO: include info about occupying unit\n      (throw (ex-info \"Base is occupied\" {:q q :r r})))))\n\n(defn unoccupied? [db game q r]\n  (try\n    (check-unoccupied db game q r)\n    true\n    (catch :default ex\n      false)))\n\n(defn build-tx\n  \"Returns a transaction that creates a new unit and updates faction credits.\"\n  ([db game q r unit-type-id]\n   (let [base (checked-base-at db game q r)]\n     (build-tx db game base unit-type-id)))\n  ([db game base unit-type-id]\n   (let [unit-type (find-by db :unit-type/id unit-type-id)\n         cur-faction (:game/current-faction game)\n         credits (:faction/credits cur-faction)\n         cost (:unit-type/cost unit-type)\n         base-q (:terrain/q base)\n         base-r (:terrain/r base)]\n     (check-base-current db game base)\n     (check-unoccupied db game base-q base-r)\n     (when (> cost credits)\n       (throw (ex-info \"Unit cost exceeds available credits\"\n                       {:credits credits\n                        :cost cost})))\n     [{:db/id -1\n       :unit/game-pos-idx (game-pos-idx game base-q base-r)\n       :unit/q base-q\n       :unit/r base-r\n       :unit/terrain (e base)\n       :unit/round-built (:game/round game)\n       :unit/type (e unit-type)\n       :unit/count (:game/max-count-per-unit game)\n       :unit/move-count 0\n       :unit/attack-count 0\n       :unit/attacked-count 0\n       :unit/repaired false\n       :unit/capturing false\n       :unit/state (-> unit-type built-state e)}\n      {:db/id (e cur-faction)\n       :faction/credits (- credits cost)\n       :faction/units -1}])))\n\n(defn build! [conn game-id q r unit-type-id]\n  (let [db @conn\n        game (game-by-id db game-id)\n        tx (build-tx db game q r unit-type-id)]\n    (d/transact! conn tx)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; End Turn\n\n(defn unit-end-turn-tx [db game unit]\n  (let [{:keys [unit/q unit/r unit/capturing unit/capture-round]} unit]\n    (if (and capturing (= capture-round (:game/round game)))\n      (let [faction (unit-faction db unit)\n            terrain (checked-base-at db game q r)]\n        [[:db/add (e terrain) :terrain/owner (e faction)]\n         [:db.fn/retractEntity (e unit)]])\n      (into [{:db/id (e unit)\n              :unit/repaired false\n              :unit/move-count 0\n              :unit/attack-count 0\n              :unit/attacked-count 0\n              :unit/state (-> unit start-state e)}]\n            (map (fn [u] [:db/retract (e unit) :unit/attacked-from (e u)]))\n            (:unit/attacked-from unit)))))\n\n(defn end-turn-tx\n  \"Return a transaction that completes captures, clears per round unit flags,\n  updates the current faction, adds faction credits, and updates the round.\"\n  [db game]\n  (let [starting-faction (:game/starting-faction game)\n        cur-faction (:game/current-faction game)\n        next-faction (:faction/next-faction cur-faction)\n        cur-round (:game/round game)\n        new-round (if (= starting-faction next-faction)\n                    (inc cur-round)\n                    cur-round)\n        credits (+ (:faction/credits next-faction)\n                   (if (> new-round 1)\n                     (income db game next-faction)\n                     0))\n        units (:faction/units cur-faction)\n        attacked-froms (:game/attacked-froms game)]\n    (into [{:db/id (e next-faction)\n            :faction/credits credits}\n           {:db/id (e game)\n            :game/round new-round\n            :game/current-faction (e next-faction)}]\n          (mapcat #(unit-end-turn-tx db game %) units))))\n\n(defn end-turn! [conn game-id]\n  (let [db @conn\n        game (game-by-id db game-id)]\n    (d/transact! conn (end-turn-tx db game))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Actions\n\n(defn action-tx [db game action]\n  (case (:action/type action)\n    :action.type/build-unit\n    (let [{:keys [action/q action/r action/unit-type-id]} action]\n      (build-tx db game q r unit-type-id))\n\n    :action.type/move-unit\n    (let [{:keys [action/from-q action/from-r\n                  action/to-q action/to-r]} action]\n      (move-tx db game from-q from-r to-q to-r))\n\n    :action.type/attack-unit\n    (let [{:keys [action/attacker-q action/attacker-r\n                  action/defender-q action/defender-r\n                  action/attacker-damage\n                  action/defender-damage]} action]\n      (battle-tx db game\n                 attacker-q attacker-r\n                 defender-q defender-r\n                 attacker-damage\n                 defender-damage))\n\n    :action.type/repair-unit\n    (let [{:keys [action/q action/r]} action]\n      (repair-tx db game q r))\n\n    :action.type/field-repair-unit\n    (let [{:keys [action/repairer-q action/repairer-r\n                  action/target-q action/target-r]} action]\n      (field-repair-tx db game\n                       repairer-q repairer-r\n                       target-q target-r))\n\n    :action.type/capture-base\n    (let [{:keys [action/q action/r]} action]\n      (capture-tx db game q r))\n\n    :action.type/end-turn\n    (end-turn-tx db game)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; AI Helpers\n\n(defn buildable-unit-types [db game]\n  (qess '[:find ?ut\n          :in $ ?g\n          :where\n          [?g  :game/current-faction ?f]\n          [?f  :faction/credits ?credits]\n          [?ut :unit-type/cost ?cost]\n          [(>= ?credits ?cost)]]\n        db (e game)))\n\n(defn base-can-act? [db game base]\n  (let [{:keys [terrain/q terrain/r]} base]\n    (and (> (count (buildable-unit-types db game)) 0)\n         (unoccupied? db game q r))))\n\n(defn base-actions [db game base]\n  (let [{:keys [terrain/q terrain/r]} base]\n    (map (fn [ut]\n           {:action/type :action.type/build-unit\n            :action/q q\n            :action/r r\n            :action/unit-type-id (:unit-type/id ut)})\n         (buildable-unit-types db game))))\n\n(defn actionable-bases [db game]\n  (let [faction (:game/current-faction game)\n        bases (faction-bases db faction)]\n    (filter #(base-can-act? db game %) bases)))\n\n;; TODO: rename to enemy-units (?)\n(defn enemies [db game unit]\n  (let [u-faction (unit-faction db unit)]\n    (qess '[:find ?u\n            :in $ ?g ?f-arg\n            :where\n            [?g :game/factions ?f]\n            [?f :faction/units ?u]\n            [(not= ?f ?f-arg)]]\n          db (e game) (e u-faction))))\n\n(defn enemies-in-range [db game unit]\n  (into []\n        (filter #(in-range? db unit %))\n        (enemies db game unit)))\n\n(defn closest-enemy [db game unit]\n  (let [unit-q (:unit/q unit)\n        unit-r (:unit/r unit)]\n    (reduce\n     (fn [closest enemy]\n       (let [enemy-q (:unit/q enemy)\n             enemy-r (:unit/r enemy)\n             closest-q (:unit/q closest)\n             closest-r (:unit/r closest)]\n         (if (< (hex/distance unit-q unit-r enemy-q enemy-r)\n                (hex/distance unit-q unit-r closest-q closest-r))\n           enemy\n           closest)))\n     (enemies db game unit))))\n\n(defn friends [db game unit]\n  (let [u-faction (unit-faction db unit)]\n    (qess '[:find ?u\n            :in $ ?g ?f-arg\n            :where\n            [?g :game/factions ?f]\n            [?f :faction/units ?u]\n            [(= ?f ?f-arg)]]\n          db (e game) (e u-faction))))\n\n(defn friends-in-range [db game unit]\n  (into []\n        (filter #(in-range? db unit %))\n        (friends db game unit)))\n\n(defn repairable-friends-in-range [db game unit]\n  (into []\n        (filter #(repairable? db game %))\n        (friends-in-range db game unit)))\n\n(defn closest-friend [db game unit]\n  (let [unit-q (:unit/q unit)\n        unit-r (:unit/r unit)]\n    (reduce\n     (fn [closest friend]\n       (let [friend-q (:unit/q friend)\n             friend-r (:unit/r friend)\n             closest-q (:unit/q closest)\n             closest-r (:unit/r closest)]\n         (if (< (hex/distance unit-q unit-r friend-q friend-r)\n                (hex/distance unit-q unit-r closest-q closest-r))\n           friend\n           closest)))\n     (friends db game unit))))\n\n(defn unit-can-act? [db game unit]\n  (let [terrain (:unit/terrain unit)]\n    (or (can-move? db game unit)\n        (and (can-attack? db game unit)\n             (> (count (enemies-in-range db game unit)) 0))\n        (can-repair? db game unit)\n        (can-capture? db game unit terrain))))\n\n(defn move-actions [db game unit]\n  (if (can-move? db game unit)\n    (map (fn [move]\n           (let [[to-q to-r] (:to move)\n                 [from-q from-r] (:from move)]\n             (-> move\n                 (dissoc :to :from)\n                 (assoc :action/type :action.type/move-unit\n                        :action/to-q to-q\n                        :action/to-r to-r\n                        :action/from-q from-q\n                        :action/from-r from-r))))\n         (valid-moves db game unit))\n    []))\n\n(defn attack-actions [db game unit]\n  (if (can-attack? db game unit)\n    (map (fn [defender]\n           {:action/type :action.type/attack-unit\n            :action/attacker-q (:unit/q unit)\n            :action/attacker-r (:unit/r unit)\n            :action/defender-q (:unit/q defender)\n            :action/defender-r (:unit/r defender)})\n         (enemies-in-range db game unit))\n    []))\n\n(defn repair-actions [db game unit]\n  (if (can-repair? db game unit)\n    [{:action/type :action.type/repair-unit\n      :action/q (:unit/q unit)\n      :action/r (:unit/r unit)}]\n    []))\n\n(defn field-repair-actions [db game unit]\n  (if (can-field-repair? db game unit)\n    (map (fn [target]\n           {:action/type :action.type/field-repair-unit\n            :action/repairer-q (:unit/q unit)\n            :action/repairer-r (:unit/r unit)\n            :action/target-q (:unit/q target)\n            :action/target-r (:unit/r target)})\n         (friends-in-range db game unit))\n    []))\n\n(defn capture-actions [db game unit]\n  (let [{:keys [unit/q unit/r unit/terrain]} unit]\n    (if (can-capture? db game unit terrain)\n      [{:action/type :action.type/capture-base\n        :action/q q\n        :action/r r}]\n      [])))\n\n(defn unit-actions [db game unit]\n  (concat (move-actions db game unit)\n          (attack-actions db game unit)\n          (repair-actions db game unit)\n          (field-repair-actions db game unit)\n          (capture-actions db game unit)))\n\n(defn actionable-units [db game]\n  (let [units (get-in game [:game/current-faction :faction/units])]\n    (filter #(unit-can-act? db game %) units)))\n\n(defn actionable-actors [db game]\n  (concat (actionable-units db game)\n          (actionable-bases db game)))\n\n(defn capturable-bases [db game unit]\n  (when (get-in unit [:unit/type :unit-type/can-capture])\n    (let [u-faction (unit-faction db unit)]\n      (into []\n            (filter #(not= u-faction (:terrain/owner %)))\n            (qess '[:find ?t\n                    :in $ ?g\n                    :where\n                    [?g  :game/map ?m]\n                    [?m  :map/terrains ?t]\n                    [?t  :terrain/type ?tt]\n                    [?tt :terrain-type/id :terrain-type.id/base]]\n                  db (e game))))))\n\n(defn closest-capturable-base [db game unit]\n  (let [unit-q (:unit/q unit)\n        unit-r (:unit/r unit)\n        bases (capturable-bases db game unit)]\n    (reduce\n     (fn [closest terrain]\n       (let [terrain-q (:terrain/q terrain)\n             terrain-r (:terrain/r terrain)\n             closest-q (:terrain/q closest)\n             closest-r (:terrain/r closest)]\n         (if (< (hex/distance unit-q unit-r terrain-q terrain-r)\n                (hex/distance unit-q unit-r closest-q closest-r))\n           terrain\n           closest)))\n     bases)))\n\n;; TODO: return nil if no move is found\n(defn closest-move-to-hex [db game unit q r]\n  (reduce\n   (fn [closest move]\n     (if closest\n       (let [[closest-q closest-r] (:to closest)\n             [move-q move-r] (:to move)]\n         (if (< (hex/distance move-q move-r q r)\n                (hex/distance closest-q closest-r q r))\n           move\n           closest))\n       move))\n   (valid-moves db game unit)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Setup\n\n(defn settings-tx [db game-id settings-def]\n  (let [game (game-by-id db game-id)]\n    [{:db/id (e game)\n      :game/ranged-attack-bonus (:ranged-attack-bonus settings-def)\n      :game/adjacent-attack-bonus (:adjacent-attack-bonus settings-def)\n      :game/flanking-attack-bonus (:flanking-attack-bonus settings-def)\n      :game/opposite-attack-bonus (:opposite-attack-bonus settings-def)\n      :game/stochastic-damage (:stochastic-damage settings-def)\n      :game/self-repair (:self-repair settings-def)\n      :game/move-through-friendly (:move-through-friendly settings-def)}]))\n\n(defn terrain-types-tx [db game-id terrains-def]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (map\n           (fn [[terrain-type-name terrain-def]]\n             (let [terrain-type-id (to-terrain-type-id terrain-type-name)\n                   terrain-type-idx (game-id-idx game-id terrain-type-id)]\n               {:db/id (db/next-temp-id)\n                :game/_terrain-types (e game)\n                :terrain-type/id terrain-type-id\n                :terrain-type/game-id-idx terrain-type-idx\n                :terrain-type/description (:description terrain-def)\n                :terrain-type/image (:image terrain-def)})))\n          terrains-def)))\n\n(defn attack-strengths-tx [db game-id unit-type-eid attack-strengths-def]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (map\n           (fn [[armor-type-name attack-strength]]\n             {:db/id (db/next-temp-id)\n              :unit-type/_strengths unit-type-eid\n              :unit-strength/armor-type (to-armor-type armor-type-name)\n              :unit-strength/attack attack-strength}))\n          attack-strengths-def)))\n\n(defn terrain-effects-tx [db game-id unit-type-eid terrain-effects-def]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (map\n           (fn [[terrain-type-name terrain-effect-def]]\n             (let [{:keys [attack-bonus armor-bonus movement-cost]} terrain-effect-def\n                   terrain-type-idx (->> terrain-type-name to-terrain-type-id (game-id-idx game-id))]\n               {:db/id (db/next-temp-id)\n                :terrain-type/_effects [:terrain-type/game-id-idx terrain-type-idx]\n                :terrain-effect/unit-type unit-type-eid\n                :terrain-effect/movement-cost movement-cost\n                :terrain-effect/attack-bonus attack-bonus\n                :terrain-effect/armor-bonus armor-bonus})))\n          terrain-effects-def)))\n\n(defn terrain-build-tx [db game-id unit-type-eid buildable-at-def]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (map\n           (fn [terrain-type-name]\n             (let [terrain-type-idx (->> terrain-type-name to-terrain-type-id (game-id-idx game-id))]\n               {:db/id [:terrain-type/game-id-idx terrain-type-idx]\n                :terrain-type/can-build unit-type-eid})))\n          buildable-at-def)))\n\n(defn unit-types-tx [db game-id units-def]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (mapcat\n           (fn [[unit-type-name unit-def]]\n             (let [{:keys [armor capturing-armor state-map]} unit-def\n                   unit-state-map-idx (->> state-map to-unit-state-map-id (game-id-idx game-id))\n                   unit-type-eid (db/next-temp-id)]\n               (-> [{:db/id unit-type-eid\n                     :game/_unit-types (e game)\n                     :unit-type/id (to-unit-type-id unit-type-name)\n                     :unit-type/description (:description unit-def)\n                     :unit-type/cost (:cost unit-def)\n                     :unit-type/can-capture (:can-capture unit-def)\n                     :unit-type/can-repair (map #(to-armor-type %) (:can-repair unit-def))\n                     :unit-type/movement (:movement unit-def)\n                     :unit-type/min-range (:min-range unit-def)\n                     :unit-type/max-range (:max-range unit-def)\n                     :unit-type/armor-type (-> unit-def :armor-type to-armor-type)\n                     :unit-type/armor armor\n                     :unit-type/capturing-armor (or capturing-armor armor)\n                     :unit-type/repair (:repair unit-def)\n                     :unit-type/state-map [:unit-state-map/game-id-idx unit-state-map-idx]\n                     :unit-type/image (:image unit-def)\n                     :unit-type/zoc-armor-types (map #(to-armor-type %) (:zoc unit-def))}]\n                   (into (attack-strengths-tx db game-id unit-type-eid (:attack-strengths unit-def)))\n                   (into (terrain-effects-tx db game-id unit-type-eid (:terrain-effects unit-def)))\n                   (into (terrain-build-tx db game-id unit-type-eid (:buildable-at unit-def)))))))\n          units-def)))\n\n(defn unit-states-tx [db game-id state-map-eid state-map-name states]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (map\n           (fn [[state-name states]]\n             (let [state-id (to-unit-state-id state-map-name state-name)\n                   state-idx (game-id-idx game-id state-id)]\n               {:db/id (db/next-temp-id)\n                :game/_unit-states (e game)\n                :unit-state/id state-id\n                :unit-state/game-id-idx state-idx\n                :unit-state-map/_states state-map-eid})))\n          states)))\n\n(defn unit-states-transitions-tx [db game-id state-map-name states]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (mapcat\n           (fn [[state-name {:keys [transitions]}]]\n             (map\n              (fn [[action new-state]]\n                (let [state-idx (->> state-name (to-unit-state-id state-map-name) (game-id-idx game-id))\n                      new-state-idx (->> new-state (to-unit-state-id state-map-name) (game-id-idx game-id))]\n                  {:db/id (db/next-temp-id)\n                   :unit-state-transition/action-type (to-action-type action)\n                   :unit-state-transition/new-state [:unit-state/game-id-idx new-state-idx]\n                   :unit-state/_transitions [:unit-state/game-id-idx state-idx]}))\n              transitions)))\n          states)))\n\n(defn unit-state-map-tx [db game-id state-maps-def]\n  (let [game (game-by-id db game-id)]\n    (into []\n          (mapcat\n           (fn [[state-map-name state-map-def]]\n             (let [{:keys [states start-state built-state]} state-map-def\n                   map-id (to-unit-state-map-id state-map-name)\n                   map-idx (game-id-idx game-id map-id)\n                   start-idx (->> start-state (to-unit-state-id state-map-name) (game-id-idx game-id))\n                   built-idx (->> built-state (to-unit-state-id state-map-name) (game-id-idx game-id))\n                   state-map-temp-eid (db/next-temp-id)]\n               (-> [{:db/id state-map-temp-eid\n                     :game/_unit-state-maps (e game)\n                     :unit-state-map/id map-id\n                     :unit-state-map/game-id-idx map-idx}]\n                   (into (unit-states-tx db game-id state-map-temp-eid state-map-name states))\n                   (into (unit-states-transitions-tx db game-id state-map-name states))\n                   (into [{:db/id state-map-temp-eid\n                           :unit-state-map/start-state [:unit-state/game-id-idx start-idx]\n                           :unit-state-map/built-state [:unit-state/game-id-idx built-idx]}])))))\n          state-maps-def)))\n\n(defn game-map-tx [db game-id map-def]\n  (let [game (game-by-id db game-id)\n        map-eid (db/next-temp-id)]\n    (into [{:db/id map-eid\n            :map/id (:id map-def)\n            :map/description (:description map-def)\n            :game/_map (e game)}]\n          (map\n           (fn [t]\n             (let [{:keys [q r]} t\n                   terrain-type-id (to-terrain-type-id (:terrain-type t))\n                   terrain-type (terrain-type-by-id db game terrain-type-id)]\n               {:db/id (db/next-temp-id)\n                :terrain/game-pos-idx (game-pos-idx game q r)\n                :terrain/q q\n                :terrain/r r\n                :terrain/type (e terrain-type)\n                :map/_terrains map-eid}))\n           (:terrains map-def)))))\n\n(defn create-game!\n  ([conn scenario-def]\n   (create-game! conn scenario-def {}))\n  ([conn scenario-def game-state]\n   (let [game-id (random-uuid)\n         {:keys [id credits-per-base max-count-per-unit]} scenario-def]\n     (d/transact! conn [{:db/id (db/next-temp-id)\n                         :game/id game-id\n                         :game/scenario-id id\n                         :game/round (:round game-state 1)\n                         :game/max-count-per-unit max-count-per-unit\n                         :game/credits-per-base credits-per-base}])\n     game-id)))\n\n(defn bases-tx [db game-id scenario-def]\n  (let [game (game-by-id db game-id)]\n    (for [base (:bases scenario-def)]\n      (let [{:keys [q r base-type]} base]\n        {:terrain/game-pos-idx (game-pos-idx game q r)\n         :terrain/q q\n         :terrain/r r\n         :terrain/type [:terrain-type/game-id-idx (game-id-idx game-id (to-terrain-type-id base-type))]\n         :map/_terrains (e (:game/map game))}))))\n\n(defn factions-tx [db game-id factions]\n  (let [game (game-by-id db game-id)]\n    (map-indexed (fn [i faction]\n                   (let [{:keys [color credits ai]} faction\n                         next-id (- -101 (mod (inc i) (count factions)))]\n                     {:db/id (- -101 i)\n                      :faction/color (to-faction-color color)\n                      :faction/credits credits\n                      :faction/ai ai\n                      :faction/order (inc i)\n                      :faction/next-faction next-id\n                      :game/_factions (e game)}))\n                 factions)))\n\n(defn factions-bases-tx [db game-id factions]\n  (let [game (game-by-id db game-id)]\n    (mapcat (fn [faction]\n              (let [{:keys [bases color]} faction\n                    faction (faction-by-color db game color)]\n                (map (fn [{:keys [q r]}]\n                       {:terrain/game-pos-idx (game-pos-idx game q r)\n                        :terrain/owner (e faction)})\n                     bases)))\n            factions)))\n\n(defn factions-units-tx [db game-id factions]\n  (let [game (game-by-id db game-id)]\n    (mapcat (fn [faction]\n              (let [{:keys [game/max-count-per-unit]} game\n                    {:keys [units color]} faction\n                    faction-eid (e (faction-by-color db game color))]\n                (map\n                 (fn [{:keys [q r] :as unit}]\n                   (let [unit-type-id (to-unit-type-id (:unit-type unit))\n                         unit-state (:state unit)\n                         unit-type (find-by db :unit-type/id unit-type-id)\n                         capturing (:capturing unit false)\n                         terrain (terrain-at db game q r)]\n                     (cond-> {:db/id (db/next-temp-id)\n                              :unit/game-pos-idx (game-pos-idx game q r)\n                              :unit/q q\n                              :unit/r r\n                              :unit/terrain (e terrain)\n                              :unit/count (:count unit max-count-per-unit)\n                              :unit/round-built (:round-built unit 0)\n                              :unit/move-count (:move-count unit 0)\n                              :unit/attack-count (:attack-count unit 0)\n                              :unit/attacked-count (:attack-count unit 0)\n                              :unit/repaired (:repaired unit false)\n                              :unit/capturing capturing\n                              :unit/type (e unit-type)\n                              :unit/state (if unit-state\n                                            [:unit-state/game-id-idx (->> unit-state to-unit-state-id (game-id-idx game-id))]\n                                            (-> unit-type start-state e))\n                              :faction/_units faction-eid}\n                       capturing\n                       (assoc :unit/capture-round (:capture-round unit)))))\n                 units)))\n            factions)))\n\n(defn load-scenario! [conn rulesets map-defs scenario-def]\n  (let [game-id (create-game! conn scenario-def)\n        {:keys [ruleset-id map-id credits-per-base factions]} scenario-def\n        starting-faction-color (get-in factions [0 :color])\n        ruleset (rulesets ruleset-id)]\n    ;; Rules\n    (d/transact! conn (settings-tx @conn game-id (:settings ruleset)))\n    (d/transact! conn (terrain-types-tx @conn game-id (:terrains ruleset)))\n    (d/transact! conn (unit-state-map-tx @conn game-id (:unit-state-maps ruleset)))\n    (d/transact! conn (unit-types-tx @conn game-id (:units ruleset)))\n\n    ;; Map and bases\n    (d/transact! conn (game-map-tx @conn game-id (map-defs map-id)))\n    (d/transact! conn (bases-tx @conn game-id scenario-def))\n\n    ;; Factions\n    (d/transact! conn (factions-tx @conn game-id factions))\n    (d/transact! conn (factions-bases-tx @conn game-id factions))\n    (d/transact! conn (factions-units-tx @conn game-id factions))\n    (let [db @conn\n          game (game-by-id db game-id)\n          starting-faction-eid (->> starting-faction-color\n                                    (faction-by-color db game)\n                                    e)]\n      (d/transact! conn [{:db/id (e game)\n                          :game/starting-faction starting-faction-eid\n                          :game/current-faction starting-faction-eid}]))\n    game-id))\n\n;; Info needed to load game from URL\n;; - scenario id\n;; - current-faction\n;; - faction\n;;   - credits\n;;   - ai status\n;;   - owned bases\n;;     - location\n;;   - units\n;;     - location\n;;     - health\n;;     - round-built\n;;     - capturing\n\n(defn load-game-state! [conn rulesets map-defs scenario-defs game-state]\n  (let [{:keys [scenario-id factions]} game-state\n        current-faction-color (:current-faction game-state)\n        scenario-def (scenario-defs scenario-id)\n        starting-faction-color (get-in scenario-def [:factions 0 :color])\n        game-id (create-game! conn scenario-def game-state)\n        {:keys [ruleset-id map-id credits-per-base]} scenario-def\n        ruleset (rulesets ruleset-id)]\n    ;; Rules\n    (d/transact! conn (settings-tx @conn game-id (:settings ruleset)))\n    (d/transact! conn (terrain-types-tx @conn game-id (:terrains ruleset)))\n    (d/transact! conn (unit-state-map-tx @conn game-id (:unit-state-maps ruleset)))\n    (d/transact! conn (unit-types-tx @conn game-id (:units ruleset)))\n\n    ;; Map and bases\n    (d/transact! conn (game-map-tx @conn game-id (map-defs map-id)))\n    (d/transact! conn (bases-tx @conn game-id scenario-def))\n\n    ;; Factions\n    (d/transact! conn (factions-tx @conn game-id factions))\n    (d/transact! conn (factions-bases-tx @conn game-id factions))\n    (d/transact! conn (factions-units-tx @conn game-id factions))\n    (let [db @conn\n          game (game-by-id db game-id)\n          starting-faction-eid (e (faction-by-color db game starting-faction-color))\n          current-faction-eid (e (faction-by-color db game current-faction-color))]\n      (d/transact! conn [{:db/id (e game)\n                          :game/starting-faction starting-faction-eid\n                          :game/current-faction current-faction-eid}]))\n    game-id))\n\n;; TODO: figure out better name\n(defn get-game-state\n  ([db game]\n   (get-game-state db game :minimal))\n  ([db game dump-type]\n   (let [factions (->> game :game/factions (sort-by :order))]\n     {:scenario-id (:game/scenario-id game)\n      :round (:game/round game)\n      :current-faction (-> game\n                           :game/current-faction\n                           :faction/color\n                           name\n                           keyword)\n      :factions\n      (into []\n            (for [faction factions]\n              {:credits (:faction/credits faction)\n               :ai (:faction/ai faction)\n               :color (-> (:faction/color faction)\n                          name\n                          keyword)\n               :bases\n               (into []\n                     (for [base (faction-bases db faction)]\n                       {:q (:terrain/q base)\n                        :r (:terrain/r base)}))\n               :units\n               (into []\n                     (for [unit (:faction/units faction)]\n                       (cond-> {:q (:unit/q unit)\n                                :r (:unit/r unit)\n                                :unit-type (-> unit\n                                               :unit/type\n                                               :unit-type/id\n                                               name\n                                               keyword)\n                                :count (:unit/count unit)}\n\n                         (:unit/capturing unit)\n                         (assoc :capturing true\n                                :capture-round (:unit/capture-round unit))\n\n                         (= dump-type :full)\n                         (assoc :attack-count (:unit/attack-count unit)\n                                :move-count (:unit/move-count unit)\n                                :repaired (:unit/repaired unit)\n                                :round-built (:unit/round-built unit)\n                                :state (-> unit\n                                           :unit/state\n                                           :unit-state/id\n                                           name\n                                           keyword))\n                         )))\n               }))\n      })))\n"
  },
  {
    "path": "src/cljs/zetawar/js/game.cljs",
    "content": "(ns zetawar.js.game\n  (:require\n   [zetawar.game :as game]))\n\n(defn ^:export is_unit [x]\n  (game/unit? x))\n\n(defn ^:export is_base [x]\n  (game/base? x))\n\n(defn ^:export terrain_hex [terrain]\n  (clj->js (game/terrain-hex terrain)))\n\n(defn ^:export unit_hex [terrain]\n  (clj->js (game/unit-hex terrain)))\n\n(defn ^:export closest_capturable_base [db game unit]\n  (game/closest-capturable-base db game unit))\n\n(defn ^:export closest_enemy [db game unit]\n  (game/closest-enemy db game unit))\n"
  },
  {
    "path": "src/cljs/zetawar/js/hex.cljs",
    "content": "(ns zetawar.js.hex\n  (:require\n   [zetawar.hex :as hex]))\n\n(defn ^:export distance [q1 r1 q2 r2]\n  (hex/distance q1 r1 q2 r2))\n"
  },
  {
    "path": "src/cljs/zetawar/logging.cljs",
    "content": "(ns zetawar.logging)\n\n(goog-define LOG_LEVEL 4)\n\n(defn log [& args]\n  (let [[ex & other-args] args]\n    (if (.-stack ex)\n      (do\n        (js/console.log (apply str (interpose \" \" other-args)))\n        (js/console.log ex))\n      (js/console.log (apply str (interpose \" \" args))))))\n\n(defn error [& args]\n  (when (>= LOG_LEVEL 0)\n    (apply log args)))\n\n(defn warn [& args]\n  (when (>= LOG_LEVEL 1)\n    (apply log args)))\n\n(defn info [& args]\n  (when (>= LOG_LEVEL 2)\n    (apply log args)))\n\n(defn debug [& args]\n  (when (>= LOG_LEVEL 3)\n    (apply log args)))\n\n(defn trace [& args]\n  (when (>= LOG_LEVEL 4)\n    (apply log args)))\n"
  },
  {
    "path": "src/cljs/zetawar/players/ai/custom.cljs",
    "content": "(ns zetawar.players.ai.custom\n  (:require\n   [cljs.core.async :as async]\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.db :as db]\n   [zetawar.game :as game]\n   [zetawar.hex :as hex]\n   [zetawar.logging :as log]\n   [zetawar.players :as players]\n   [zetawar.players.simple-embedded :refer [simple-embedded-player]]))\n\n(defn score-actor [db game actor actor-ctx]\n  (cond\n    (game/unit? actor) (rand-int 100)\n    (game/base? actor) (+ (rand-int 100) 100)))\n\n(defn score-base-action [db game base action-ctx action]\n  (rand-int 200))\n\n(defn mk-unit-action-ctx [db game actor-ctx unit]\n  (assoc actor-ctx\n         :closest-base (game/closest-capturable-base db game unit)\n         :closest-enemy (game/closest-enemy db game unit)))\n\n(defn score-unit-action [db game unit action-ctx action]\n  (let [{:keys [closest-base closest-enemy]} action-ctx]\n    (case (:action/type action)\n      :action.type/capture-base\n      200\n\n      :action.type/attack-unit\n      100\n\n      :action.type/move-unit\n      (if closest-base\n        (let [[base-q base-r] (game/terrain-hex closest-base)\n              {:keys [action/to-q action/to-r]} action\n              base-distance (hex/distance base-q base-r to-q to-r)]\n          (- 100 base-distance))\n        (let [[enemy-q enemy-r] (game/unit-hex closest-enemy)\n              {:keys [action/to-q action/to-r]} action\n              enemy-distance (hex/distance enemy-q enemy-r to-q to-r)]\n          (- 100 enemy-distance)))\n\n      0)))\n\n(defmethod players/new-player ::players/custom-ai\n  [{:as app-ctx :keys [ev-chan notify-pub]} player-type faction-color]\n  (let [fns {:score-actor #'score-actor\n             :score-base-action #'score-base-action\n             :mk-unit-action-ctx #'mk-unit-action-ctx\n             :score-unit-action #'score-unit-action}]\n    (simple-embedded-player faction-color\n                            ev-chan\n                            notify-pub\n                            fns)))\n"
  },
  {
    "path": "src/cljs/zetawar/players/ai/custom_js.cljs",
    "content": "(ns zetawar.players.ai.custom-js\n  (:require\n   [cljs.core.async :as async]\n   [datascript.core :as d]\n   [zetawar-js-ai]\n   [zetawar.app :as app]\n   [zetawar.db :as db]\n   [zetawar.game :as game]\n   [zetawar.hex :as hex]\n   [zetawar.logging :as log]\n   [zetawar.players :as players]\n   [zetawar.players.simple-embedded :refer [simple-embedded-player]]))\n\n(defn mk-actor-ctx [db game actor]\n  (js/ZetawarAI.makeActorContext db game actor))\n\n(defn score-actor [db game actor actor-ctx]\n  (js/ZetawarAI.scoreActor db game actor actor-ctx))\n\n(defn mk-base-action-ctx [db game actor-ctx base]\n  (js/ZetawarAI.makeBaseActionContext db game actor-ctx base))\n\n(defn score-base-action [db game base action-ctx action]\n  (js/ZetawarAI.scoreBaseAction db game base action-ctx (clj->js action)))\n\n(defn mk-unit-action-ctx [db game actor-ctx unit]\n  (js/ZetawarAI.makeUnitActionContext db game actor-ctx unit))\n\n(defn score-unit-action [db game unit action-ctx action]\n  (js/ZetawarAI.scoreUnitAction db game unit action-ctx (clj->js action)))\n\n(defmethod players/new-player ::players/custom-js-ai\n  [{:as app-ctx :keys [ev-chan notify-pub]} player-type faction-color]\n  (let [fns {:mk-actor-ctx #'mk-actor-ctx\n             :score-actor #'score-actor\n             :mk-base-action-ctx #'mk-base-action-ctx\n             :score-base-action #'score-base-action\n             :mk-unit-action-ctx #'mk-unit-action-ctx\n             :score-unit-action #'score-unit-action}]\n    (simple-embedded-player faction-color\n                            ev-chan\n                            notify-pub\n                            fns)))\n"
  },
  {
    "path": "src/cljs/zetawar/players/ai/reference.cljs",
    "content": "(ns zetawar.players.ai.reference\n  (:require\n   [cljs.core.async :as async]\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.db :as db]\n   [zetawar.game :as game]\n   [zetawar.hex :as hex]\n   [zetawar.logging :as log]\n   [zetawar.players :as players]\n   [zetawar.players.simple-embedded :refer [simple-embedded-player]]))\n\n(defn score-actor [db game actor actor-ctx]\n  (cond\n    (game/unit? actor) (rand-int 100)\n    (game/base? actor) (+ (rand-int 100) 100)))\n\n(defn score-base-action [db game base action-ctx action]\n  (rand-int 200))\n\n(defn mk-unit-action-ctx [db game actor-ctx unit]\n  (assoc actor-ctx\n         :closest-base (game/closest-capturable-base db game unit)\n         :closest-enemy (game/closest-enemy db game unit)))\n\n(defn score-unit-action [db game unit action-ctx action]\n  (let [{:keys [closest-base closest-enemy]} action-ctx]\n    (case (:action/type action)\n      :action.type/capture-base\n      200\n\n      :action.type/attack-unit\n      100\n\n      :action.type/move-unit\n      (if closest-base\n        (let [[base-q base-r] (game/terrain-hex closest-base)\n              {:keys [action/to-q action/to-r]} action\n              base-distance (hex/distance base-q base-r to-q to-r)]\n          (- 100 base-distance))\n        (let [[enemy-q enemy-r] (game/unit-hex closest-enemy)\n              {:keys [action/to-q action/to-r]} action\n              enemy-distance (hex/distance enemy-q enemy-r to-q to-r)]\n          (- 100 enemy-distance)))\n\n      0)))\n\n(defmethod players/new-player ::players/reference-ai\n  [{:as app-ctx :keys [ev-chan notify-pub]} player-type faction-color]\n  (let [fns {:score-actor #'score-actor\n             :score-base-action #'score-base-action\n             :mk-unit-action-ctx #'mk-unit-action-ctx\n             :score-unit-action #'score-unit-action}]\n    (simple-embedded-player faction-color\n                            ev-chan\n                            notify-pub\n                            fns)))\n"
  },
  {
    "path": "src/cljs/zetawar/players/human.cljs",
    "content": "(ns zetawar.players.human\n  (:require\n   [zetawar.players :as players]))\n\n(defrecord HumanPlayer [faction-color]\n  players/Player\n  (start [player])\n  (stop [player]))\n\n(defmethod players/new-player ::players/human\n  [_ _ faction-color]\n  (HumanPlayer. faction-color))\n"
  },
  {
    "path": "src/cljs/zetawar/players/simple_embedded.cljs",
    "content": "(ns zetawar.players.simple-embedded\n  (:require\n   [cljs.core.async :as async]\n   [datascript.core :as d]\n   [zetawar.logging :as log]\n   [zetawar.app :as app]\n   [zetawar.db :as db]\n   [zetawar.game :as game]\n   [zetawar.players :as players]\n   [zetawar.router :as router])\n  (:require-macros\n   [cljs.core.async.macros :refer [go go-loop]]))\n\n(defmulti handle-event (fn [player [ev-type & _]] ev-type))\n\n(defmethod handle-event :default\n  [_ msg]\n  (log/debug \"Unhandled player event:\" (pr-str msg)))\n\n(defn handle-event* [{:as player :keys [ev-chan]} msg]\n  (let [{:as ret :keys [tx]} (handle-event player msg)]\n    (log/trace \"Player handler returned:\" (pr-str ret))\n    (doseq [new-msg (:dispatch ret)]\n      (router/dispatch ev-chan new-msg))))\n\n(defrecord SimpleEmbeddedPlayer [faction-color ev-chan notify-pub player-chan conn fns]\n  players/Player\n  (start [player]\n    (let [{:keys [notify-pub]} player]\n      (async/sub notify-pub :faction.color/all player-chan)\n      (async/sub notify-pub faction-color player-chan)\n      (go-loop [msg (<! player-chan)]\n        (when msg\n          (log/debug (str \"[\" faction-color \"]\") \"Handling player event:\"  (pr-str msg))\n          ;; TODO: validate event\n          ;; TODO: validate handler return value\n          ;; TODO: catch exceptions\n          (handle-event* player msg)\n          (recur (<! player-chan))))))\n  (stop [player]\n    (async/close! player-chan)))\n\n(defn simple-embedded-player [faction-color ev-chan notify-pub fns]\n  (let [player-chan (async/chan (async/dropping-buffer 10))\n        conn (d/create-conn db/schema)]\n    (SimpleEmbeddedPlayer. faction-color ev-chan notify-pub player-chan conn fns)))\n\n(defmethod handle-event ::players/start-turn\n  [{:as player :keys [faction-color]} _]\n  {:dispatch [[:zetawar.events.player/send-game-state faction-color]]})\n\n;; TODO: apply actions incrementally instead of loading complete state every time\n(defmethod handle-event ::players/apply-action\n  [{:as player :keys [db faction-color]} [_ _ action]]\n  (when (and (= faction-color (:action/faction-color action))\n             (not= (:action/type action) :action.type/end-turn))\n    {:dispatch [[:zetawar.events.player/send-game-state faction-color]]}))\n\n(declare ^:dynamic *mk-actor-ctx*)\n\n(declare ^:dynamic *score-actor*)\n\n(defn choose-actor [db game ctx]\n  (->> (game/actionable-actors db game)\n       (keep (juxt identity #(*score-actor* db game ctx %)))\n       (apply max-key second)\n       first))\n\n(declare ^:dynamic *mk-base-action-ctx*)\n\n(declare ^:dynamic *score-base-action*)\n\n(defn choose-base-action [db game base action-ctx]\n  (->> (game/base-actions db game base)\n       (keep (juxt identity #(*score-base-action* db game base action-ctx %)))\n       (apply max-key second)\n       first))\n\n(declare ^:dynamic *mk-unit-action-ctx*)\n\n(declare ^:dynamic *score-unit-action*)\n\n(defn choose-unit-action [db game unit action-ctx]\n  (->> (game/unit-actions db game unit)\n       (keep (juxt identity #(*score-unit-action* db game unit action-ctx %)))\n       (apply max-key second)\n       first))\n\n(defn choose-action [player db game]\n  (let [actor-ctx (*mk-actor-ctx* db game)\n        actor (choose-actor db game actor-ctx)]\n    (when actor\n      (cond\n        (game/base? actor)\n        (let [action-ctx (*mk-base-action-ctx* db game actor-ctx actor)]\n          (choose-base-action db game actor action-ctx))\n\n        (game/unit? actor)\n        (let [action-ctx (*mk-unit-action-ctx* db game actor-ctx actor)]\n          (choose-unit-action db game actor action-ctx))))))\n\n(defn wrap-exception-handler [f]\n  (fn [& args]\n    (try\n      (apply f args)\n      (catch :default ex\n        (log/error \"Error running player function:\" ex)\n        nil))))\n\n(defmethod handle-event ::players/update-game-state\n  [{:as player :keys [conn faction-color fns]} [_ _ game-state]]\n  (let [new-conn (d/create-conn db/schema)\n        game-id (players/load-player-game-state! new-conn game-state)]\n    (reset! conn @new-conn)\n    (let [db @conn\n          game (game/game-by-id db game-id)\n          {:keys [mk-actor-ctx score-actor\n                  mk-base-action-ctx score-base-action\n                  mk-unit-action-ctx score-unit-action]} fns\n          action (or (binding [*mk-actor-ctx* (wrap-exception-handler (or mk-actor-ctx (constantly nil)))\n                               *score-actor* (wrap-exception-handler score-actor)\n                               *mk-base-action-ctx* (wrap-exception-handler (or mk-base-action-ctx (fn [db game actor-ctx base] actor-ctx)))\n                               *score-base-action* (wrap-exception-handler score-base-action)\n                               *mk-unit-action-ctx* (wrap-exception-handler (or mk-unit-action-ctx (fn [db game actor-ctx unit] actor-ctx)))\n                               *score-unit-action* (wrap-exception-handler score-unit-action)]\n                       (choose-action player db game))\n                     {:action/type :action.type/end-turn\n                      :action/faction-color faction-color})]\n      {:dispatch [[:zetawar.events.player/execute-action action]]})))\n"
  },
  {
    "path": "src/cljs/zetawar/players.cljs",
    "content": "(ns zetawar.players\n  (:require\n   [cljs.core.async :as async]\n   [zetawar.data :as data]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]))\n\n;; TODO: move to data ns?\n;; TODO: namespace keys?\n(def player-types [[::human        {:description \"Human\"\n                                    :ai          false}]\n                   [::reference-ai {:description \"Reference AI\"\n                                    :ai          true}]\n                   [::custom-ai    {:description \"Custom AI\"\n                                    :ai          true}]\n                   [::custom-js-ai {:description \"Custom JavaScript AI\"\n                                    :ai          true}]])\n\n(def player-types-by-id\n  (into {} player-types))\n\n(defprotocol Player\n  (start [player])\n  (stop [player]))\n\n(defmulti new-player (fn [player-ctx player-type faction-color] player-type))\n\n(defn notify [notify-chan msg]\n  (log/debug \"Notifying player:\" (pr-str msg))\n  (async/put! notify-chan msg))\n\n(defn load-player-game-state! [conn game-state]\n  (game/load-game-state! conn\n                         data/rulesets\n                         data/maps\n                         data/scenarios\n                         game-state))\n\n;; requests\n;; - get-state\n;; - move\n;; - attack\n;; - capture\n;; - build\n;; - end-turn\n\n;; events\n;; - start-turn\n;; - game-state\n\n;; events structure\n;; - event-type\n\n;; request response notification\n;; - request-id\n;; - response or error\n"
  },
  {
    "path": "src/cljs/zetawar/router/reagent.cljs",
    "content": "(ns zetawar.router.reagent\n  (:require\n   [cljs.core.async :refer [<! >! chan offer!]]\n   [reagent.core :as r]\n   [zetawar.logging :as log])\n  (:require-macros\n   [cljs.core.async.macros :refer [go]]))\n\n;; TODO: consider alternative names (event-wrapper-fn?)\n(defn handler-wrapper-fn [{:as router-ctx :keys [max-render-interval]} event-handler]\n  (let [timer-start (atom -1)\n        last-render (atom 0)\n        render-queued? (atom false)\n        render-chan (chan 1)]\n    (fn reagent-handler-wrapper [event-handler]\n      (go\n        ;; Reset render timer if a render just occurred\n        (when (< @timer-start @last-render)\n          (let [now (.getTime (js/Date.))]\n            (reset! timer-start now)\n            (log/trace \"@timer-start:\" @timer-start)))\n\n        ;; Queue notification of render\n        (when-not @render-queued?\n          (r/next-tick #(let [now (.getTime (js/Date.))]\n                          (log/trace \"Rendering...\")\n                          (offer! render-chan :rendered)\n                          (when (> now @last-render)\n                            (reset! last-render now)\n                            (log/trace \"@last-render:\" @last-render)\n                            (reset! render-queued? false)))))\n\n        (event-handler)\n\n        ;; Block till render if max-render-interval has been exceeded\n        (let [since-last-render (- (.getTime (js/Date.)) @timer-start)]\n          (log/trace \"since-last-render:\" since-last-render)\n          (when (> since-last-render max-render-interval)\n            (log/trace \"Blocking till next render...\")\n            (<! render-chan)\n            (log/trace \"Render completed; unblocking\")))))))\n"
  },
  {
    "path": "src/cljs/zetawar/router.cljs",
    "content": "(ns zetawar.router\n  (:require\n   [cljs.core.async :refer [<! >! chan offer!]]\n   [datascript.core :as d]\n   [goog.object :as gobj]\n   [zetawar.logging :as log]\n   [zetawar.players :as players])\n  (:require-macros\n   [cljs.core.async.macros :refer [go go-loop]]))\n\n(when-not (exists? js/Raven)\n  (let [stub #js {\"captureMessage\" #()\n                  ;; TODO: something better than js/console.trace\n                  \"captureException\" #(js/console.trace)}\n        global (if (exists? js/window) js/window js/global)]\n    (gobj/set global \"Raven\" stub)))\n\n(defmulti handle-event (fn [ev-ctx [ev-type & _]] ev-type))\n\n(defmethod handle-event :default\n  [_ msg]\n  (let [log-msg (str \"Unhandled event: \" (pr-str msg))]\n    (js/Raven.captureMessage log-msg)\n    (log/warn log-msg))\n  nil)\n\n(defn dispatch [ch msg]\n  (if msg\n    (if (offer! ch msg)\n      (log/info \"Dispatching event:\" (pr-str msg))\n      (let [log-msg (str \"Failed to dispatch event (buffer full?): \" (pr-str msg))]\n        (js/Raven.captureMessage log-msg)\n        (log/error log-msg)))\n    (let [log-msg \"Unable to dispatch 'nil' event message\"]\n      (js/Raven.captureMessage log-msg)\n      (log/error log-msg))))\n\n(defn handle-event* [{:as router-ctx :keys [conn ev-chan notify-chan]} msg]\n  (let [ev-ctx (assoc router-ctx :db @conn)\n        {:as ret :keys [tx]} (handle-event ev-ctx msg)]\n    (log/trace \"Handler returned:\" (pr-str ret))\n    (when tx\n      (log/debug \"Transacting:\" (pr-str tx))\n      (d/transact! conn tx))\n    (doseq [new-msg (:dispatch ret)]\n      (dispatch ev-chan new-msg))\n    ;; TODO: block with timeout when notifying players\n    (doseq [notify-msg (:notify ret)]\n      (players/notify notify-chan notify-msg))))\n\n(defn start [{:as router-ctx :keys [ev-chan handler-wrapper-fn max-render-interval]}]\n  (let [handler-wrapper (if handler-wrapper-fn\n                          (handler-wrapper-fn router-ctx)\n                          (fn [handler] (go (handler))))]\n    (go-loop []\n      (when-let [msg (<! ev-chan)]\n        (<! (handler-wrapper\n             ;; Handle event\n             #(try\n                (log/debug \"Handling event:\" (pr-str msg))\n                (handle-event* router-ctx msg)\n                (catch :default ex\n                  (js/Raven.captureException ex)\n                  (log/error ex \"Error handling event:\" (pr-str msg))\n                  ))))\n        (recur)))))\n"
  },
  {
    "path": "src/cljs/zetawar/serialization.cljs",
    "content": "(ns zetawar.serialization\n  (:require\n   [cognitect.transit :as transit]\n   [goog.crypt.base64 :as base64]\n   [lzw]\n   [zetawar.app :as app]\n   [zetawar.data :as data]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]\n   [zetawar.util :as util :refer [breakpoint inspect]]))\n\n;; TODO: rename ns to encodeing or codecs?\n\n;; TODO: get lzw working in nodejs\n;; TODO: move to encoding or serialization ns\n;; TODO: document UTF-8 hack\n\n(defn encode-game-state [game-state]\n  (let [writer (transit/writer :json)]\n    (-> (transit/write writer game-state)\n        js/lzwEncode\n        js/encodeURIComponent\n        js/unescape\n        (base64/encodeString true))))\n\n(defn decode-game-state [encoded-game-state]\n  (let [reader (transit/reader :json)\n        transit-game-state (-> encoded-game-state\n                               (base64/decodeString true)\n                               js/escape\n                               js/decodeURIComponent\n                               js/lzwDecode)]\n    (transit/read reader transit-game-state)))\n\n;; TODO: move load-encoded-game-state! and set-url-game-state! back to app ns once\n;; lzw works on node\n\n(defn load-encoded-game-state!\n  ([{:as app-ctx :keys [conn players]} encoded-game-state]\n   (load-encoded-game-state! app-ctx data/rulesets data/maps data/scenarios encoded-game-state))\n  ([{:as app-ctx :keys [conn players]} rulesets map-defs scenario-defs encoded-game-state]\n   (->> encoded-game-state\n        decode-game-state\n        (app/load-game-state! app-ctx rulesets map-defs scenario-defs))))\n\n;; TODO: put URL in paste buffer automatically\n(defn set-url-game-state! [db]\n  (let [encoded-game-state (->> (app/current-game db)\n                                (game/get-game-state db)\n                                encode-game-state)]\n    (set! js/window.location.hash encoded-game-state)))\n"
  },
  {
    "path": "src/cljs/zetawar/subs.cljs",
    "content": "(ns zetawar.subs\n  (:require\n   [datascript.core :as d]\n   [posh.reagent :as posh]\n   [reagent.core :as r]\n   [zetawar.db :refer [e qe]]\n   [zetawar.game :as game]\n   [zetawar.hex :as hex]\n   [zetawar.logging :as log]\n   [zetawar.tiles :as tiles]\n   [zetawar.util :refer [breakpoint inspect select-values]])\n  (:require-macros\n   [zetawar.subs :refer [deftrack]]))\n\n;; TODO: add param asserts to check params for better error messages?\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; App\n\n(deftrack app-eid [conn]\n  (ffirst @(posh/q '[:find ?a\n                     :where\n                     [?a :app/game]]\n                   conn)))\n\n(deftrack app [conn]\n  @(posh/pull conn '[*] @(app-eid conn)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Game\n\n(deftrack game-eid [conn]\n  (-> @(app conn) :app/game e))\n\n(def game-pull [:game/id\n                :game/self-repair\n                :game/credits-per-base\n                :game/max-count-per-unit\n                :game/scenario-id\n                {:game/map []}\n                {:game/factions []}\n                :game/starting-faction\n                :game/current-faction\n                :game/round])\n\n(deftrack game [conn]\n  @(posh/pull conn game-pull @(game-eid conn)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Map\n\n(deftrack game-map-eid [conn]\n  (-> @(game conn) :game/map e))\n\n(deftrack game-map [conn]\n  @(posh/pull conn\n              '[:map/description]\n              @(game-map-eid conn)))\n\n;; TODO: use terrains output instead of running a separate query\n(deftrack terrain-eid-at [conn q r]\n  (let [game-eid' @(game-eid conn)\n        idx (game/game-pos-idx game-eid' q r)]\n    (ffirst @(posh/q '[:find ?u\n                       :in $ ?idx\n                       :where\n                       [?u :terrain/game-pos-idx ?idx]]\n                     conn (game/game-pos-idx game-eid' q r)))))\n\n(def terrain-pull [:terrain/q\n                   :terrain/r\n                   {:terrain/type [:terrain-type/id\n                                   :terrain-type/image\n                                   :terrain-type/can-build]\n                    :terrain/owner [:faction/color]}])\n\n;; TODO: use terrains output instead of running a separate query\n(deftrack terrain-at [conn q r]\n  (when-let [terrain-eid @(terrain-eid-at conn q r)]\n    @(posh/pull conn terrain-pull terrain-eid)))\n\n(deftrack terrains [conn]\n  (let [map-eid' @(game-map-eid conn)]\n    (:map/terrains\n     @(posh/pull conn [{:map/terrains terrain-pull}]\n                 map-eid'))))\n\n(deftrack map-width [conn]\n  (or (->> @(terrains conn)\n           (map :terrain/q)\n           (apply max))\n      0))\n\n(deftrack map-height [conn]\n  (or (->> @(terrains conn)\n           (map :terrain/r)\n           (apply max))\n      0))\n\n(deftrack map-width-px [conn]\n  (+ tiles/odd-row-column-offset\n     (* tiles/width\n        (inc @(map-width conn)))))\n\n(deftrack map-height-px [conn]\n  (+ tiles/height\n     (* tiles/row-offset\n        @(map-height conn))))\n\n;; TODO: use terrains output instead of running a separate query\n(defn current-base-locations [conn]\n  (posh/q '[:find ?q ?r\n            :where\n            [_  :app/game ?g]\n            [?g :game/map ?m]\n            [?g :game/current-faction ?f]\n            [?t :terrain/owner ?f]\n            [?m :map/terrains ?t]\n            [?t :terrain/q ?q]\n            [?t :terrain/r ?r]]\n          conn\n          {:cache :forever}))\n\n(deftrack current-base? [conn q r]\n  (contains? @(current-base-locations conn) [q r]))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Factions\n\n(deftrack faction-eids [conn]\n  (into []\n        (map e)\n        (:game/factions @(game conn))))\n\n(def faction-pull '[:faction/color\n                    :faction/credits\n                    :faction/player-type\n                    :faction/ai\n                    :faction/next-faction\n                    :faction/order])\n\n(deftrack factions-by-eid [conn]\n  (->> @(faction-eids conn)\n       (map (fn [eid] [eid @(posh/pull conn faction-pull eid)]))\n       (into {})))\n\n(deftrack factions [conn]\n  (->> @(factions-by-eid conn)\n       (map second)\n       (sort-by :faction/order)\n       (into [])))\n\n(deftrack faction-eid->base-count [conn]\n  (->> @(posh/q '[:find ?f (count ?t)\n                  :where\n                  [_  :app/game ?g]\n                  [?g :game/factions ?f]\n                  [?t :terrain/owner ?f]]\n                conn)\n       (into {})))\n\n(deftrack faction-eid->base-being-captured-count [conn]\n  (->> @(posh/q '[:find ?f (count ?t)\n                  :where\n                  [_ :app/game ?g]\n                  [?g :game/factions ?f]\n                  [?t :terrain/owner ?f]\n                  [(not= ?ef f)]\n                  [?ef :faction/units ?u]\n                  [?u :unit/terrain ?t]\n                  [?u :unit/capturing true]]\n                conn)\n       (into {})))\n\n(deftrack faction-eid->unit-count [conn]\n  (->> @(posh/q '[:find ?f (count ?u)\n                  :where\n                  [_  :app/game ?g]\n                  [?g :game/factions ?f]\n                  [?f :faction/units ?u]]\n                conn)\n       (into {})))\n\n(deftrack winning-faction-eid [conn]\n  (let [faction-eids-with-bases (into []\n                                      (comp (filter #(> (second %) 0))\n                                            (map first))\n                                      @(faction-eid->base-count conn))\n        faction-eids-with-units (into []\n                                      (comp (filter #(> (second %) 0))\n                                            (map first))\n                                      @(faction-eid->unit-count conn))]\n    (when (and (= (count faction-eids-with-bases) 1)\n               (= (count faction-eids-with-units) 1)\n               (= faction-eids-with-bases faction-eids-with-units))\n      (first faction-eids-with-bases))))\n\n(deftrack current-faction-eid [conn]\n  (e (:game/current-faction @(game conn))))\n\n(defn current-faction [conn]\n  (posh/pull conn faction-pull @(current-faction-eid conn)))\n\n(deftrack current-faction-won? [conn]\n  (= @(current-faction-eid conn)\n     @(winning-faction-eid conn)))\n\n(deftrack current-base-count [conn]\n  (get @(faction-eid->base-count conn) @(current-faction-eid conn)))\n\n(deftrack current-base-being-captured-count [conn]\n  (get @(faction-eid->base-being-captured-count conn) @(current-faction-eid conn)))\n\n(deftrack current-unit-count [conn]\n  (get @(faction-eid->unit-count conn) @(current-faction-eid conn)))\n\n(deftrack current-income [conn]\n  (let [{:keys [game/credits-per-base]} @(game conn)]\n    (* credits-per-base\n       (- @(current-base-count conn)\n          @(current-base-being-captured-count conn)))))\n\n(deftrack enemy-unit-count [conn]\n  (or (ffirst @(posh/q '[:find (count ?u)\n                         :in $ ?cf\n                         :where\n                         [?f :faction/units ?u]\n                         [(not= ?f ?cf)]]\n                       conn @(current-faction-eid conn)))\n      0))\n\n(deftrack enemy-base-count [conn]\n  (or (ffirst @(posh/q '[:find (count ?b)\n                         :in $ ?cf\n                         :where\n                         [?b :terrain/owner ?f]\n                         [(not= ?f ?cf)]]\n                       conn @(current-faction-eid conn)))\n      0))\n\n(deftrack faction-color-name [faction]\n  (when @faction\n    (-> @faction\n        :faction/color\n        name\n        (str \"-name\")\n        keyword)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Units\n\n(deftrack unit-eid-at [conn q r]\n  (let [game-eid' @(game-eid conn)\n        idx (game/game-pos-idx game-eid' q r)]\n    (ffirst @(posh/q '[:find ?u\n                       :in $ ?idx\n                       :where\n                       [?u :unit/game-pos-idx ?idx]]\n                     conn idx))))\n\n(deftrack unit-at? [conn q r]\n  (some? @(unit-eid-at conn q r)))\n\n(deftrack unit-at [conn q r]\n  (when-let [unit-eid @(unit-eid-at conn q r)]\n    @(posh/pull conn '[:unit/q\n                       :unit/r\n                       :unit/round-built\n                       :unit/move-count\n                       :unit/attack-count\n                       :unit/count\n                       :unit/repaired\n                       :unit/capturing\n                       {:faction/_units [:faction/color]\n                        :unit/type [:unit-type/id\n                                    :unit-type/description\n                                    :unit-type/can-capture\n                                    :unit-type/can-repair\n                                    :unit-type/armor-type\n                                    :unit-type/min-range\n                                    :unit-type/max-range\n                                    :unit-type/image]}]\n                unit-eid)))\n\n(deftrack current-unit-at [conn q r]\n  (when-let [unit @(unit-at conn q r)]\n    (let [cur-faction-eid @(current-faction-eid conn)]\n      (when (= cur-faction-eid (e (:faction/_units unit)))\n        unit))))\n\n(deftrack current-unit-eid-at [conn q r]\n  (some-> @(current-unit-at conn q r) e))\n\n(deftrack current-unit-at? [conn q r]\n  (some? @(current-unit-at conn q r)))\n\n(deftrack unit-color-at [conn q r]\n  (get-in @(unit-at conn q r) [:faction/_units :faction/color]))\n\n(deftrack unit-type-at [conn q r]\n  (get-in @(unit-at conn q r) [:unit/type :unit-type/id]))\n\n(deftrack enemy-locations [conn]\n  @(posh/q '[:find ?q ?r\n             :in $ ?g ?cf\n             :where\n             [_  :app/game ?g]\n             [?g :game/factions ?f]\n             [?f :faction/units ?u]\n             [?u :unit/q ?q]\n             [?u :unit/r ?r]\n             [(not= ?f ?cf)]]\n           conn @(game-eid conn) @(current-faction-eid conn)\n           {:cache :forever}))\n\n(deftrack enemy-at? [conn q r]\n  (contains? @(enemy-locations conn) [q r]))\n\n(deftrack enemy-locations-in-range-of [conn q r]\n  (let [attacker @(unit-at conn q r)\n        min-range (get-in attacker [:unit/type :unit-type/min-range])\n        max-range (get-in attacker [:unit/type :unit-type/max-range])]\n    (into #{}\n          (filter #(let [distance (apply hex/distance q r %)]\n                     (and (>= distance min-range) (<= distance max-range))))\n          @(enemy-locations conn))))\n\n(deftrack any-enemy-in-range-of? [conn q r]\n  (not (empty? @(enemy-locations-in-range-of conn q r))))\n\n(deftrack in-range-of-enemy-at? [conn unit-q unit-r enemy-q enemy-r]\n  (contains? @(enemy-locations-in-range-of conn unit-q unit-r) [enemy-q enemy-r]))\n\n(deftrack friend-locations [conn]\n  @(posh/q '[:find ?q ?r\n             :in $ ?g ?cf\n             :where\n             [_  :app/game ?g]\n             [?g :game/factions ?f]\n             [?f :faction/units ?u]\n             [?u :unit/q ?q]\n             [?u :unit/r ?r]\n             [(= ?f ?cf)]]\n           conn @(game-eid conn) @(current-faction-eid conn)\n           {:cache :forever}))\n\n(deftrack friend-at? [conn q r]\n  (contains? @(friend-locations conn) [q r]))\n\n(deftrack friend-locations-in-range-of [conn q r]\n  (let [unit @(unit-at conn q r)\n        min-range (get-in unit [:unit/type :unit-type/min-range])\n        max-range (get-in unit [:unit/type :unit-type/max-range])]\n    (into #{}\n          (filter #(let [distance (apply hex/distance q r %)]\n                     (and (>= distance min-range) (<= distance max-range))))\n          @(friend-locations conn))))\n\n(deftrack any-friend-in-range-of? [conn q r]\n  (not (empty? @(friend-locations-in-range-of conn q r))))\n\n(deftrack in-range-of-friend-at? [conn unit-q unit-r friend-q friend-r]\n  (contains? @(friend-locations-in-range-of conn unit-q unit-r) [friend-q friend-r]))\n\n(deftrack unit-terrain-effects [conn unit-q unit-r terrain-q terrain-r]\n  (when-let [unit @(unit-at conn unit-q unit-r)]\n    (let [terrain @(terrain-at conn terrain-q terrain-r)]\n      (game/unit-terrain-effects @conn unit terrain))))\n\n(deftrack repairable? [conn q r]\n  (when-let [unit @(unit-at conn q r)]\n    (game/repairable? @conn @(game conn) unit)))\n\n(deftrack can-move? [conn q r]\n  (when-let [unit @(unit-at conn q r)]\n    (game/can-move? @conn @(game conn) unit)))\n\n(deftrack can-attack? [conn q r]\n  (when-let [unit @(unit-at conn q r)]\n    (and (game/can-attack? @conn @(game conn) unit)\n         @(any-enemy-in-range-of? conn q r))))\n\n(deftrack can-repair? [conn q r]\n  (when-let [unit @(unit-at conn q r)]\n    (game/can-repair? @conn @(game conn) unit)))\n\n(deftrack can-field-repair? [conn q r]\n  (when-let [unit @(unit-at conn q r)]\n    (game/can-field-repair? @conn @(game conn) unit)))\n\n(deftrack can-capture? [conn q r]\n  (let [unit @(unit-at conn q r)\n        terrain @(terrain-at conn q r)]\n    (and unit\n         terrain\n         (game/can-capture? @conn @(game conn) unit terrain))))\n\n(deftrack unit-can-act? [conn q r]\n  (or @(can-move? conn q r)\n      @(can-attack? conn q r)\n      @(can-repair? conn q r)\n      @(can-field-repair? conn q r)\n      @(can-capture? conn q r)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Selection and target\n\n(deftrack selected? [conn q r]\n  (let [app' @(app conn)]\n    (and (= q (:app/selected-q app'))\n         (= r (:app/selected-r app')))))\n\n(deftrack targeted? [conn q r]\n  (let [app' @(app conn)]\n    (and (= q (:app/targeted-q app'))\n         (= r (:app/targeted-r app')))))\n\n(deftrack selected-hex [conn]\n  (-> @(app conn)\n      (select-values [:app/selected-q\n                      :app/selected-r])\n      not-empty))\n\n(deftrack targeted-hex [conn]\n  (-> @(app conn)\n      (select-values [:app/targeted-q\n                      :app/targeted-r])\n      not-empty))\n\n(deftrack selected-unit [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(unit-at conn q r)))\n\n(deftrack selected-terrain-effects [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(unit-terrain-effects conn q r q r)))\n\n(deftrack targeted-terrain-effects [conn]\n  (when-let [[terrain-q terrain-r] @(targeted-hex conn)]\n    (let [[unit-q unit-r] @(selected-hex conn)]\n      @(unit-terrain-effects conn unit-q unit-r terrain-q terrain-r))))\n\n(deftrack unit-selected? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(unit-at? conn q r)))\n\n(deftrack selected-can-move? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(can-move? conn q r)))\n\n(deftrack selected-can-attack? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(can-attack? conn q r)))\n\n(deftrack selected-can-repair? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(can-repair? conn q r)))\n\n(deftrack selected-can-field-repair? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(can-field-repair? conn q r)))\n\n(deftrack has-repairable-armor-type? [conn targeted-q targeted-r]\n  (when-let [[selected-q selected-r] @(selected-hex conn)]\n    (game/has-repairable-armor-type? @conn @(game conn)\n                                     @(unit-at conn selected-q selected-r)\n                                     @(unit-at conn targeted-q targeted-r))))\n\n(deftrack selected-can-capture? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    @(can-capture? conn q r)))\n\n(deftrack selected-can-build? [conn]\n  (when-let [[q r] @(selected-hex conn)]\n    (and (not @(unit-selected? conn))\n         @(current-base? conn q r))))\n\n(deftrack valid-destinations-for-selected [conn]\n  (if @(selected-can-move? conn)\n    (let [db @conn\n          game' (d/entity db @(game-eid conn))\n          [q r] @(selected-hex conn)\n          unit (game/unit-at db game' q r)]\n      (game/valid-destinations db game' unit))\n    #{}))\n\n(deftrack valid-destination-for-selected? [conn q r]\n  (contains? @(valid-destinations-for-selected conn) [q r]))\n\n(deftrack selected-can-move-to-targeted? [conn]\n  (when-let [[q r] @(targeted-hex conn)]\n    (and @(selected-can-move? conn)\n         (contains? @(valid-destinations-for-selected conn) [q r]))))\n\n(deftrack enemy-in-range-of-selected? [conn q r]\n  (when-let [[selected-q selected-r] @(selected-hex conn)]\n    @(in-range-of-enemy-at? conn selected-q selected-r q r)))\n\n(deftrack friend-in-range-of-selected? [conn q r]\n  (when-let [[selected-q selected-r] @(selected-hex conn)]\n    @(in-range-of-friend-at? conn selected-q selected-r q r)))\n\n(deftrack repairable-friend-in-range-of-selected? [conn q r]\n  (when-let [[selected-q selected-r] @(selected-hex conn)]\n    (and @(in-range-of-friend-at? conn selected-q selected-r q r)\n         @(repairable? conn q r))))\n\n(deftrack selected-can-attack-targeted? [conn]\n  (when-let [[q r] @(targeted-hex conn)]\n    (and @(selected-can-attack? conn)\n         @(enemy-in-range-of-selected? conn q r))))\n\n(deftrack selected-can-repair-targeted? [conn]\n  (when-let [[targeted-q targeted-r] @(targeted-hex conn)]\n    (and @(selected-can-field-repair? conn)\n         @(friend-in-range-of-selected? conn targeted-q targeted-r)\n         @(repairable? conn targeted-q targeted-r)\n         @(has-repairable-armor-type? conn targeted-q targeted-r))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Clickable\n\n(deftrack clickable? [conn q r]\n  (or\n   @(valid-destination-for-selected? conn q r)\n   @(enemy-in-range-of-selected? conn q r)\n   @(unit-can-act? conn q r)\n   (and @(current-base? conn q r)\n        (not @(unit-at? conn q r)))\n   (and @(repairable-friend-in-range-of-selected? conn q r)\n        @(selected-can-field-repair? conn)\n        @(has-repairable-armor-type? conn q r))))\n\n;;; Unit construction\n\n(deftrack available-unit-type-eids [conn]\n  (when-let [[sel-q sel-r] @(selected-hex conn)]\n    (let [selected-base (e @(terrain-at conn sel-q sel-r))]\n      (->> @(posh/q '[:find ?ut\n                      :in $ ?g ?t\n                      :where\n                      [_   :app/game ?g]\n                      [?g  :game/current-faction ?f]\n                      [?f  :faction/credits ?credits]\n                      [?ut :unit-type/cost ?cost]\n                      [?ut :unit-type/id ?unit-type-id]\n                      [?t  :terrain/type ?tt]\n                      [?tt :terrain-type/can-build ?ut]]\n                    conn @(game-eid conn) selected-base\n                    {:cache :forever})\n           (map first)\n           (into [])))))\n\n(deftrack available-unit-types [conn]\n  (let [{:keys [faction/credits]} @(current-faction conn)]\n    (->> @(available-unit-type-eids conn)\n         (map (fn [ut-eid]\n                (let [ut @(posh/pull conn '[*] ut-eid)\n                      affordable (<= (:unit-type/cost ut) credits)]\n                  ;; TODO: make affordable a namespaced key (?)\n                  (assoc ut :affordable affordable))))\n         (sort-by :unit-type/cost)\n         (into []))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Unit picker\n\n(deftrack picking-unit? [conn]\n  (:app/picking-unit @(app conn)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Win message\n\n(deftrack show-win-message? [conn]\n  (and @(current-faction-won? conn)\n       (not (:faction/ai @(current-faction conn)))\n       (not (:app/hide-win-message @(app conn)))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Faction configuration\n\n(deftrack faction-to-configure [conn]\n  (some->> @(app conn)\n           :app/configuring-faction\n           e\n           (get @(factions-by-eid conn))))\n\n(deftrack configuring-faction? [conn]\n  (some? @(faction-to-configure conn)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; New game configuration\n\n(deftrack configuring-new-game? [conn]\n  (:app/configuring-new-game @(app conn)))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Tile coordinates\n\n(deftrack hover-hex [conn]\n  (-> @(app conn)\n      (select-values [:app/hover-q\n                      :app/hover-r])\n      not-empty))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; End turn\n\n(deftrack available-moves-left? [conn]\n  (some\n   (fn [[q r] coordinates] @(unit-can-act? conn q r))\n   @(friend-locations conn)))\n\n(deftrack show-end-turn-alert? [conn]\n  (:app/end-turn-alert @(app conn)))\n"
  },
  {
    "path": "src/cljs/zetawar/system/datascript.cljs",
    "content": "(ns zetawar.system.datascript\n  (:require\n   [datascript.core :as d]\n   [integrant.core :as ig]))\n\n(defmethod ig/init-key :zetawar.system/datascript [_ opts]\n  (let [{:keys [schema]} opts]\n    {:schema schema\n     :conn (d/create-conn schema)}))\n\n(defmethod ig/resume-key :zetawar.system/datascript [_ opts old-opts old-impl]\n  (let [{:keys [schema]} opts\n        old-schema (:schema old-opts)]\n    (if (= schema old-schema)\n      old-impl\n      {:schema schema\n       :conn (d/create-conn schema)})))\n"
  },
  {
    "path": "src/cljs/zetawar/system/game.cljs",
    "content": "(ns zetawar.system.game\n  (:require\n   [integrant.core :as ig]\n   [zetawar.logging :as log]\n   [zetawar.router :as router]))\n\n;; TODO: setup initial game + load game state (move from core)\n(defmethod ig/init-key :zetawar.system/game [_ opts]\n  (let [{:keys [datascript players router]} opts\n        {:keys [conn]} datascript\n        {:keys [ev-chan notify-pub]} router]\n    {:conn conn\n     :players players\n     :ev-chan ev-chan\n     :notify-pub notify-pub}))\n"
  },
  {
    "path": "src/cljs/zetawar/system/game_views.cljs",
    "content": "(ns zetawar.system.game-views\n  (:require\n   [integrant.core :as ig]\n   [posh.reagent :as posh]\n   [tongue.core :as tongue]\n   [zetawar.data :as data]\n   [zetawar.logging :as log]\n   [zetawar.router :as router]))\n\n;; TODO: start Reagent components when running in the browser\n(defmethod ig/init-key :zetawar.system/game-views [_ opts]\n  (let [{:keys [datascript router locale]} opts\n        {:keys [conn]} datascript\n        {:keys [ev-chan]} router\n        dispatch #(router/dispatch ev-chan %)\n        translate (-> data/dicts\n                      tongue/build-translate\n                      (partial locale))]\n    (posh/posh! conn)\n    {:conn conn\n     :dispatch dispatch\n     :translate translate}))\n\n(defmethod ig/resume-key :zetawar.system/game-views [_ opts old-opts old-impl]\n  (let [{:keys [datascript router locale]} opts\n        {:keys [conn]} datascript\n        {:keys [ev-chan]} router\n        old-conn (get-in old-opts [:datascript :conn])\n        dispatch #(router/dispatch ev-chan %)\n        translate (-> data/dicts\n                      tongue/build-translate\n                      (partial locale))]\n    (when-not (= conn old-conn)\n      (posh/posh! conn))\n    {:conn conn\n     :dispatch dispatch\n     :translate translate}))\n"
  },
  {
    "path": "src/cljs/zetawar/system/players.cljs",
    "content": "(ns zetawar.system.players\n  (:require\n   [integrant.core :as ig]\n   [zetawar.players :as players]))\n\n(defmethod ig/init-key :zetawar.system/players [_ opts]\n  (atom {}))\n\n;; TODO: re-enable once game setup is part of Integrant system\n#_(defmethod ig/halt-key! :zetawar.system/players [_ players]\n    (doseq [player players]\n      (players/stop player)))\n"
  },
  {
    "path": "src/cljs/zetawar/system/reagent.cljs",
    "content": "(ns zetawar.system.reagent\n  (:require\n   [integrant.core :as ig]\n   [zetawar.logging :as log]\n   [zetawar.router.reagent]))\n\n(defmethod ig/init-key :zetawar.system/reagent [_ opts]\n  {:handler-wrapper-fn zetawar.router.reagent/handler-wrapper-fn})\n"
  },
  {
    "path": "src/cljs/zetawar/system/router.cljs",
    "content": "(ns zetawar.system.router\n  (:require\n   [cljs.core.async :as async]\n   [integrant.core :as ig]\n   [zetawar.router :as router]))\n\n(defmethod ig/init-key :zetawar.system/router [_ opts]\n  (let [{:keys [datascript renderer players]} opts\n        {:keys [conn]} datascript\n        {:keys [handler-wrapper-fn]} renderer\n        ev-chan (async/chan 100)\n        notify-chan (async/chan)\n        notify-pub (async/pub notify-chan #(nth % 1))]\n    (router/start {:ev-chan ev-chan\n                   :handler-wrapper-fn handler-wrapper-fn\n                   :max-render-interval 200\n                   :notify-chan notify-chan\n                   :notify-pub notify-pub\n                   :conn conn\n                   :players players})\n    {:ev-chan ev-chan\n     :notify-chan notify-chan\n     :notify-pub notify-pub}))\n\n(defmethod ig/halt-key! :zetawar.system/router [_ router]\n  (let [{:keys [ev-chan notify-chan notify-pub]} router]\n    (async/close! ev-chan)\n    (async/close! notify-chan)))\n"
  },
  {
    "path": "src/cljs/zetawar/system/spec.cljs",
    "content": "(ns zetawar.system.spec\n  (:require\n   [cljs.core.async :as async]\n   [cljs.core.async.impl.protocols :as async.protocols]\n   [clojure.spec :as s]\n   [clojure.spec.impl.gen :as gen]\n   [clojure.test.check]\n   [datascript.core :as d]))\n\n;; Logger\n(s/def :zetawar.system/logger nil?)\n\n;; DataScript\n(s/def :zetawar.system.datascript/schema\n  (s/with-gen map?\n    #(gen/return {})))\n\n(s/def :zetawar.system.datascript/conn\n  (s/with-gen d/conn?\n    #(gen/return (d/create-conn {}))))\n\n(s/def :zetawar.system/datascript\n  (s/keys :req-un [:zetawar.system.datascript/schema\n                   :zetawar.system.datascript/conn]))\n\n;; Players\n(s/def :zetawar.system/players\n  (s/with-gen (s/and #(satisfies? IDeref %)\n                     #(map? (deref %)))\n    #(gen/fmap (fn [m] (atom m))\n               (gen/map (gen/keyword) (gen/any-printable)))))\n\n;; Router\n(s/def :zetawar.system.router/ev-chan\n  (s/with-gen\n    (s/and #(satisfies? async.protocols/ReadPort %)\n           #(satisfies? async.protocols/WritePort %))\n    #(gen/return (async/chan))))\n\n(s/def :zetawar.system.router/notify-chan\n  (s/with-gen\n    #(satisfies? async.protocols/WritePort %)\n    #(gen/return (async/chan))))\n\n(s/def :zetawar.system.router/notify-pub\n  (s/with-gen\n    #(satisfies? async/Pub %)\n    #(gen/return (let [notify-chan (async/chan)]\n                   (async/pub notify-chan (fn [x] (nth x 1)))))))\n\n(s/def :zetawar.system/router\n  (s/keys :req-un [:zetawar.system.datascript/conn\n                   :zetawar.system/players\n                   :zetawar.system.router/ev-chan\n                   :zetawar.system.router/notify-chan\n                   :zetawar.system.router/notify-pub]))\n\n;; Views\n(s/def :zetawar.system.views/dispatch fn?)\n(s/def :zetawar.system.views/translate fn?)\n\n(s/def :zetawar.system/views\n  (s/keys :req-un [:zetawar.system.datascript/conn\n                   :zetawar.system.views/dispatch\n                   :zetawar.system.views/translate]))\n\n;; System\n(s/def :zetawar/system\n  (s/keys :req-un [:zetawar.system/logger\n                   :zetawar.system/datascript\n                   :zetawar.system/players\n                   :zetawar.system/router\n                   :zetawar.system/views]))\n"
  },
  {
    "path": "src/cljs/zetawar/system.cljs",
    "content": "(ns zetawar.system\n  (:require\n   [integrant.core :as ig]\n   [zetawar.db :as db]))\n\n;; TODO: rename to browser-cfg\n(def game-config\n  {:zetawar.system/datascript {:schema     db/schema}\n   :zetawar.system/reagent    {}\n   :zetawar.system/players    {}\n   :zetawar.system/router     {:datascript (ig/ref :zetawar.system/datascript)\n                               :renderer   (ig/ref :zetawar.system/reagent)\n                               :players    (ig/ref :zetawar.system/players)}\n   :zetawar.system/game       {:datascript (ig/ref :zetawar.system/datascript)\n                               :router     (ig/ref :zetawar.system/router)\n                               :players    (ig/ref :zetawar.system/players)}\n   :zetawar.system/game-views {:datascript (ig/ref :zetawar.system/datascript)\n                               :router     (ig/ref :zetawar.system/router)\n                               :locale     :en}})\n\n;; TODO: add cli-cfg\n"
  },
  {
    "path": "src/cljs/zetawar/tiles.cljs",
    "content": "(ns zetawar.tiles)\n\n(def width 32)\n(def height 34)\n(def row-offset 26)\n(def odd-row-column-offset (/ width 2))\n\n(def offset->pixel\n  (memoize\n   (fn [q r]\n     [(cond-> (* q width)\n        (odd? r) (+ odd-row-column-offset))\n      (* r row-offset)])))\n"
  },
  {
    "path": "src/cljs/zetawar/views.cljs",
    "content": "(ns zetawar.views\n  (:require\n   [cljsjs.clipboard]\n   [cljsjs.react-bootstrap]\n   [clojure.string :as string]\n   [datascript.core :as d]\n   [posh.reagent :as posh]\n   [reagent.core :as r :refer [with-let]]\n   [zetawar.data :as data]\n   [zetawar.db :refer [e qe]]\n   [zetawar.events.ui :as events.ui]\n   [zetawar.game :as game]\n   [zetawar.logging :as log]\n   [zetawar.players :as players]\n   [zetawar.site :as site]\n   [zetawar.subs :as subs]\n   [zetawar.tiles :as tiles]\n   [zetawar.util :refer [breakpoint inspect only oonly]]\n   [zetawar.views.common :refer [footer navbar]]))\n\n(defn tile-border [{:as view-ctx :keys [conn]} q r]\n  (let [[x y] (tiles/offset->pixel q r)]\n    [:g {:id (str \"border-\" q \",\" r)}\n     (cond\n       ;; Selected\n       @(subs/selected? conn q r)\n       [:image {:x x :y y\n                :width tiles/width :height tiles/height\n                :xlink-href (site/prefix \"/images/game/borders/selected.png\")}]\n\n       ;; Enemy unit targeted\n       (and @(subs/targeted? conn q r)\n            @(subs/enemy-at? conn q r))\n       [:image {:x x :y y\n                :width tiles/width :height tiles/height\n                :xlink-href (site/prefix \"/images/game/borders/targeted-enemy.png\")}]\n\n       ;; Friend unit targeted (for repair)\n       (and @(subs/targeted? conn q r)\n            @(subs/friend-at? conn q r))\n       [:image {:x x :y y\n                :width tiles/width :height tiles/height\n                :xlink-href (site/prefix \"/images/game/borders/targeted-friend.png\")}]\n\n       ;; Terrain targeted\n       @(subs/targeted? conn q r)\n       [:image {:x x :y y\n                :width tiles/width :height tiles/height\n                :xlink-href (site/prefix \"/images/game/borders/selected.png\")}])]))\n\n(defn unit-image [unit]\n  (let [color-name (-> unit game/unit-color name)]\n    ;; TODO: return placeholder if terrain image is not found\n    (some-> unit\n            (get-in [:unit/type :unit-type/image])\n            (string/replace \"COLOR\" color-name))))\n\n(defn board-unit [{:as view-ctx :keys [conn dispatch]} q r]\n  (when-let [unit @(subs/unit-at conn q r)]\n    (let [[x y] (tiles/offset->pixel q r)\n          image (unit-image unit)]\n      [:g {:id (str \"unit-\" (e unit))}\n       [:image {:x x :y y\n                :width tiles/width :height tiles/height\n                :xlink-href (site/prefix \"/images/game/\" image)\n                :on-click #(dispatch [::events.ui/select-hex q r])}]\n       (when (:unit/capturing unit)\n         [:image {:x x :y y\n                  :width tiles/width :height tiles/height\n                  :xlink-href (site/prefix \"/images/game/capturing.gif\")}])\n       [:image {:x x :y y\n                :width tiles/width :height tiles/height\n                :xlink-href (site/prefix \"/images/game/health/\" (:unit/count unit) \".png\")}]])))\n\n(defn tile-mask [{:as view-ctx :keys [conn]} q r]\n  (let [[x y] (tiles/offset->pixel q r)\n        show (or\n              ;; No unit selected and tile contains current unit with no actions\n              (and (not @(subs/unit-selected? conn))\n                   @(subs/current-unit-at? conn q r)\n                   (not @(subs/unit-can-act? conn q r)))\n\n              ;; Unit selected and tile is a valid attack, repair, or move target\n              (and @(subs/unit-selected? conn)\n                   (not @(subs/selected? conn q r))\n                   (not @(subs/enemy-in-range-of-selected? conn q r))\n                   (not (and @(subs/repairable-friend-in-range-of-selected? conn q r)\n                             @(subs/selected-can-field-repair? conn)\n                             @(subs/has-repairable-armor-type? conn q r)))\n                   (not @(subs/valid-destination-for-selected? conn q r))))]\n    [:image {:visibility (if show \"visible\" \"hidden\")\n             :x x :y y\n             :width tiles/width :height tiles/height\n             :xlink-href (site/prefix \"/images/game/mask.png\")}]))\n\n(defn terrain-image [terrain]\n  (let [color-name (-> terrain\n                       (get-in [:terrain/owner :faction/color])\n                       (or :none)\n                       name)]\n    ;; TODO: return placeholder if terrain image is not found\n    (some-> terrain\n            (get-in [:terrain/type :terrain-type/image])\n            (string/replace \"COLOR\" color-name))))\n\n(defn terrain-tile [view-ctx terrain q r]\n  (let [[x y] (tiles/offset->pixel q r)\n        image (terrain-image terrain)]\n    [:image {:x x :y y\n             :width tiles/width :height tiles/height\n             :xlink-href (site/prefix \"/images/game/\" image)}]))\n\n(defn tile [{:as view-ctx :keys [conn dispatch]} terrain]\n  (let [{:keys [terrain/q terrain/r]} terrain]\n    ^{:key (str q \",\" r)}\n    [:g {:on-click #(dispatch [::events.ui/select-hex q r])\n         :on-mouse-enter #(dispatch [::events.ui/hover-hex-enter q r])\n         :on-mouse-leave #(dispatch [::events.ui/hover-hex-leave q r])\n         :cursor (if @(subs/clickable? conn q r)\n                   \"pointer\"\n                   \"default\")}\n     [terrain-tile view-ctx terrain q r]\n     [tile-border view-ctx q r]\n     [board-unit view-ctx q r]\n     [tile-mask view-ctx q r]]))\n\n(defn tiles [{:as view-ctx :keys [conn]}]\n  (into [:g]\n        (for [terrain @(subs/terrains conn)]\n          [tile view-ctx terrain])))\n\n(defn board [{:as view-ctx :keys [conn]}]\n  [:svg#board {:width @(subs/map-width-px conn)\n               :height @(subs/map-height-px conn)}\n   [tiles view-ctx]])\n\n(defn faction-credits [{:as view-ctx :keys [conn translate]}]\n  (let [{:keys [faction/credits]} @(subs/current-faction conn)\n        {:keys [map/credits-per-base]} @(subs/game-map conn)\n        income @(subs/current-income conn)]\n    [:p#faction-credits\n     [:strong (str credits \" \" (translate :credits-label))]\n     [:span.text-muted.pull-right\n      (str \"+\" income)]]))\n\n(defn copy-url-link [{:as view-ctx :keys [conn translate]}]\n  (let [clipboard (atom nil)\n        text-fn (fn [] js/window.location)]\n    (r/create-class\n     {:component-did-mount\n      (fn [this]\n        (reset! clipboard (js/Clipboard. (r/dom-node this) #js {\"text\" text-fn})))\n      :component-will-unmount\n      (fn [this]\n        (.destroy @clipboard)\n        (reset! clipboard nil))\n      :reagent-render\n      (fn [this]\n        [:a {:href \"#\" :on-click #(.preventDefault %)}\n         (translate :copy-game-url-link)])})))\n\n(defn end-turn-alert [{:as view-ctx :keys [conn dispatch translate]}]\n  [:> js/ReactBootstrap.Modal {:show @(subs/show-end-turn-alert? conn)\n                               :on-hide #(dispatch [::events.ui/hide-end-turn-alert])}\n   [:> js/ReactBootstrap.Modal.Body\n    (translate :end-turn-alert)]\n   [:> js/ReactBootstrap.Modal.Footer\n    [:div.btn.btn-default {:on-click (fn [e]\n                                       (.preventDefault e)\n                                       (dispatch [::events.ui/end-turn])\n                                       (dispatch [::events.ui/hide-end-turn-alert]))}\n     (translate :end-turn-confirm)]\n    [:div.btn.btn-default {:on-click #(dispatch [::events.ui/hide-end-turn-alert])}\n     (translate :cancel-button)]]])\n\n(defn faction-status [{:as view-ctx :keys [conn dispatch translate]}]\n  (let [{:keys [app/show-copy-link]} @(subs/app conn)\n        {:keys [game/round]} @(subs/game conn)\n        base-count @(subs/current-base-count conn)]\n    [:div#faction-status\n     ;; TODO: make link red\n     [:a {:href \"#\" :on-click (fn [e]\n                                (.preventDefault e)\n                                (if @(subs/available-moves-left? conn)\n                                  (dispatch [::events.ui/show-end-turn-alert])\n                                  (dispatch [::events.ui/end-turn])))}\n      (translate :end-turn-link)]\n     (when show-copy-link\n       [:span \" · \" [copy-url-link view-ctx]])\n     [:div.pull-right\n      [:a {:href \"#\"\n           :on-click (fn [e]\n                       (.preventDefault e)\n                       (dispatch [::events.ui/show-new-game-settings]))}\n       (translate :new-game-link)]\n      \" · \"\n      (str (translate :round-label) \" \" round)]]))\n\n(defn faction-actions [{:as view-ctx :keys [conn dispatch translate]}]\n  ;; TODO: replace query with something from subs ns\n  (let [[round current-color] (-> @(posh/q '[:find ?round ?current-color\n                                             :where\n                                             [?g :game/round ?round]\n                                             [?g :game/current-faction ?f]\n                                             [?f :faction/color ?current-color]]\n                                           conn)\n                                  first)\n        {:keys [faction/credits]} @(subs/current-faction conn)]\n    [:div#faction-actions\n     (when @(subs/selected-can-move-to-targeted? conn)\n       [:p\n        [:button.btn.btn-primary.btn-block\n         {:on-click #(dispatch [::events.ui/move-selected-unit])}\n         (translate :move-unit-button)]])\n     (when @(subs/selected-can-build? conn)\n       [:p\n        [:button.btn.btn-primary.btn-block\n         {:on-click #(dispatch [::events.ui/show-unit-picker])}\n         (translate :build-unit-button)]])\n     (when @(subs/selected-can-attack-targeted? conn)\n       [:p\n        [:button.btn.btn-danger.btn-block\n         {:on-click #(dispatch [::events.ui/attack-targeted])}\n         (translate :attack-unit-button)]])\n     (when @(subs/selected-can-repair? conn)\n       [:p\n        [:button.btn.btn-success.btn-block\n         {:on-click #(dispatch [::events.ui/repair-selected])}\n         (translate :repair-unit-button)]])\n     (when @(subs/selected-can-repair-targeted? conn)\n       [:p\n        [:button.btn.btn-success.btn-block\n         {:on-click #(dispatch [::events.ui/repair-targeted])}\n         (translate :field-repair-button)]])\n     (when @(subs/selected-can-capture? conn)\n       [:p\n        [:button.btn.btn-primary.btn-block\n         {:on-click #(dispatch [::events.ui/capture-selected])}\n         (translate :capture-base-button)]])\n     ;; TODO: cleanup conditionals\n     ;; TODO: make help text a separate component\n     (when (not (or @(subs/selected-can-move? conn)\n                    @(subs/selected-can-build? conn)\n                    @(subs/selected-can-attack? conn)\n                    @(subs/selected-can-repair? conn)\n                    @(subs/selected-can-capture? conn)))\n       [:p.hidden-xs.hidden-sm\n        (translate :select-unit-or-base-tip)])\n     (when (and\n            (or @(subs/selected-can-move? conn)\n                @(subs/selected-can-attack? conn)\n                @(subs/selected-can-repair? conn))\n            (not\n             (or @(subs/selected-can-move-to-targeted? conn)\n                 @(subs/selected-can-attack-targeted? conn)\n                 @(subs/selected-can-repair-targeted? conn))))\n       [:p.hidden-xs.hidden-sm\n        (translate :select-target-or-destination-tip)])\n     ;; TODO: only display when starting faction is active\n     (when (and (= round 1)\n                (not @(subs/selected-hex conn)))\n       [:p.hidden-xs.hidden-sm\n        {:dangerouslySetInnerHTML {:__html (translate :multiplayer-tip)}}])]))\n\n(defn faction-list [{:as view-ctx :keys [conn dispatch translate]}]\n  (into [:ul.list-group]\n        (for [faction @(subs/factions conn)]\n          (let [faction-eid (e faction)\n                color (-> faction\n                          :faction/color\n                          name\n                          string/capitalize)\n                active (= faction-eid @(subs/current-faction-eid conn))\n                li-class (if active\n                           \"list-group-item active\"\n                           \"list-group-item\")\n                icon-class (if (:faction/ai faction)\n                             \"fa fa-fw fa-laptop clickable\"\n                             \"fa fa-fw fa-user clickable\")]\n            [:li {:class li-class}\n             color\n             \" \"\n             (when active\n               [:span.fa.fa-angle-double-left\n                {:aria-hidden true}])\n             [:div.pull-right\n              [:span\n               {:class icon-class\n                :aria-hidden true\n                :on-click #(dispatch [::events.ui/configure-faction faction])\n                :title (translate :configure-faction-tip)}]]]))))\n\n(defn status-info [{:as view-ctx :keys [conn translate]}]\n  [:div\n   (let [[sel-q sel-r] @(subs/selected-hex conn)\n         [tar-q tar-r] @(subs/targeted-hex conn)\n         [sel-mc sel-at sel-ar] @(subs/selected-terrain-effects conn)\n         [tar-mc tar-at tar-ar] @(subs/targeted-terrain-effects conn)\n         [hover-q hover-r] @(subs/hover-hex conn)]\n     [:span\n      (translate :selected-label)\n      (if sel-q\n        [:span\n         [:abbr {:title (translate :tile-coordinates-label) :style {:cursor \"inherit\"}}\n          (str sel-q \",\" sel-r)]\n         (if sel-mc ;; If selected doesn't contain a unit\n           [:span\n            \" (\"\n            [:abbr {:title (translate :terrain-effects-label) :style {:cursor \"inherit\"}}\n             (str sel-mc \",\" sel-at \",\" sel-ar)]\n            \")\"])]\n        [:span \" -\"])\n      \" • \"\n      (translate :targeted-label)\n      (if tar-q\n        [:span\n         [:abbr {:title (translate :tile-coordinates-label) :style {:cursor \"inherit\"}}\n          (str tar-q \",\" tar-r)]\n         \" (\"\n         [:abbr {:title (translate :terrain-effects-label) :style {:cursor \"inherit\"}}\n          (str tar-mc \",\" tar-at \",\" tar-ar)]\n         \")\"]\n        [:span \" -\"])\n      [:span.hidden-xs.hidden-sm\n       \" • \"\n       (translate :hover-tile-location)\n       (if hover-q\n         (str hover-q \",\" hover-r)\n         \"-\")]])])\n\n(def armor-type-abbrevs\n  {:unit-type.armor-type/personnel \"P\"\n   :unit-type.armor-type/armored \"Ar\"\n   :unit-type.armor-type/naval \"N\"\n   :unit-type.armor-type/air \"Ai\"})\n\n;; TODO: cleanup unit-picker\n(defn unit-picker [{:as view-ctx :keys [conn dispatch translate]}]\n  (let [unit-types @(subs/available-unit-types conn)\n        cur-faction @(subs/current-faction conn)\n        color (name (:faction/color cur-faction))\n        hide-picker #(dispatch [::events.ui/hide-unit-picker])]\n    [:> js/ReactBootstrap.Modal {:show @(subs/picking-unit? conn)\n                                 :on-hide hide-picker}\n     [:> js/ReactBootstrap.Modal.Header {:close-button true}\n      [:> js/ReactBootstrap.Modal.Title\n       (translate :build-title)]]\n     [:> js/ReactBootstrap.Modal.Body\n      [:> js/ReactBootstrap.Table {:bordered true\n                                   :striped true\n                                   :condensed true\n                                   :hover true}\n       [:thead>tr\n        [:th \"\"]\n        [:th.text-center {:style {:width \"12%\"}}\n         (translate :armor-type-label)]\n        [:th.text-center {:style {:width \"12%\"}}\n         (translate :movement-label)]\n        [:th.text-center {:style {:width \"12%\"}}\n         (translate :armor-label)]\n        [:th.text-center {:style {:width \"12%\"}}\n         (translate :range-label)]\n        [:th.text-center {:style {:width \"12%\"}}\n         (translate :attack-label)]\n        [:th.text-center {:style {:width \"12%\"}}\n         (translate :field-repair-label)]]\n       (into [:tbody]\n             (for [unit-type unit-types]\n               (let [;; TODO: replace with unit-type-image\n                     color-or-grey (if (:affordable unit-type)\n                                     color\n                                     \"unavailable\")\n                     image (->> (string/replace (:unit-type/image unit-type)\n                                                \"COLOR\" color-or-grey)\n                                (str \"/images/game/\"))\n                     media-class (if (:affordable unit-type)\n                                   \"media text-left\"\n                                   \"media text-left text-muted\")\n                     {:keys [unit-type/id\n                             unit-type/description\n                             unit-type/cost\n                             unit-type/movement\n                             unit-type/armor\n                             unit-type/armor-type\n                             unit-type/can-capture\n                             unit-type/capturing-armor\n                             unit-type/min-range\n                             unit-type/max-range]} unit-type\n                     armor-type-abbrev (armor-type-abbrevs armor-type)]\n                 [:tr.text-center.clickable\n                  {:on-click #(when (:affordable unit-type)\n                                (dispatch [::events.ui/hide-unit-picker])\n                                (dispatch [::events.ui/build-unit id]))}\n                  [:td>div {:class media-class}\n                   [:div.media-left.media-middle\n                    [:img {:src image}]]\n                   [:div.media-body\n                    [:h4.media-heading description]\n                    (str (translate :unit-cost-label) cost)]]\n                  [:td (case armor-type\n                         :unit-type.armor-type/personnel\n                         [:abbr {:title (translate :personnel-name)\n                                 :style {:cursor \"inherit\"}}\n                          armor-type-abbrev]\n\n                         :unit-type.armor-type/armored\n                         [:abbr {:title (translate :armored-name)\n                                 :style {:cursor \"inherit\"}}\n                          armor-type-abbrev]\n\n                         :unit-type.armor-type/naval\n                         [:abbr {:title (translate :naval-name)\n                                 :style {:cursor \"inherit\"}}\n                          armor-type-abbrev]\n\n                         :unit-type.armor-type/air\n                         [:abbr {:title (translate :air-name)\n                                 :style {:cursor \"inherit\"}}\n                          armor-type-abbrev])]\n                  [:td movement]\n                  [:td (if can-capture\n                         [:abbr {:title (str (translate :while-capturing-label)\n                                             capturing-armor)\n                                 :style {:cursor \"inherit\"}}\n                          armor]\n                         [:abbr {:title (translate :unit-cannot-capture-bases-label)\n                                 :style {:cursor \"inherit\"}}\n                          armor])]\n                  [:td min-range \"-\" max-range]\n                  (into [:td]\n                        (for [unit-strength (:unit-type/strengths unit-type)]\n                          (let [{:keys [unit-strength/armor-type\n                                        unit-strength/attack]} unit-strength\n                                armor-type-abbrev (armor-type-abbrevs armor-type)]\n                            [:div (str armor-type-abbrev \": \" attack)])))\n                  [:td (string/join \", \"\n                                    (for [can-repair (:unit-type/can-repair unit-type)]\n                                      (armor-type-abbrevs can-repair \"\")))]])))]]\n     [:> js/ReactBootstrap.Modal.Footer\n      [:div.btn.btn-default {:on-click hide-picker}\n       (translate :cancel-button)]]]))\n\n(defn faction-settings [{:as views-ctx :keys [conn dispatch translate]}]\n  (with-let [faction (subs/faction-to-configure conn)\n             faction-color (subs/faction-color-name faction)\n             selected-player-type (r/atom nil)\n             hide-settings (fn [ev]\n                             (when ev (.preventDefault ev))\n                             (dispatch [::events.ui/hide-faction-settings]))\n             select-player-type #(reset! selected-player-type (.-target.value %))\n             set-player-type (fn [ev]\n                               (.preventDefault ev)\n                               (when-let [player-type-id (->> (or @selected-player-type :human)\n                                                              (keyword 'zetawar.players))]\n                                 (reset! selected-player-type nil)\n                                 (dispatch [::events.ui/set-faction-player-type @faction player-type-id]))\n                               (dispatch [::events.ui/hide-faction-settings]))]\n    [:> js/ReactBootstrap.Modal {:show (some? @faction)\n                                 :on-hide hide-settings}\n     [:> js/ReactBootstrap.Modal.Header {:close-button true}\n      [:> js/ReactBootstrap.Modal.Title\n       (translate :configure-faction-title-prefix)\n       (translate @faction-color)]]\n     [:> js/ReactBootstrap.Modal.Body\n      [:form\n       [:div.form-group\n        [:label {:for \"player-type\"}\n         (translate :player-type-label)]\n        (into [:select.form-control {:id \"player-type\"\n                                     :value (or @selected-player-type\n                                                (some-> @faction :faction/player-type name)\n                                                \"\")\n                                     :on-change select-player-type}]\n              (for [[player-type-id {:keys [description ai]}] players/player-types]\n                [:option {:value (name player-type-id)}\n                 description]))\n        [:> js/ReactBootstrap.Modal.Footer\n         [:button.btn.btn-primary {:on-click set-player-type}\n          (translate :save-button)]\n         [:button.btn.btn-default {:on-click hide-settings}\n          (translate :cancel-button)]]]]]]))\n\n;; TODO: move default-scenario-id to data ns?\n(defn new-game-settings [{:as view-ctx :keys [conn dispatch translate]}]\n  (with-let [default-scenario-id :sterlings-aruba-multiplayer\n             selected-scenario-id (r/atom default-scenario-id)\n             hide-settings (fn [ev]\n                             (when ev (.preventDefault ev))\n                             (dispatch [::events.ui/hide-new-game-settings]))\n             select-scenario #(reset! selected-scenario-id (keyword (.-target.value %)))\n             start-new-game #(do\n                               (.preventDefault %)\n                               (dispatch [::events.ui/start-new-game @selected-scenario-id])\n                               (reset! selected-scenario-id default-scenario-id)\n                               (dispatch [::events.ui/hide-new-game-settings]))]\n    [:> js/ReactBootstrap.Modal {:show @(subs/configuring-new-game? conn)\n                                 :on-hide hide-settings}\n     [:> js/ReactBootstrap.Modal.Header {:close-button true}\n      [:> js/ReactBootstrap.Modal.Title\n       (translate :new-game-title)]]\n     [:> js/ReactBootstrap.Modal.Body\n      [:form\n       [:div.form-group\n        [:label {:for \"scenario-id\"}\n         (translate :scenario-label)]\n        (into [:select.form-control {:id \"scenario-id\"\n                                     :selected (some-> @selected-scenario-id name)\n                                     :on-change select-scenario}]\n              (for [[scenario-id {:keys [description notes]}] data/scenarios]\n                [:option {:value (name scenario-id)}\n                 (if notes\n                   (str description \": \" notes)\n                   description)]))\n        [:> js/ReactBootstrap.Modal.Footer\n         [:button.btn.btn-primary {:on-click start-new-game}\n          (translate :start-button)]\n         [:button.btn.btn-default {:on-click hide-settings}\n          (translate :cancel-button)]]]]]]))\n\n(defn alert [{:as view-ctx :keys [conn dispatch]}]\n  (let [{:keys [app/alert-message app/alert-type]} @(subs/app conn)\n        alert-class (str \"alert alert-\" (some-> alert-type name))]\n    (when alert-message\n      [:div.row\n       [:div.col-md-12\n        [:div {:class alert-class}\n         [:button.close {:type :button\n                         :aria-label \"Close\"\n                         :on-click #(dispatch [::events.ui/hide-alert])}\n          [:span {:aria-hidden true} \"×\"]]\n         alert-message]]])))\n\n(defn game-interface [view-ctx]\n  [:div.row\n   [:div.col-md-2\n    [faction-credits view-ctx]\n    [faction-list view-ctx]\n    [faction-actions view-ctx]]\n   [:div.col-md-10\n    [faction-status view-ctx]\n    [board view-ctx]\n    [status-info view-ctx]]])\n\n(defn app-root [{:as view-ctx :keys [conn dispatch translate]}]\n  [:div\n   [new-game-settings view-ctx]\n   [faction-settings view-ctx]\n   [unit-picker view-ctx]\n   [end-turn-alert view-ctx]\n   ;; TODO: break win dialog out into it's own component\n   ;; TODO: add continue + start new game buttons to win dialog\n   [:> js/ReactBootstrap.Modal {:show @(subs/show-win-message? conn)\n                                :on-hide #(dispatch [::events.ui/hide-win-message])}\n    [:> js/ReactBootstrap.Modal.Header\n     [:> js/ReactBootstrap.Modal.Title\n      (translate :win-title)]]\n    [:> js/ReactBootstrap.Modal.Body\n     {:dangerouslySetInnerHTML {:__html (translate :win-body)}}]\n    [:> js/ReactBootstrap.Modal.Footer\n     [:button.btn.btn-default {:on-click #(dispatch [::events.ui/hide-win-message])}\n      (translate :close-button)]]]\n   (navbar \"Game\")\n   [:div.container\n    [alert view-ctx]\n    [game-interface view-ctx]]\n   (footer)])\n"
  },
  {
    "path": "src/cljs/zetawar/workspaces/test_cards.cljs",
    "content": "(ns zetawar.workspaces.test-cards\n  (:require\n   [\"react\" :as react]\n   [nubank.workspaces.card-types.react :as ct.react]\n   [nubank.workspaces.core :as ws]))\n\n;; simple function to create react elemnents\n(defn element [name props & children]\n  (apply react/createElement name (clj->js props) children))\n\n(ws/defcard hello-card\n  (ct.react/react-card\n   (element \"div\" {} \"Hello World\")))\n"
  },
  {
    "path": "src/js/custom_ai.js",
    "content": "ZetawarAI = (function() {\n    var ZetawarAI = {};\n\n    ZetawarAI.makeActorContext = function(db, game, actor) {\n        return {};\n    };\n\n    ZetawarAI.scoreActor = function(db, game, actor, actor_ctx) {\n        if (zetawar.js.game.is_base(actor)) {\n            return 100 + Math.floor(Math.random() * 100);\n        }\n        return Math.floor(Math.random() * 100);\n    };\n\n    ZetawarAI.makeBaseActionContext = function(db, game, actor_ctx, base) {\n        return actor_ctx;\n    };\n\n    ZetawarAI.scoreBaseAction = function(db, game, base, actor_ctx, action) {\n        return Math.floor(Math.random() * 200);\n    };\n\n    ZetawarAI.makeUnitActionContext = function(db, game, actor_ctx, unit) {\n        actor_ctx['closest_base'] = zetawar.js.game.closest_capturable_base(db, game, unit);\n        actor_ctx['closest_enemy'] = zetawar.js.game.closest_enemy(db, game, unit);\n        return actor_ctx;\n    };\n\n    ZetawarAI.scoreUnitAction = function(db, game, unit, action_ctx, action) {\n        var closestBase = action_ctx.closest_base;\n        var closestEnemy = action_ctx.closest_enemy;\n        var ret = 0;\n\n        switch (action[\"type\"]) {\n        case \"capture-base\":\n            return 200;\n        case \"attack-unit\":\n            return 100;\n        case \"move-unit\":\n            if (closestBase) {\n                [baseQ, baseR] = zetawar.js.game.terrain_hex(closestBase);\n                toQ = action[\"to-q\"];\n                toR = action[\"to-r\"];\n                distanceFromBase = zetawar.js.hex.distance(baseQ, baseR, toQ, toR);\n                ret = 100  - distanceFromBase;\n            } else {\n                [enemyQ, enemyR] = zetawar.js.game.unit_hex(closestEnemy);\n                toQ = action[\"to-q\"];\n                toR = action[\"to-r\"];\n                distanceFromEnemy = zetawar.js.hex.distance(enemyQ, enemyR, toQ, toR);\n                ret = 100  - distanceFromEnemy;\n            }\n        }\n\n        return ret;\n    };\n\n    return ZetawarAI;\n})();\n"
  },
  {
    "path": "src/js/lzw.js",
    "content": "// From: https://gist.github.com/revolunet/843889\n\n// LZW-compress a string\nfunction lzwEncode(s) {\n  var dict = {};\n  var data = (s + \"\").split(\"\");\n  var out = [];\n  var currChar;\n  var phrase = data[0];\n  var code = 256;\n  for (var i = 1; i < data.length; i++) {\n    currChar = data[i];\n    if (dict['_' + phrase + currChar] != null) {\n      phrase += currChar;\n    }\n    else {\n      out.push(phrase.length > 1 ? dict['_' + phrase] : phrase.charCodeAt(0));\n      dict['_' + phrase + currChar] = code;\n      code++;\n      phrase = currChar;\n    }\n  }\n  out.push(phrase.length > 1 ? dict['_' + phrase] : phrase.charCodeAt(0));\n  for (var i=0; i<out.length; i++) {\n    out[i] = String.fromCharCode(out[i]);\n  }\n  return out.join(\"\");\n}\n\n// Decompress an LZW-encoded string\nfunction lzwDecode(s) {\n  var dict = {};\n  var data = (s + \"\").split(\"\");\n  var currChar = data[0];\n  var oldPhrase = currChar;\n  var out = [currChar];\n  var code = 256;\n  var phrase;\n  for (var i = 1; i < data.length; i++) {\n    var currCode = data[i].charCodeAt(0);\n    if (currCode < 256) {\n      phrase = data[i];\n    }\n    else {\n      phrase = dict['_' + currCode] ? dict['_' + currCode] : (oldPhrase + currChar);\n    }\n    out.push(phrase);\n    currChar = phrase.charAt(0);\n    dict['_' + code] = oldPhrase + currChar;\n    code++;\n    oldPhrase = phrase;\n  }\n  return out.join(\"\");\n}\n"
  },
  {
    "path": "src/scss/main.scss",
    "content": "@import \"bootstrap/bootstrap\";\n@import \"font-awesome/font-awesome\";\n\n///////////////////////////////////////////////////////////////////////////////\n// Global\n\nbody {\n    padding-top: 70px;\n}\n\n.navbar-brand img {\n    display: inline;\n    margin: 0 12px;\n}\n\n#footer {\n    text-align: center;\n    margin-top: 20px;\n    padding: 5px;\n    border-top: 1px solid #e5e5e5;\n    color: #888;\n}\n\n.noselect {\n    @include user-select(none);\n}\n\n.clickable {\n    @include user-select(none);\n    cursor: pointer;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// Game\n\nsvg {\n    image-rendering: -moz-crisp-edges;         /* Firefox */\n    image-rendering:   -o-crisp-edges;         /* Opera */\n    image-rendering: -webkit-optimize-contrast;/* Webkit (non-standard naming) */\n    image-rendering: crisp-edges;\n    -ms-interpolation-mode: nearest-neighbor;  /* IE (non-standard property) */\n}\n\n#faction-status {\n    border-bottom: 1px solid #e5e5e5;\n    padding-bottom: 5px;\n    margin-bottom: 10px;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n// Blog\n\n.blog-post {\n    margin-bottom: 60px;\n}\n\n.blog-post-title {\n    margin-bottom: 5px;\n    font-size: 40px;\n}\n\n.blog-post-meta {\n    margin-bottom: 20px;\n    color: #999;\n}\n\n.blog-post-meta a {\n    color: #999;\n}\n"
  },
  {
    "path": "test/cljs/zetawar/game_test.cljs",
    "content": "(ns zetawar.game-test\n  (:require\n   [cljs.test :refer-macros [deftest testing is async use-fixtures]]\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.data :as data]\n   [zetawar.db :as db :refer [e qe]]\n   [zetawar.game :as game]\n   [zetawar.test-helper :as helper]\n   [zetawar.util :refer [breakpoint inspect]]))\n\n;; TODO: use fixtures\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Util\n\n(deftest test-game-pos-idx\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (integer? (game/game-pos-idx game 1 2)))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Game\n\n(deftest test-game-by-id\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (= game (game/game-by-id db (:game/id game))))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Terrain\n\n(deftest test-terrain?\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)\n        terrain (game/terrain-at db game 1 0)]\n    (testing \"a unit is not a terrain\"\n      (is (not (game/terrain? unit))))\n    (testing \"a terrain is a terrain\"\n      (is (game/terrain? terrain)))))\n\n(deftest test-terrain-hex\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        terrain (game/terrain-at db game 1 0)]\n    (is (= [1 0] (game/terrain-hex terrain)))))\n\n(deftest test-terrain-at\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (nil? (game/base-at db game 99 99)))\n    (is (game/terrain? (game/base-at db game 1 2)))))\n\n(deftest test-checked-terrain-at\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (thrown? ExceptionInfo (game/checked-terrain-at db game 99 99)))\n    (is (game/terrain? (game/checked-terrain-at db game 1 2)))))\n\n(deftest test-base-at\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (nil? (game/base-at db game 1 0)))\n    (is (game/base? (game/base-at db game 1 2)))))\n\n(deftest test-checked-base-at\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (thrown? ExceptionInfo (game/checked-base-at db game 1 0)))\n    (is (game/base? (game/checked-base-at db game 1 2)))))\n\n(deftest test-check-base-current\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        current-base (game/base-at db game 1 2)\n        unowned-base (game/base-at db game 2 1)\n        enemy-base (game/base-at db game 7 6)]\n    (is (nil? (game/check-base-current db game current-base)))\n    (is (thrown? ExceptionInfo (game/check-base-current db game unowned-base)))\n    (is (thrown? ExceptionInfo (game/check-base-current db game enemy-base)))))\n\n(deftest test-check-current-base?\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        current-base (game/base-at db game 1 2)\n        unowned-base (game/base-at db game 2 1)\n        enemy-base (game/base-at db game 7 6)]\n    (is (game/current-base? db game current-base))\n    (is (not (game/current-base? db game unowned-base)))\n    (is (not (game/current-base? db game enemy-base)))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Units\n\n(deftest test-unit?\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)\n        terrain (game/terrain-at db game 1 0)]\n    (is (not (game/unit? terrain)))\n    (is (game/unit? unit))))\n\n(deftest test-unit-hex\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (is (= [2 2] (game/unit-hex unit)))))\n\n(deftest test-unit-at\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (nil? (game/unit-at db game 99 99)))\n    (is (game/unit? (game/unit-at db game 2 2)))))\n\n(deftest test-checked-unit-at\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (is (thrown? ExceptionInfo (game/checked-unit-at db game 99 99)))\n    (is (game/unit? (game/checked-unit-at db game 2 2)))))\n\n(deftest test-unit-faction\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (is (= :faction.color/blue\n           (:faction/color (game/unit-faction db unit))))))\n\n(deftest test-check-unit-current\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        current-unit (game/unit-at db game 2 2)\n        enemy-unit (game/unit-at db game 7 7) ]\n    (is (= nil (game/check-unit-current db game current-unit)))\n    (is (thrown? ExceptionInfo (game/check-unit-current db game enemy-unit)))))\n\n(deftest test-unit-current?\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        current-unit (game/unit-at db game 2 2)\n        enemy-unit (game/unit-at db game 7 7) ]\n    (is (game/unit-current? db game current-unit))\n    (is (not (game/unit-current? db game enemy-unit)))))\n\n(deftest test-on-base?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game @conn) ]\n    (d/transact conn (game/teleport-tx @conn game 2 2 1 2))\n    (let [db @conn]\n      (is (game/on-base? db game (game/unit-at db game 1 2)))\n      (is (not (game/on-base? db game (game/unit-at db game 7 7)))))))\n\n(deftest test-on-capturable-base?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game @conn) ]\n    (d/transact conn (game/teleport-tx @conn game 2 2 1 2))\n    (is (not (game/on-capturable-base? @conn game (game/unit-at @conn game 1 2))))\n    (d/transact conn (game/teleport-tx @conn game 1 2 2 1))\n    (is (game/on-capturable-base? @conn game (game/unit-at @conn game 2 1)))\n    (d/transact conn (game/teleport-tx @conn game 2 1 7 6))\n    (is (game/on-capturable-base? @conn game (game/unit-at @conn game 7 6)))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Movement\n\n(def valid-destinations\n  #{[0 1] [3 4] [1 2] [1 5] [3 2] [2 4] [4 2] [1 3] [2 3] [3 1]\n    [0 2] [3 0] [1 1] [3 3] [1 4] [0 3] [2 1] [4 4] [1 0] [2 0]\n    [2 5]})\n\n(deftest test-valid-moves\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (is (= #{:to :from :cost :path}\n           (into #{} (-> (game/valid-moves db game unit)\n                         first\n                         keys))))\n    (is (= valid-destinations\n           (into #{}\n                 (map :to)\n                 (game/valid-moves db game unit))))))\n\n(deftest test-valid-destinations\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (is (= valid-destinations (game/valid-destinations db game unit)))))\n\n;; TODO: add test for check-valid-destination\n\n(deftest test-valid-destination?\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (is (= (count valid-destinations)\n           (count (filter #(apply game/valid-destination? db game unit %)\n                          valid-destinations))))\n    (is (= false (game/valid-destination? db game unit 6 6)))))\n\n(deftest test-can-move-fns\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n\n    ;; TODO: update for state machine changes\n\n    #_(testing \"units that have not yet performed any actions can move\"\n        (is (= nil (game/check-can-move db game unit)))\n        (is (= true (game/can-move? db game unit))))\n    #_(testing \"newly built units cannot move\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/round-built 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-move db' game unit')))\n          (is (= false (game/can-move? db' game unit')))))\n    #_(testing \"moved units cannot move again\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/move-count 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-move db' game unit')))\n          (is (= false (game/can-move? db' game unit')))))\n    #_(testing \"units that have already attacked cannot move\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/attack-count 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-move db' game unit')))\n          (is (= false (game/can-move? db' game unit')))))\n    #_(testing \"capturing units cannot move\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/capturing true]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-move db' game unit')))\n          (is (= false (game/can-move? db' game unit')))))\n    #_(testing \"repaired units cannot move\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/repaired true]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-move db' game unit')))\n          (is (= false (game/can-move? db' game unit')))))\n\n    ))\n\n(deftest test-teleport-tx\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (let [db' (d/db-with db (game/teleport-tx db game 2 2 4 4))\n          teleported-unit (game/unit-at db' game 4 4)]\n      (is (= (e unit) (e teleported-unit)))\n      (is (= 0 (:unit/move-count teleported-unit))))\n\n    ;; TODO: test moving to an invalid destination\n\n    ))\n\n(deftest test-move-tx\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (testing \"moving to a valid destination\"\n      (let [db' (d/db-with db (game/move-tx db game 2 2 4 4))\n            moved-unit (game/unit-at db' game 4 4)]\n        (is (= (e unit) (e moved-unit)))\n        (is (= 1 (:unit/move-count moved-unit)))))\n\n    ;; TODO: test moving to an invalid destination\n\n    ))\n\n(deftest test-move!\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        game-id (app/current-game-id db)\n        unit (game/unit-at db game 2 2)]\n    (game/move! conn game-id 2 2 4 4)\n    (is (= (e unit) (e (game/unit-at @conn game 4 4))))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Attack\n\n(deftest test-can-attack-fns\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n\n    ;; TODO: update for state machine changes\n\n    #_(testing \"units that have not yet performed any actions can attack\"\n        (is (= nil (game/check-can-attack db game unit)))\n        (is (= true (game/can-attack? db game unit))))\n    #_(testing \"newly built units cannot attack\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/round-built 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-attack db' game unit')))\n          (is (= false (game/can-attack? db' game unit')))))\n    #_(testing \"moved units can attack\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/move-count 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (= nil (game/check-can-attack db' game unit')))\n          (is (= true (game/can-attack? db' game unit')))))\n    #_(testing \"units that have already attacked cannot attack again\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/attack-count 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-attack db' game unit')))\n          (is (= false (game/can-attack? db' game unit')))))\n    #_(testing \"capturing units cannot attack\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/capturing true]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-attack db' game unit')))\n          (is (= false (game/can-attack? db' game unit')))))\n    #_(testing \"repaired units cannot attack\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/repaired true]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-attack db' game unit')))\n          (is (= false (game/can-attack? db' game unit')))))\n\n    ))\n\n(deftest test-in-range-fns\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)]\n    (testing \"can attack units that are in range\"\n      (let [db' (d/db-with db (game/teleport-tx db game 7 8 3 2))\n            attacker (game/unit-at db' game 2 2)\n            defender (game/unit-at db' game 3 2)]\n        (is (= nil (game/check-in-range db' attacker defender)))\n        (is (= true (game/in-range? db' attacker defender)))))\n    (testing \"cannot attack units that are out of range\"\n      (let [attacker (game/unit-at db game 2 2)\n            defender (game/unit-at db game 7 8)]\n        (is (thrown? ExceptionInfo (game/check-in-range db attacker defender)))\n        (is (= false (game/in-range? db attacker defender)))))))\n\n(deftest test-attack-tx\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game @conn)\n        db (d/db-with @conn (game/teleport-tx @conn game 7 8 3 2))]\n    (testing \"attacking unit in range\"\n      (let [db' (d/db-with db (game/attack-tx db game 2 2 3 2))\n            attacker (game/unit-at db' game 2 2)\n            defender (game/unit-at db' game 3 2)]\n        (is (< (:unit/count attacker) 10))\n        (is (= (:unit/attack-count attacker) 1))\n        (is (< (:unit/count defender) 10))\n        (is (= (:unit/attack-count defender) 0))))))\n\n;; TODO: test attack!\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Repair\n\n(deftest test-repair-checking\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)]\n    (testing \"undamaged units cannot be repaired\"\n      (is (thrown? ExceptionInfo (game/check-can-repair db game unit)))\n      (is (= false (game/can-repair? db game unit))))\n\n    ;; TODO: update for state machine changes\n\n    #_(testing \"damaged units that have not performed any actions can be repaired\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/count 9]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (= nil (game/check-can-repair db' game unit')))\n          (is (= true (game/can-repair? db' game unit')))))\n    #_(testing \"newly built units cannot be repaired\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/round-built 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-repair db' game unit')))\n          (is (= false (game/can-repair? db' game unit')))))\n    #_(testing \"moved units cannot be repaired\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/move-count 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-repair db' game unit')))\n          (is (= false (game/can-repair? db' game unit')))))\n    #_(testing \"units that have already attacked cannot be repaired\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/attack-count 1]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-repair db' game unit')))\n          (is (= false (game/can-repair? db' game unit')))))\n    #_(testing \"capturing units cannot be repaired\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/capturing true]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-repair db' game unit')))\n          (is (= false (game/can-repair? db' game unit')))))\n    #_(testing \"already repaired units cannot be repaired\"\n        (let [db' (d/db-with db [[:db/add (e unit) :unit/repaired true]])\n              unit' (game/unit-at db' game 2 2)]\n          (is (thrown? ExceptionInfo (game/check-can-repair db' game unit')))\n          (is (= false (game/can-repair? db' game unit')))))\n\n    ))\n\n(deftest test-repair-tx\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)\n        damaged-db (-> db\n                       (d/db-with (game/teleport-tx db game 2 2 1 2))\n                       (d/db-with [[:db/add (e unit) :unit/count 9]]))\n        damaged-unit (game/unit-at damaged-db game 1 2)]\n    (testing \"repairing damaged unit\"\n      (let [repaired-db (d/db-with damaged-db (game/repair-tx damaged-db game damaged-unit))\n            repaired-unit (game/unit-at repaired-db game 1 2)]\n        (is (= (:unit/count repaired-unit) 10))))))\n\n;; TODO: test repair!\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Capture\n\n(deftest test-can-capture-fns\n  (let [db @(helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)\n        terrain (game/terrain-at db game 2 2)]\n    (testing \"unit not on a base cannot capture\"\n      (is (thrown? ExceptionInfo (game/check-capturable db game unit terrain)))\n      (is (= false (game/can-capture? db game unit terrain))))\n    (testing \"unit on already owned base cannot capture\"\n      (let [db' (d/db-with db (game/teleport-tx db game 2 2 1 2))\n            unit' (game/unit-at db' game 1 2)\n            terrain' (game/terrain-at db game 1 2)]\n        (is (thrown? ExceptionInfo (game/check-capturable db' game unit' terrain')))\n        (is (= false (game/can-capture? db' game unit' terrain')))))\n\n    ;; TODO: update for state machine changes\n\n    #_(testing \"unit on unowned base can capture\"\n        (let [db' (d/db-with db (game/teleport-tx db game 2 2 2 1))\n              unit' (game/unit-at db' game 2 1)\n              terrain' (game/terrain-at db' game 2 1)]\n          (is (= nil (game/check-capturable db' game unit' terrain')))\n          (is (= true (game/can-capture? db' game unit' terrain')))))\n    #_(testing \"unit on enemy base can capture\"\n        (let [db' (d/db-with db (game/teleport-tx db game 2 2 7 6))\n              unit' (game/unit-at db' game 7 6)\n              terrain' (game/terrain-at db' game 7 6)]\n          (is (= nil (game/check-capturable db' game unit' terrain')))\n          (is (= true (game/can-capture? db' game unit' terrain')))))\n    #_(testing \"units that have attacked cannot capture\"\n        (let [db' (d/db-with db (concat (game/teleport-tx db game 2 2 2 1)\n                                        [[:db/add (e unit) :unit/attack-count 1]]))\n              unit' (game/unit-at db' game 2 1)\n              terrain' (game/terrain-at db' game 2 1)]\n          (is (thrown? ExceptionInfo (game/check-capturable db' game unit' terrain')))\n          (is (= false (game/can-capture? db' game unit' terrain')))))\n    #_(testing \"repaired units cannot capture\"\n        (let [db' (d/db-with db (concat (game/teleport-tx db game 2 2 2 1)\n                                        [[:db/add (e unit) :unit/repaired true]]))\n              unit' (game/unit-at db' game 2 1)\n              terrain' (game/terrain-at db' game 2 1)]\n          (is (thrown? ExceptionInfo (game/check-capturable db' game unit' terrain')))\n          (is (= false (game/can-capture? db' game unit' terrain')))))\n\n    (testing \"already capturing units cannot capture\"\n      (let [db' (d/db-with db (concat (game/teleport-tx db game 2 2 2 1)\n                                      [[:db/add (e unit) :unit/capturing true]]))\n            unit' (game/unit-at db' game 2 1)\n            terrain' (game/terrain-at db' game 2 1)]\n        (is (thrown? ExceptionInfo (game/check-capturable db' game unit' terrain')))\n        (is (= false (game/can-capture? db' game unit' terrain')))))))\n\n(deftest test-capture-tx\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        unit (game/unit-at db game 2 2)\n        moved-db (d/db-with db (game/teleport-tx db game 2 2 2 1))\n        moved-unit (game/unit-at moved-db game 2 1)]\n    (testing \"capturing unowned base\"\n      (let [capturing-db (d/db-with moved-db (game/capture-tx moved-db game moved-unit))\n            capturing-unit (game/unit-at capturing-db game 2 1)]\n        (is (= (:unit/capturing capturing-unit) true))\n        (is (= (:unit/capture-round capturing-unit) 2))))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Build\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; End Turn\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Setup\n\n(comment\n\n  (defcard terrain-types-tx\n    (let [terrains-spec (select-keys data/ruleset [:terrains :plains])]\n      {:terrains-spec terrains-spec\n       :terrain-types-tx (game/terrain-types-tx terrains-spec)}))\n\n  (defcard units-spec-tx\n    (let [conn (d/create-conn db/schema)\n          units-spec (select-keys data/ruleset [:units :infantry])]\n      (d/transact! conn (game/terrain-types-tx (:terains data/ruleset)))\n      {:units-spec units-spec\n       :unit-types-tx (game/unit-types-tx @conn units-spec)}))\n\n  (deftest test-map-tx\n    (let [game-id (random-uuid)]\n      (is (= nil (game/map-tx game-id (:sterlings-aruba data/maps))))\n      ))\n\n  )\n"
  },
  {
    "path": "test/cljs/zetawar/subs_test.cljs",
    "content": "(ns zetawar.subs-test\n  (:require\n   [cljs.test :refer-macros [deftest testing is async use-fixtures]]\n   [datascript.core :as d]\n   [zetawar.app :as app]\n   [zetawar.db :refer [e qe]]\n   [zetawar.game :as game]\n   [zetawar.subs :as subs]\n   [zetawar.test-helper :as helper]))\n\n;; TODO: use fixtures\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; App\n\n(deftest test-app-eid\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        app-eid (ffirst (d/q '[:find ?a\n                               :where\n                               [?a :app/game]]\n                             @conn))]\n    (testing \"returned app-eid matches queried app-eid\"\n      (is (= app-eid @(subs/app-eid conn))))))\n\n(deftest test-app\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)]\n    (testing \"returned app has expected attributes\"\n      (is (:app/game @(subs/app conn))))))\n\n;; TODO: add show-win-message? test\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Game\n\n(deftest test-game-eid\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        game-eid (ffirst (d/q '[:find ?g\n                                :where\n                                [_ :app/game ?g]]\n                              @conn))]\n    (testing \"game-eid returned by subscription is the same as queried game-eid\"\n      (is (= game-eid @(subs/game-eid conn))))))\n\n(deftest test-game\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)]\n    (testing \"game returned by subscription has expected attributes\"\n      (is  (= #{:db/id\n                :game/id\n                :game/scenario-id\n                :game/self-repair\n                :game/map\n                :game/credits-per-base\n                :game/max-count-per-unit\n                :game/starting-faction\n                :game/current-faction\n                :game/factions\n                :game/round}\n              (into #{} (keys @(subs/game conn))))))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Map\n\n(deftest test-game-map-eid\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        map-eid (ffirst (d/q '[:find ?m\n                               :where\n                               [_ :app/game ?g]\n                               [?g :game/map ?m]]\n                             @conn))]\n    (testing \"game-map-eid returned by subscription is the same as queried game-map-eid\"\n      (is (= map-eid @(subs/game-map-eid conn))))))\n\n(deftest test-game-map\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)]\n    (testing \"game map returned by subscription has expected attributes\"\n      (is (= #{:db/id\n               :map/description}\n             (into #{} (keys @(subs/game-map conn))))))))\n\n(deftest test-terrains\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        terrains @(subs/terrains conn)]\n    (testing \"returns expected number of terrains\"\n      (is (= 74 (count terrains))))))\n\n(deftest test-current-base-locations\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        terrains @(subs/terrains conn)]\n    (testing \"returns the locations of all the current faction's bases\"\n      (is (= #{[1 2]} @(subs/current-base-locations conn))))))\n\n(deftest test-current-base?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        terrains @(subs/terrains conn)]\n    (testing \"returns true given the coordinates of a base owned by the current faction\"\n      (is (= true @(subs/current-base? conn 1 2))))\n    (testing \"returns false given the coordinates of an unowned base\"\n      (is (= false @(subs/current-base? conn 2 1))))\n    (testing \"returns false given the coordinates of an enemy base\"\n      (is (= false @(subs/current-base? conn 7 6))))\n    (testing \"returns false given the coordinates of a non-base terrain\"\n      (is (= false @(subs/current-base? conn 7 6))))))\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Factions\n\n(deftest test-current-faction-eid\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        faction-eid (ffirst (d/q '[:find ?f\n                                   :where\n                                   [_ :app/game ?g]\n                                   [?g :game/current-faction ?f]]\n                                 @conn))]\n    (testing \"returned eid is the same as queried eid\"\n      (is (= faction-eid @(subs/current-faction-eid conn))))))\n\n(deftest test-current-faction\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)]\n    (testing \"returned faction has expected attributes\"\n      (is (= #{:db/id\n               :faction/color\n               :faction/credits\n               :faction/next-faction\n               :faction/ai\n               :faction/order}\n             (into #{} (keys @(subs/current-faction conn))))))))\n\n(deftest test-current-unit-count\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)]\n    (testing \"returns the count of units owned by the current faction (blue)\"\n      (is (= 1 @(subs/current-unit-count conn))))\n    (game/end-turn! conn (app/current-game-id @conn))\n    (testing \"returns the count of units owned by the current faction (red)\"\n      (is (= 2 @(subs/current-unit-count conn))))))\n\n(deftest test-current-base-count\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)]\n    (testing \"returns the count of bases owned by the current faction\"\n      (is (= 1 @(subs/current-unit-count conn))))))\n\n(deftest test-unit-eid-at\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns eid for unit at specified coordinates\"\n      (is (= (:db/id (game/unit-at db game 2 2))\n             @(subs/unit-eid-at conn 2 2))))\n    (testing \"returns nil if there is no unit at the specified coordinates\"\n      (is (= (:db/id (game/unit-at db game 4 4))\n             @(subs/unit-eid-at conn 4 4))))))\n\n(deftest test-current-unit-eid-at\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns eid for current unit at specified coordinates\"\n      (is (= (:db/id (game/unit-at db game 2 2))\n             @(subs/current-unit-eid-at conn 2 2))))\n    (testing \"returns nil if unit at specified coordinates is an enemy\"\n      (is (= nil @(subs/current-unit-eid-at conn 7 8))))\n    (testing \"returns nil if there is no unit at the specified coordinates\"\n      (is (= nil @(subs/current-unit-eid-at conn 4 4))))))\n\n(deftest test-unit-at?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns true if there is a unit at the specified cordinates\"\n      (is (= true @(subs/unit-at? conn 2 2))))\n    (testing \"returns false if there is no unit at the specified coordinates\"\n      (is (= false @(subs/unit-at? conn 4 4))))))\n\n(deftest test-current-unit-at?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns true for current unit at specified coordinates\"\n      (is (= true @(subs/current-unit-at? conn 2 2))))\n    (testing \"returns false if unit at specified coordinates is an enemy\"\n      (is (= false @(subs/current-unit-at? conn 7 8))))\n    (testing \"returns false if there is no unit at the specified coordinates\"\n      (is (= false @(subs/current-unit-at? conn 4 4))))))\n\n(deftest test-unit-at\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returned unit has expected attributes\"\n      (let [unit @(subs/unit-at conn 2 2)\n            unit-keys (keys unit)\n            unit-type-keys (keys (:unit/type unit))]\n        (is (= #{:db/id\n                 :unit/q\n                 :unit/r\n                 :unit/round-built\n                 :unit/move-count\n                 :unit/attack-count\n                 :unit/count\n                 :unit/repaired\n                 :unit/capturing\n                 :unit/type\n                 :faction/_units}\n               (into #{} unit-keys)))\n        (is (= #{:db/id\n                 :unit-type/id\n                 :unit-type/description\n                 :unit-type/min-range\n                 :unit-type/max-range\n                 :unit-type/can-capture\n                 :unit-type/armor-type\n                 :unit-type/image}\n               (into #{} unit-type-keys)))))))\n\n(deftest test-unit-color-at\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns the color for unit at the specified coordinates\"\n      (is (= :faction.color/blue @(subs/unit-color-at conn 2 2))))))\n\n(deftest test-unit-type-at\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns the type name for unit at the specified coordinates\"\n      (is (= :unit-type.id/infantry @(subs/unit-type-at conn 2 2))))))\n\n(deftest test-enemy-locations\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns the locations of all enemy units\"\n      (is (= #{[7 7] [7 8]} @(subs/enemy-locations conn))))))\n\n(deftest test-enemy-at?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns true if there is an enemy at the specified coordinates\"\n      (is (= true @(subs/enemy-at? conn 7 7))))\n    (testing \"returns false if there is not an enemy at the specified coordinates\"\n      (is (= false @(subs/enemy-at? conn 4 4))))))\n\n(deftest test-enemy-locations-in-range-of\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns enemy locations in range of unit at specified coordinates\"\n      (is (= #{} @(subs/enemy-locations-in-range-of conn 2 2)))\n      (d/transact! conn (game/teleport-tx db game 2 2 6 8))\n      (is (= #{[7 8]} @(subs/enemy-locations-in-range-of conn 6 8))))))\n\n(deftest test-any-enemy-in-range-of?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)]\n    (testing \"returns false if no enemies are in range of unit at specified coordinates\"\n      (is (= false @(subs/any-enemy-in-range-of? conn 2 2))))\n    (testing  \"returns true if enemies are in range of unit at specified coordinates\"\n      (d/transact! conn (game/teleport-tx db game 2 2 6 8))\n      (is (= true @(subs/any-enemy-in-range-of? conn 6 8))))))\n\n;; TODO: implement check tests\n\n;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n;;; Selection\n\n(deftest test-selected?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (d/transact! conn [{:db/id (e app)\n                        :app/selected-q 1\n                        :app/selected-r 2}])\n    (testing \"returns true if specified coordinates are selected\"\n      (is (= true @(subs/selected? conn 1 2))))\n    (testing \"returns false if specified coordinates are selected\"\n      (is (= false @(subs/selected? conn 2 2))))))\n\n(deftest test-targeted?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (d/transact! conn [{:db/id (e app)\n                        :app/targeted-q 1\n                        :app/targeted-r 2}])\n    (testing \"returns true if specified coordinates are targeted\"\n      (is (= true @(subs/targeted? conn 1 2))))\n    (testing \"returns false if specified coordinates are targeted\"\n      (is (= false @(subs/targeted? conn 2 2))))))\n\n(deftest test-selected-hex\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (testing \"returns selected coordinates\"\n      (is (= nil @(subs/selected-hex conn)))\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 1\n                          :app/selected-r 2}])\n      (is (= [1 2] @(subs/selected-hex conn)))\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 2\n                          :app/selected-r 1}])\n      (is (= [2 1] @(subs/selected-hex conn))))))\n\n(deftest test-targeted-hex\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (testing \"returns selected coordinates\"\n      (is (= nil @(subs/targeted-hex conn)))\n      (d/transact! conn [{:db/id (e app)\n                          :app/targeted-q 1\n                          :app/targeted-r 2}])\n      (is (= [1 2] @(subs/targeted-hex conn)))\n      (d/transact! conn [{:db/id (e app)\n                          :app/targeted-q 2\n                          :app/targeted-r 1}])\n      (is (= [2 1] @(subs/targeted-hex conn))))))\n\n;; TODO: combine with copy in game-test\n(def valid-destinations\n  #{[0 1] [3 4] [1 2] [1 5] [3 2] [2 4] [4 2] [1 3] [2 3] [3 1]\n    [0 2] [3 0] [1 1] [3 3] [1 4] [0 3] [2 1] [4 4] [1 0] [2 0]\n    [2 5]})\n\n(deftest test-valid-destinations-for-selected\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (testing \"returns valid destinations for selected unit\"\n      (is (= #{} @(subs/valid-destinations-for-selected conn)))\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 2\n                          :app/selected-r 2}])\n      (is (= valid-destinations @(subs/valid-destinations-for-selected conn))))))\n\n;; TODO: test valid-destination-for-selected?\n\n(deftest test-selected-can-move-to-targeted?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (testing \"returns true if selected unit can move to targeted location\"\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 2\n                          :app/selected-r 2\n                          :app/targeted-q 4\n                          :app/targeted-r 4}])\n      (is (= true @(subs/selected-can-move-to-targeted? conn))))\n    (testing \"returns false if selected unit cannot move to targeted location\"\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 2\n                          :app/selected-r 2\n                          :app/targeted-q 5\n                          :app/targeted-r 5}])\n      (is (= false @(subs/selected-can-move-to-targeted? conn))))))\n\n(deftest test-enemy-in-range-of-selected?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (testing \"returns true if selected unit is in range of enemy at specified coordinates\"\n      (d/transact! conn (conj (game/teleport-tx db game 2 2 6 8)\n                              {:db/id (e app)\n                               :app/selected-q 6\n                               :app/selected-r 8}))\n      (is (= true @(subs/enemy-in-range-of-selected? conn 7 8))))\n    (testing \"returns false if selected unit is not in range of enemy at specified coordinates\"\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 6\n                          :app/selected-r 8}])\n      (is (= false @(subs/enemy-in-range-of-selected? conn 7 7))))))\n\n(deftest test-selected-can-attack-targeted?\n  (let [conn (helper/create-scenario-conn :sterlings-aruba-multiplayer)\n        db @conn\n        game (app/current-game db)\n        app (app/root db)]\n    (testing \"returns true if selected unit can attack targeted unit\"\n      (d/transact! conn (conj (game/teleport-tx db game 2 2 6 8)\n                              {:db/id (e app)\n                               :app/selected-q 6\n                               :app/selected-r 8\n                               :app/targeted-q 7\n                               :app/targeted-r 8}))\n      (is (= true @(subs/selected-can-attack-targeted? conn))))\n    (testing \"returns false if selected unit cannot attack targeted unit\"\n      (d/transact! conn [{:db/id (e app)\n                          :app/selected-q 6\n                          :app/selected-r 8\n                          :app/targeted-q 7\n                          :app/targeted-r 7}])\n      (is (= false @(subs/selected-can-attack-targeted? conn))))))\n"
  },
  {
    "path": "test/cljs/zetawar/test_helper.cljs",
    "content": "(ns zetawar.test-helper\n  (:require\n   [datascript.core :as d]\n   [posh.reagent :as posh]\n   [reagent.core :as r]\n   [zetawar.app :as app]\n   [zetawar.db :as db]\n   [zetawar.game :as game]))\n\n(defn create-scenario-conn [scenario-id]\n  (let [conn (d/create-conn db/schema)]\n    (posh/posh! conn)\n    (app/start-new-game! {:conn conn} scenario-id)\n    conn))\n"
  },
  {
    "path": "test/cljs/zetawar/test_runner.cljs",
    "content": "(ns ^:figwheel-hooks zetawar.test-runner\n  (:require\n   [cljs-test-display.core]\n   [cljs.test]\n   [zetawar.game-test]\n   [zetawar.subs-test]))\n\n(defn ^:after-load test-run []\n  (cljs.test/run-tests\n   (cljs-test-display.core/init! \"app\")\n   'zetawar.game-test\n   'zetawar.subs-test))\n\n(defonce runit (test-run))\n"
  }
]