From 1db3a14c5426abce0e1e06b7b5dfbe8a0cb20533 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Wed, 15 Jan 2020 09:29:11 +0000 Subject: [PATCH 01/35] Eta-reduce several calls to pcall --- .../assets/computercraft/lua/bios.lua | 29 +++++++++---------- .../computercraft/lua/rom/programs/lua.lua | 2 +- .../lua/rom/programs/rednet/chat.lua | 16 +++++----- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index 2913da090..500276da4 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -991,22 +991,19 @@ if fs.exists( ".settings" ) then end -- Run the shell -local ok, err = pcall( function() - parallel.waitForAny( - function() - local sShell - if term.isColour() and settings.get( "bios.use_multishell" ) then - sShell = "rom/programs/advanced/multishell.lua" - else - sShell = "rom/programs/shell.lua" - end - os.run( {}, sShell ) - os.run( {}, "rom/programs/shutdown.lua" ) - end, - function() - rednet.run() - end ) -end ) +local ok, err = pcall(parallel.waitForAny, + function() + local sShell + if term.isColour() and settings.get( "bios.use_multishell" ) then + sShell = "rom/programs/advanced/multishell.lua" + else + sShell = "rom/programs/shell.lua" + end + os.run( {}, sShell ) + os.run( {}, "rom/programs/shutdown.lua" ) + end, + rednet.run +) -- If the shell errored, let the user read it. term.redirect( term.native() ) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua b/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua index 63d4c6add..0a6a664fe 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua @@ -10,7 +10,7 @@ local bRunning = true local tCommandHistory = {} local tEnv = { ["exit"] = setmetatable({}, { - __tostring = function() return "Call exit() to exit" end, + __tostring = function() return "Call exit() to exit." end, __call = function() bRunning = false end, }), ["_echo"] = function( ... ) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua index 58a513f36..4c4ae75c0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua @@ -105,8 +105,8 @@ if sCommand == "host" then end -- Handle messages - local ok, error = pcall( function() - parallel.waitForAny( function() + local ok, error = pcall(parallel.waitForAny, + function() while true do local _, timer = os.pullEvent( "timer" ) local nUserID = tPingPongTimer[ timer ] @@ -223,8 +223,8 @@ if sCommand == "host" then end end end - end ) - end ) + end + ) if not ok then printError( error ) end @@ -332,8 +332,8 @@ elseif sCommand == "join" then drawTitle() - local ok, error = pcall( function() - parallel.waitForAny( function() + local ok, error = pcall(parallel.waitForAny, + function() while true do local sEvent, timer = os.pullEvent() if sEvent == "timer" then @@ -402,8 +402,8 @@ elseif sCommand == "join" then table.insert( tSendHistory, sChat ) end end - end ) - end ) + end + ) -- Close the windows term.redirect( parentTerm ) From c79f643ba71cb9a6e63e21aa6457949f7cf47d4d Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 17 Jan 2020 10:18:46 +0000 Subject: [PATCH 02/35] Remove redundant illuaminate options --- illuaminate.sexp | 3 --- 1 file changed, 3 deletions(-) diff --git a/illuaminate.sexp b/illuaminate.sexp index b37f34928..d6865618c 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -23,6 +23,3 @@ (linters -var:unused-global) (lint (allow-toplevel-global true))) - -;; These warnings are broken right now -(at (bios.lua worm.lua) (linters -control:unreachable)) From 798868427e8e03edf67976d7ff0cbfe67a8be856 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 17 Jan 2020 22:51:36 +0000 Subject: [PATCH 03/35] Use a Wadler style pretty printer in the Lua REPL (#334) - Add a cc.pretty module, which provides a Wadler style pretty printer [1]. - The cc.pretty.pretty function converts an arbitrary object into a pretty-printed document. This can then be printed to the screen with cc.pretty.{write, print} or converted to a string with cc.pretty.render. - Convert the Lua REPL to use the pretty printer rather than textutils.serialise. [1]: http://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf --- .../lua/rom/modules/main/cc/pretty.lua | 416 ++++++++++++++++++ .../computercraft/lua/rom/programs/lua.lua | 19 +- .../test-rom/spec/modules/cc/pretty_spec.lua | 208 +++++++++ 3 files changed, 630 insertions(+), 13 deletions(-) create mode 100644 src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua create mode 100644 src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua new file mode 100644 index 000000000..cf0007568 --- /dev/null +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -0,0 +1,416 @@ +--- Provides a "pretty printer", for rendering data structures in an +-- aesthetically pleasing manner. +-- +-- In order to display something using @{cc.pretty}, you build up a series of +-- @{documents|Doc}. These behave a little bit like strings; you can concatenate +-- them together and then print them to the screen. +-- +-- However, documents also allow you to control how they should be printed. There +-- are several functions (such as @{nest} and @{group}) which allow you to control +-- the "layout" of the document. When you come to display the document, the 'best' +-- (most compact) layout is used. +-- +-- @module cc.pretty +-- @usage Print a table to the terminal +-- local pretty = require "cc.pretty" +-- pretty.write(pretty.dump({ 1, 2, 3 })) +-- +-- @usage Build a custom document and display it +-- local pretty = require "cc.pretty" +-- pretty.write(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world"))) + +local expect = require "cc.expect".expect +local type, getmetatable, setmetatable, colours, str_write = type, getmetatable, setmetatable, colours, write + +--- @{table.insert} alternative, but with the length stored inline. +local function append(out, value) + local n = out.n + 1 + out[n], out.n = value, n +end + +--- A document, which +-- +-- Documents effectively represent a sequence of strings in alternative layouts, +-- which we will try to print in the most compact form necessary. +-- +-- @type Doc +local Doc = { } + +--- An empty document. +local empty = setmetatable({ tag = "nil" }, Doc) + +--- A document with a single space in it. +local space = setmetatable({ tag = "text", text = " " }, Doc) + +--- A line break. When collapsed with @{group}, this will be replaced with @{empty}. +local line = setmetatable({ tag = "line", flat = empty }, Doc) + +--- A line break. When collapsed with @{group}, this will be replaced with @{space}. +local space_line = setmetatable({ tag = "line", flat = space }, Doc) + +local text_cache = { [""] = empty, [" "] = space, ["\n"] = space_line } + +local function mk_text(text, colour) + return text_cache[text] or setmetatable({ tag = "text", text = text, colour = colour }, Doc) +end + +--- Create a new document from a string. +-- +-- If your string contains multiple lines, @{group} will flatten the string +-- into a single line, with spaces between each line. +-- +-- @tparam string text The string to construct a new document with. +-- @tparam[opt] number colour The colour this text should be printed with. If not given, we default to the current +-- colour. +-- @treturn Doc The document with the provided text. +local function text(text, colour) + expect(1, text, "string") + expect(2, colour, "number", "nil") + + local cached = text_cache[text] + if cached then return cached end + + local new_line = text:find("\n", 1) + if not new_line then return mk_text(text, colour) end + + -- Split the string by "\n". With a micro-optimisation to skip empty strings. + local doc = setmetatable({ tag = "concat", n = 0 }, Doc) + if new_line ~= 1 then append(doc, mk_text(text:sub(1, new_line - 1), colour)) end + + new_line = new_line + 1 + while true do + local next_line = text:find("\n", new_line) + append(doc, space_line) + if not next_line then + if new_line <= #text then append(doc, mk_text(text:sub(new_line), colour)) end + return doc + else + if new_line <= next_line - 1 then + append(doc, mk_text(text:sub(new_line, next_line - 1), colour)) + end + new_line = next_line + 1 + end + end +end + +--- Concatenate several documents together. This behaves very similar to string concatenation. + +-- @tparam Doc|string ... The documents to concatenate. +-- @treturn Doc The concatenated documents. +-- @usage pretty.concat(doc1, " - ", doc2) +-- @usage doc1 .. " - " .. doc2 +local function concat(...) + local args = table.pack(...) + for i = 1, args.n do + if type(args[i]) == "string" then args[i] = text(args[i]) end + if getmetatable(args[i]) ~= Doc then expect(i, args[i], "document") end + end + + if args.n == 0 then return empty end + if args.n == 1 then return args[1] end + + args.tag = "concat" + return setmetatable(args, Doc) +end + +Doc.__concat = concat + +--- Indent later lines of the given document with the given number of spaces. +-- +-- For instance, nesting the document +-- ```txt +-- foo +-- bar +-- `` +-- by two spaces will produce +-- ```txt +-- foo +-- bar +-- ``` +-- +-- @tparam number depth The number of spaces with which the document should be indented. +-- @tparam Doc doc The document to indent. +-- @treturn Doc The nested document. +-- @usage pretty.nest(2, pretty.text("foo\nbar")) +local function nest(depth, doc) + expect(1, depth, "number") + if getmetatable(doc) ~= Doc then expect(2, doc, "document") end + if depth <= 0 then error("depth must be a positive number", 2) end + + return setmetatable({ tag = "nest", depth = depth, doc }, Doc) +end + +local function flatten(doc) + if doc.flat then return doc.flat end + + local kind = doc.tag + if kind == "nil" or kind == "text" then + return doc + elseif kind == "concat" then + local out = setmetatable({ tag = "concat", n = doc.n }, Doc) + for i = 1, doc.n do out[i] = flatten(doc[i]) end + doc.flat, out.flat = out, out -- cache the flattened node + return out + elseif kind == "nest" then + return flatten(doc[1]) + elseif kind == "group" then + return doc[1] + else + error("Unknown doc " .. kind) + end +end + +--- Builds a document which is displayed on a single line if there is enough +-- room, or as normal if not. +-- +-- @tparam Doc doc The document to group. +-- @treturn Doc The grouped document. +local function group(doc) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + + if doc.tag == "group" then return doc end -- Skip if already grouped. + + local flattened = flatten(doc) + if flattened == doc then return doc end -- Also skip if flattening does nothing. + return setmetatable({ tag = "group", flattened, doc }, Doc) +end + +local function get_remaining(doc, width) + local kind = doc.tag + if kind == "nil" or kind == "line" then + return width + elseif kind == "text" then + return width - #doc.text + elseif kind == "concat" then + for i = 1, doc.n do + width = get_remaining(doc[i], width) + if width < 0 then break end + end + return width + elseif kind == "group" or kind == "nest" then + return get_remaining(kind[1]) + else + error("Unknown doc " .. kind) + end +end + +--- Display a document on the terminal. +-- +-- @tparam Doc doc The document to render +-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in. +local function write(doc, ribbon_frac) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + expect(2, ribbon_frac, "number", "nil") + + local term = term + local width, height = term.getSize() + local ribbon_width = (ribbon_frac or 0.6) * width + if ribbon_width < 0 then ribbon_width = 0 end + if ribbon_width > width then ribbon_width = width end + + local def_colour = term.getTextColour() + local current_colour = def_colour + + local function go(doc, indent, col) + local kind = doc.tag + if kind == "nil" then + return col + elseif kind == "text" then + local doc_colour = doc.colour or def_colour + if doc_colour ~= current_colour then + term.setTextColour(doc_colour) + current_colour = doc_colour + end + + str_write(doc.text) + + return col + #doc.text + elseif kind == "line" then + local _, y = term.getCursorPos() + if y < height then + term.setCursorPos(indent + 1, y + 1) + else + term.scroll(1) + term.setCursorPos(indent + 1, height) + end + + return indent + elseif kind == "concat" then + for i = 1, doc.n do col = go(doc[i], indent, col) end + return col + elseif kind == "nest" then + return go(doc[1], indent + doc.depth, col) + elseif kind == "group" then + if get_remaining(doc[1], math.min(width, ribbon_width + indent) - col) >= 0 then + return go(doc[1], indent, col) + else + return go(doc[2], indent, col) + end + else + error("Unknown doc " .. kind) + end + end + + local col = math.max(term.getCursorPos() - 1, 0) + go(doc, 0, col) + if current_colour ~= def_colour then term.setTextColour(def_colour) end +end + +--- Display a document on the terminal with a trailing new line. +-- +-- @tparam Doc doc The document to render. +-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in. +local function print(doc, ribbon_frac) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + expect(2, ribbon_frac, "number", "nil") + write(doc, ribbon_frac) + str_write("\n") +end + +--- Render a document, converting it into a string. +-- +-- @tparam Doc doc The document to render. +-- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to +-- fit this width - it is only used for finding the best layout. +-- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in. +local function render(doc, width, ribbon_frac) + if getmetatable(doc) ~= Doc then expect(1, doc, "document") end + expect(2, width, "number", "nil") + expect(3, ribbon_frac, "number", "nil") + + local ribbon_width + if width then + ribbon_width = (ribbon_frac or 0.6) * width + if ribbon_width < 0 then ribbon_width = 0 end + if ribbon_width > width then ribbon_width = width end + end + + local out = { n = 0 } + local function go(doc, indent, col) + local kind = doc.tag + if kind == "nil" then + return col + elseif kind == "text" then + append(out, doc.text) + return col + #doc.text + elseif kind == "line" then + append(out, "\n" .. (" "):rep(indent)) + return indent + elseif kind == "concat" then + for i = 1, doc.n do col = go(doc[i], indent, col) end + return col + elseif kind == "nest" then + return go(doc[1], indent + doc.depth, col) + elseif kind == "group" then + if not width or get_remaining(doc[1], math.min(width, ribbon_width + indent) - col) >= 0 then + return go(doc[1], indent, col) + else + return go(doc[2], indent, col) + end + else + error("Unknown doc " .. kind) + end + end + + go(doc, 0, 0) + return table.concat(out, "", 1, out.n) +end + +local keywords = { + [ "and" ] = true, [ "break" ] = true, [ "do" ] = true, [ "else" ] = true, + [ "elseif" ] = true, [ "end" ] = true, [ "false" ] = true, [ "for" ] = true, + [ "function" ] = true, [ "if" ] = true, [ "in" ] = true, [ "local" ] = true, + [ "nil" ] = true, [ "not" ] = true, [ "or" ] = true, [ "repeat" ] = true, [ "return" ] = true, + [ "then" ] = true, [ "true" ] = true, [ "until" ] = true, [ "while" ] = true, + } + +local comma = text(",") +local braces = text("{}") +local obrace, cbrace = text("{"), text("}") +local obracket, cbracket = text("["), text("] = ") + +local function key_compare(a, b) + local ta, tb = type(a), type(b) + + if ta == "string" then return tb ~= "string" or a < b + elseif tb == "string" then return false + end + + if ta == "number" then return tb ~= "number" or a < b end + return false +end + +local function pretty_impl(obj, tracking) + local obj_type = type(obj) + if obj_type == "string" then + local formatted = ("%q"):format(obj):gsub("\\\n", "\\n") + return text(formatted, colours.red) + elseif obj_type == "number" then + return text(tostring(obj), colours.magenta) + elseif obj_type ~= "table" or tracking[obj] then + return text(tostring(obj), colours.lightGrey) + elseif getmetatable(obj) ~= nil and getmetatable(obj).__tostring then + return text(tostring(obj)) + elseif next(obj) == nil then + return braces + else + tracking[obj] = true + local doc = setmetatable({ tag = "concat", n = 1, space_line }, Doc) + + local length, keys, keysn = #obj, {}, 1 + for k in pairs(obj) do keys[keysn], keysn = k, keysn + 1 end + table.sort(keys, key_compare) + + for i = 1, keysn - 1 do + if i > 1 then append(doc, comma) append(doc, space_line) end + + local k = keys[i] + local v = obj[k] + local ty = type(k) + if ty == "number" and k % 1 == 0 and k >= 1 and k <= length then + append(doc, pretty_impl(v, tracking)) + elseif ty == "string" and not keywords[k] and k:match("^[%a_][%a%d_]*$") then + append(doc, text(k .. " = ")) + append(doc, pretty_impl(v, tracking)) + else + append(doc, obracket) + append(doc, pretty_impl(k, tracking)) + append(doc, cbracket) + append(doc, pretty_impl(v, tracking)) + end + end + + tracking[obj] = nil + return group(concat(obrace, nest(2, concat(table.unpack(doc, 1, n))), space_line, cbrace)) + end +end + +--- Pretty-print an arbitrary object, converting it into a document. +-- +-- This can then be rendered with @{write} or @{print}. +-- +-- @param obj The object to pretty-print. +-- @treturn Doc The object formatted as a document. +-- @usage Display a table on the screen +-- local pretty = require "cc.pretty" +-- pretty.print(pretty.pretty({ 1, 2, 3 }) +local function pretty(obj) + return pretty_impl(obj, {}) +end + +return { + empty = empty, + space = space, + line = line, + space_line = space_line, + text = text, + concat = concat, + nest = nest, + group = group, + + write = write, + print = print, + render = render, + + pretty = pretty, +} diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua b/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua index 0a6a664fe..6514e6390 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua @@ -6,6 +6,8 @@ if #tArgs > 0 then return end +local pretty = require "cc.pretty" + local bRunning = true local tCommandHistory = {} local tEnv = { @@ -87,20 +89,11 @@ while bRunning do local n = 1 while n < tResults.n or n <= nForcePrint do local value = tResults[ n + 1 ] - if type( value ) == "table" then - local metatable = getmetatable( value ) - if type(metatable) == "table" and type(metatable.__tostring) == "function" then - print( tostring( value ) ) - else - local ok, serialised = pcall( textutils.serialise, value ) - if ok then - print( serialised ) - else - print( tostring( value ) ) - end - end + local ok, serialised = pcall(pretty.pretty, value) + if ok then + pretty.print(serialised) else - print( tostring( value ) ) + print(tostring(value)) end n = n + 1 end diff --git a/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua new file mode 100644 index 000000000..38b2ecc3c --- /dev/null +++ b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua @@ -0,0 +1,208 @@ +local with_window = require "test_helpers".with_window + +describe("cc.pretty", function() + local pp = require("cc.pretty") + + describe("text", function() + it("is constant for the empty string", function() + expect(pp.text("")):eq(pp.empty) + end) + + it("is constant for a space", function() + expect(pp.text(" ")):eq(pp.space) + end) + + it("is constant for a newline", function() + expect(pp.text("\n")):eq(pp.space_line) + end) + + it("validates arguments", function() + expect.error(pp.text, 123):eq("bad argument #1 (expected string, got number)") + expect.error(pp.text, "", ""):eq("bad argument #2 (expected number, got string)") + end) + + it("produces text documents", function() + expect(pp.text("a")):same({ tag = "text", text = "a" }) + expect(pp.text("a", colours.grey)):same({ tag = "text", text = "a", colour = colours.grey }) + end) + + it("splits lines", function() + expect(pp.text("a\nb")) + :same(pp.concat(pp.text("a"), pp.space_line, pp.text("b"))) + expect(pp.text("ab\ncd\nef")) + :same(pp.concat(pp.text("ab"), pp.space_line, pp.text("cd"), pp.space_line, pp.text("ef"))) + end) + + it("preserves empty lines", function() + expect(pp.text("a\n\nb")) + :same(pp.concat(pp.text("a"), pp.space_line, pp.space_line, pp.text("b"))) + expect(pp.text("\n\nb")) + :same(pp.concat(pp.space_line, pp.space_line, pp.text("b"))) + expect(pp.text("a\n\n")) + :same(pp.concat(pp.text("a"), pp.space_line, pp.space_line)) + end) + end) + + describe("concat", function() + it("returns empty with 0 arguments", function() + expect(pp.concat()):eq(pp.empty) + end) + + it("acts as the identity with 1 argument", function() + local x = pp.text("test") + expect(pp.concat(x)):eq(x) + end) + + it("coerces strings", function() + expect(pp.concat("a", "b")):same(pp.concat(pp.text("a"), pp.text("b"))) + end) + + it("validates arguments", function() + expect.error(pp.concat, 123):eq("bad argument #1 (expected document, got number)") + expect.error(pp.concat, "", {}):eq("bad argument #2 (expected document, got table)") + end) + + it("can be used as an operator", function() + local a, b = pp.text("a"), pp.text("b") + expect(pp.concat(a, b)):same(a .. b) + end) + end) + + describe("group", function() + it("is idempotent", function() + local x = pp.group(pp.text("a\nb")) + expect(pp.group(x)):eq(x) + end) + + it("does nothing for flat strings", function() + local x = pp.text("a") + expect(pp.group(x)):eq(x) + end) + end) + + -- Allows us to test + local function test_output(display) + it("displays the empty document", function() + expect(display(pp.empty)):same { "" } + end) + + it("displays a multiline string", function() + expect(display(pp.text("hello\nworld"))):same { + "hello", + "world", + } + end) + + it("displays a nested string", function() + expect(display(pp.nest(2, pp.concat("hello", pp.line, "world")))):same { + "hello", + " world", + } + end) + + it("displays a flattened group", function() + expect(display(pp.group(pp.concat("hello", pp.space_line, "world")))):same { + "hello world", + } + + expect(display(pp.group(pp.concat("hello", pp.line, "world")))):same { + "helloworld", + } + end) + + it("displays an expanded group", function() + expect(display(pp.group(pp.concat("hello darkness", pp.space_line, "my old friend")))):same { + "hello darkness", + "my old friend", + } + end) + + it("group removes nest", function() + expect(display(pp.group(pp.nest(2, pp.concat("hello", pp.space_line, "world"))))):same { + "hello world", + } + end) + end + + describe("write", function() + local function display(doc) + local w = with_window(20, 10, function() pp.write(doc) end) + local _, y = w.getCursorPos() + + local out = {} + for i = 1, y do out[i] = w.getLine(i):gsub("%s+$", "") end + return out + end + + test_output(display) + + it("wraps a long string", function() + expect(display(pp.text("hello world this is a long string which will wrap"))):same { + "hello world this is", + "a long string which", + "will wrap", + } + end) + end) + + describe("render", function() + local function display(doc) + local rendered = pp.render(doc, 20) + local n, lines = 1, {} + for line in (rendered .. "\n"):gmatch("([^\n]*)\n") do lines[n], n = line, n + 1 end + return lines + end + + test_output(display) + + it("does not wrap a long string", function() + expect(display(pp.text("hello world this is a long string which will wrap"))):same { + "hello world this is a long string which will wrap", + } + end) + end) + + describe("pretty", function() + --- We make use of "render" here, as it's considerably easier than checking against the actual structure. + -- However, it does also mean our tests are less unit-like. + local function pretty(x, width) return pp.render(pp.pretty(x), width) end + + describe("tables", function() + it("displays empty tables", function() + expect(pp.pretty({})):same(pp.text("{}")) + end) + + it("displays list-like tables", function() + expect(pretty({ 1, 2, 3 })):eq("{ 1, 2, 3 }") + end) + + it("displays mixed tables", function() + expect(pretty({ n = 3, 1, 2, 3 })):eq("{ n = 3, 1, 2, 3 }") + end) + + it("escapes keys", function() + expect(pretty({ ["and"] = 1, ["not that"] = 2 })):eq('{ ["and"] = 1, ["not that"] = 2 }') + end) + + it("sorts keys", function() + expect(pretty({ c = 1, b = 2, a = 3 })):eq('{ a = 3, b = 2, c = 1 }') + end) + + it("groups tables", function() + expect(pretty({ 1, 2, 3 }, 4)):eq("{\n 1,\n 2,\n 3\n}") + end) + end) + + it("shows numbers", function() + expect(pretty(123)):eq("123") + end) + + it("shows strings", function() + expect(pretty("hello\nworld")):eq('"hello\\nworld"') + end) + + it("shows functions", function() + expect(pretty(pretty)):eq(tostring(pretty)) + end) + end) +end) From 3f98b2785e893dc081f2a40212d1670e8149571c Mon Sep 17 00:00:00 2001 From: magiczocker10 <59317952+magiczocker10@users.noreply.github.com> Date: Thu, 23 Jan 2020 16:08:11 +0100 Subject: [PATCH 04/35] Fix turtle texture layout (#350) --- .../computercraft/models/block/modem.json | 2 +- .../models/block/turtle_base.json | 22 +++++----- .../block/turtle_upgrade_base_left.json | 4 +- .../block/turtle_upgrade_base_right.json | 4 +- .../models/block/turtle_white.json | 44 +++++++++---------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/main/resources/assets/computercraft/models/block/modem.json b/src/main/resources/assets/computercraft/models/block/modem.json index fd6ff916e..7ea4efaef 100644 --- a/src/main/resources/assets/computercraft/models/block/modem.json +++ b/src/main/resources/assets/computercraft/models/block/modem.json @@ -10,7 +10,7 @@ "faces": { "down": { "uv": [ 2, 13, 14, 16 ], "texture": "#front" }, "up": { "uv": [ 2, 0, 14, 3 ], "texture": "#front" }, - "north": { "uv": [ 2, 2, 14, 14 ], "texture": "#back" }, + "north": { "uv": [ 2, 2, 14, 14 ], "texture": "#back", "cullface": "north" }, "south": { "uv": [ 2, 2, 14, 14 ], "texture": "#front" }, "west": { "uv": [ 0, 2, 3, 14 ], "texture": "#front" }, "east": { "uv": [ 13, 2, 16, 14 ], "texture": "#front" } diff --git a/src/main/resources/assets/computercraft/models/block/turtle_base.json b/src/main/resources/assets/computercraft/models/block/turtle_base.json index a4788803d..aa3e71418 100644 --- a/src/main/resources/assets/computercraft/models/block/turtle_base.json +++ b/src/main/resources/assets/computercraft/models/block/turtle_base.json @@ -8,23 +8,23 @@ "from": [ 2, 2, 2 ], "to": [ 14, 14, 13 ], "faces": { - "down": { "uv": [ 2.75, 0, 5.75, 2.75 ], "texture": "#texture" }, - "up": { "uv": [ 5.75, 0, 8.75, 2.75 ], "texture": "#texture" }, - "north": { "uv": [ 8.5, 5.75, 11.5, 2.75 ], "texture": "#texture" }, - "south": { "uv": [ 2.75, 5.75, 5.75, 2.75 ], "texture": "#texture" }, - "west": { "uv": [ 0, 5.75, 2.75, 2.75 ], "texture": "#texture" }, - "east": { "uv": [ 5.75, 5.75, 8.5, 2.75 ], "texture": "#texture" } + "down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" }, + "up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" }, + "north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" }, + "south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" }, + "west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" }, + "east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" } } }, { "from": [ 3, 6, 13 ], "to": [ 13, 13, 15 ], "faces": { - "down": { "uv": [ 9.25, 0, 11.75, 0.5 ], "texture": "#texture" }, - "up": { "uv": [ 11.75, 0, 14.25, 0.5 ], "texture": "#texture" }, - "south": { "uv": [ 9.25, 2.25, 11.75, 0.5 ], "texture": "#texture" }, - "west": { "uv": [ 8.75, 2.25, 9.25, 0.5 ], "texture": "#texture" }, - "east": { "uv": [ 11.75, 2.25, 12.25, 0.5 ], "texture": "#texture" } + "down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" }, + "up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" }, + "south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" }, + "west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" }, + "east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" } } } ] diff --git a/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_left.json b/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_left.json index 1dcf2e72a..823c5e590 100644 --- a/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_left.json +++ b/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_left.json @@ -8,8 +8,8 @@ "from": [ 0.5, 4.5, 3.5 ], "to": [ 2, 12.5, 11.5 ], "faces": { - "down": { "uv": [ 13, 2, 16, 14 ], "texture": "#texture" }, - "up": { "uv": [ 13, 2, 16, 14 ], "texture": "#texture" }, + "down": { "uv": [ 2, 14, 14, 16 ], "texture": "#texture", "rotation": 270 }, + "up": { "uv": [ 2, 0, 14, 3 ], "texture": "#texture", "rotation": 90 }, "north": { "uv": [ 0, 2, 3, 14 ], "texture": "#texture" }, "south": { "uv": [ 13, 2, 16, 14 ], "texture": "#texture" }, "west": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture" } diff --git a/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_right.json b/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_right.json index ff52e9d67..7ab452e94 100644 --- a/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_right.json +++ b/src/main/resources/assets/computercraft/models/block/turtle_upgrade_base_right.json @@ -8,8 +8,8 @@ "from": [ 14, 4.5, 3.5 ], "to": [ 15.5, 12.5, 11.5 ], "faces": { - "down": { "uv": [ 0, 2, 3, 14 ], "texture": "#texture" }, - "up": { "uv": [ 0, 2, 3, 14 ], "texture": "#texture" }, + "down": { "uv": [ 2, 14, 14, 16 ], "texture": "#texture", "rotation": 90 }, + "up": { "uv": [ 2, 0, 14, 3 ], "texture": "#texture", "rotation": 270 }, "north": { "uv": [ 13, 2, 16, 14 ], "texture": "#texture" }, "south": { "uv": [ 0, 2, 3, 14 ], "texture": "#texture" }, "east": { "uv": [ 2, 2, 14, 14 ], "texture": "#texture" } diff --git a/src/main/resources/assets/computercraft/models/block/turtle_white.json b/src/main/resources/assets/computercraft/models/block/turtle_white.json index 7e0d2df03..fe9a5c233 100644 --- a/src/main/resources/assets/computercraft/models/block/turtle_white.json +++ b/src/main/resources/assets/computercraft/models/block/turtle_white.json @@ -8,46 +8,46 @@ "from": [ 2, 2, 2 ], "to": [ 14, 14, 13 ], "faces": { - "down": { "uv": [ 2.75, 5.75, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 }, - "up": { "uv": [ 5.75, 5.75, 8.75, 8.5 ], "texture": "#texture", "tintindex": 0 }, - "north": { "uv": [ 8.5, 11.5, 11.5, 8.5 ], "texture": "#texture", "tintindex": 0 }, - "south": { "uv": [ 2.75, 11.5, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 }, - "west": { "uv": [ 0, 11.5, 2.75, 8.5 ], "texture": "#texture", "tintindex": 0 }, - "east": { "uv": [ 5.75, 11.5, 8.5, 8.555 ], "texture": "#texture", "tintindex": 0 } + "down": { "uv": [ 5.75, 8.5, 2.75, 5.75 ], "texture": "#texture", "tintindex": 0 }, + "up": { "uv": [ 8.75, 5.75, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 }, + "north": { "uv": [ 11.5, 11.5, 8.5, 8.5 ], "texture": "#texture", "tintindex": 0 }, + "south": { "uv": [ 5.75, 11.5, 2.75, 8.5 ], "texture": "#texture", "tintindex": 0 }, + "west": { "uv": [ 8.5, 11.5, 5.75, 8.555 ], "texture": "#texture", "tintindex": 0 }, + "east": { "uv": [ 2.75, 11.5, 0, 8.5 ], "texture": "#texture", "tintindex": 0 } } }, { "from": [ 3, 6, 13 ], "to": [ 13, 13, 15 ], "faces": { - "down": { "uv": [ 9.25, 5.75, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 }, - "up": { "uv": [ 11.75, 5.75, 14.25, 6.25 ], "texture": "#texture", "tintindex": 0 }, - "south": { "uv": [ 9.25, 8, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 }, - "west": { "uv": [ 8.75, 8, 9.25, 6.25 ], "texture": "#texture", "tintindex": 0 }, - "east": { "uv": [ 11.75, 8, 12.25, 6.25 ], "texture": "#texture", "tintindex": 0 } + "down": { "uv": [ 11.75, 6.25, 9.25, 5.75 ], "texture": "#texture", "tintindex": 0 }, + "up": { "uv": [ 14.25, 5.75, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 }, + "south": { "uv": [ 11.75, 8, 9.25, 6.25 ], "texture": "#texture", "tintindex": 0 }, + "west": { "uv": [ 12.25, 8, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 }, + "east": { "uv": [ 9.25, 8, 8.75, 6.25 ], "texture": "#texture", "tintindex": 0 } } }, { "from": [ 2, 2, 2 ], "to": [ 14, 14, 13 ], "faces": { - "down": { "uv": [ 2.75, 0, 5.75, 2.75 ], "texture": "#texture" }, - "up": { "uv": [ 5.75, 0, 8.75, 2.75 ], "texture": "#texture" }, - "north": { "uv": [ 8.5, 5.75, 11.5, 2.75 ], "texture": "#texture" }, - "south": { "uv": [ 2.75, 5.75, 5.75, 2.75 ], "texture": "#texture" }, - "west": { "uv": [ 0, 5.75, 2.75, 2.75 ], "texture": "#texture" }, - "east": { "uv": [ 5.75, 5.75, 8.5, 2.75 ], "texture": "#texture" } + "down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" }, + "up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" }, + "north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" }, + "south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" }, + "west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" }, + "east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" } } }, { "from": [ 3, 6, 13 ], "to": [ 13, 13, 15 ], "faces": { - "down": { "uv": [ 9.25, 0, 11.75, 0.5 ], "texture": "#texture" }, - "up": { "uv": [ 11.75, 0, 14.25, 0.5 ], "texture": "#texture" }, - "south": { "uv": [ 9.25, 2.25, 11.75, 0.5 ], "texture": "#texture" }, - "west": { "uv": [ 8.75, 2.25, 9.25, 0.5 ], "texture": "#texture" }, - "east": { "uv": [ 11.75, 2.25, 12.25, 0.5 ], "texture": "#texture" } + "down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" }, + "up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" }, + "south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" }, + "west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" }, + "east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" } } } ] From 0de5969ec135dfa3b7d96bbdc201964f07142da0 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Thu, 23 Jan 2020 15:11:50 +0000 Subject: [PATCH 05/35] Lint whitespace during CI --- .github/workflows/main-ci.yml | 3 + .../computercraft/lua/rom/help/adventure.txt | 2 +- .../computercraft/lua/rom/help/alias.txt | 2 +- .../computercraft/lua/rom/help/chat.txt | 2 +- .../computercraft/lua/rom/help/clear.txt | 2 +- .../computercraft/lua/rom/help/coroutine.txt | 2 +- .../computercraft/lua/rom/help/dance.txt | 2 +- .../computercraft/lua/rom/help/disk.txt | 2 +- .../assets/computercraft/lua/rom/help/dj.txt | 2 +- .../computercraft/lua/rom/help/drive.txt | 2 +- .../computercraft/lua/rom/help/drives.txt | 2 +- .../computercraft/lua/rom/help/earth.txt | 2 +- .../computercraft/lua/rom/help/edit.txt | 2 +- .../computercraft/lua/rom/help/exit.txt | 2 +- .../assets/computercraft/lua/rom/help/gps.txt | 4 +- .../computercraft/lua/rom/help/gpsapi.txt | 2 +- .../computercraft/lua/rom/help/hello.txt | 2 +- .../computercraft/lua/rom/help/help.txt | 2 +- .../assets/computercraft/lua/rom/help/id.txt | 2 +- .../assets/computercraft/lua/rom/help/io.txt | 2 +- .../computercraft/lua/rom/help/keys.txt | 4 +- .../computercraft/lua/rom/help/list.txt | 2 +- .../assets/computercraft/lua/rom/help/lua.txt | 2 +- .../computercraft/lua/rom/help/math.txt | 2 +- .../computercraft/lua/rom/help/mkdir.txt | 2 +- .../computercraft/lua/rom/help/modems.txt | 2 +- .../computercraft/lua/rom/help/monitor.txt | 2 +- .../computercraft/lua/rom/help/multishell.txt | 2 +- .../assets/computercraft/lua/rom/help/os.txt | 2 +- .../computercraft/lua/rom/help/parallel.txt | 2 +- .../computercraft/lua/rom/help/pocket.txt | 2 +- .../computercraft/lua/rom/help/printers.txt | 2 +- .../lua/rom/help/programming.txt | 2 +- .../computercraft/lua/rom/help/reboot.txt | 2 +- .../computercraft/lua/rom/help/redstone.txt | 2 +- .../computercraft/lua/rom/help/refuel.txt | 2 +- .../computercraft/lua/rom/help/repeat.txt | 2 +- .../computercraft/lua/rom/help/shell.txt | 2 +- .../computercraft/lua/rom/help/shutdown.txt | 2 +- .../computercraft/lua/rom/help/string.txt | 2 +- .../computercraft/lua/rom/help/table.txt | 2 +- .../computercraft/lua/rom/help/time.txt | 2 +- .../computercraft/lua/rom/help/type.txt | 2 +- .../GopherAtl/battleship/battleship.lua | 248 +++++------ .../treasure/GravityScore/LuaIDE/luaide.lua | 46 +- .../lua/treasure/JTK/maze3d/maze2d.lua | 76 ++-- .../lua/treasure/JTK/maze3d/maze3d.lua | 64 +-- .../lua/treasure/Lyqyd/nsh/get.lua | 2 +- .../lua/treasure/Lyqyd/nsh/nsh.lua | 2 +- .../lua/treasure/Lyqyd/nsh/put.lua | 2 +- .../TheOriginalBIT/tictactoe/tictactoe.lua | 12 +- .../dan200/alongtimeago/alongtimeago.lua | 2 +- .../deprecated/GopherAtl/talk/talk.lua | 2 +- .../fredthead/protector/protector.lua | 220 +++++----- .../nitrogenfingers/goldrunner/goldrunner.lua | 156 +++---- .../nitrogenfingers/npaintpro/3dprint.lua | 16 +- .../nitrogenfingers/npaintpro/gameutils.lua | 92 ++-- .../nitrogenfingers/npaintpro/npaintpro.lua | 406 +++++++++--------- .../treasure/vilsol/gameoflife/gameoflife.lua | 2 +- src/test/resources/test-rom/mcfly.lua | 2 +- .../test-rom/spec/programs/delete_spec.lua | 2 +- .../test-rom/spec/programs/edit_spec.lua | 2 +- .../spec/programs/http/pastebin_spec.lua | 2 +- .../test-rom/spec/programs/id_spec.lua | 2 +- .../test-rom/spec/programs/motd_spec.lua | 2 +- .../test-rom/spec/programs/set_spec.lua | 6 +- .../test-rom/spec/programs/time_spec.lua | 2 +- tools/check-lines.py | 28 ++ 68 files changed, 759 insertions(+), 728 deletions(-) create mode 100644 tools/check-lines.py diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index d0caedbf2..d499cd33f 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -37,3 +37,6 @@ jobs: test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/bin/illuaminate chmod +x bin/illuaminate bin/illuaminate lint + + - name: Check whitespace + run: python3 tools/check-lines.py diff --git a/src/main/resources/assets/computercraft/lua/rom/help/adventure.txt b/src/main/resources/assets/computercraft/lua/rom/help/adventure.txt index bd1d982cf..6a9880eba 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/adventure.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/adventure.txt @@ -1 +1 @@ -adventure is a text adventure game for CraftOS. To navigate around the world of adventure, type simple instructions to the interpreter, for example: "go north", "punch tree", "craft planks", "mine coal with pickaxe", "hit creeper with sword" \ No newline at end of file +adventure is a text adventure game for CraftOS. To navigate around the world of adventure, type simple instructions to the interpreter, for example: "go north", "punch tree", "craft planks", "mine coal with pickaxe", "hit creeper with sword" diff --git a/src/main/resources/assets/computercraft/lua/rom/help/alias.txt b/src/main/resources/assets/computercraft/lua/rom/help/alias.txt index 4d11d45ce..8f742bd3f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/alias.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/alias.txt @@ -3,4 +3,4 @@ alias assigns shell commands to run other programs. ex: "alias dir ls" will make the "dir" command run the "ls" program "alias dir" will remove the alias set on "dir" -"alias" will list all current aliases. \ No newline at end of file +"alias" will list all current aliases. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/chat.txt b/src/main/resources/assets/computercraft/lua/rom/help/chat.txt index 1e99ca45c..035cb463d 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/chat.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/chat.txt @@ -2,4 +2,4 @@ Surf the rednet superhighway with "chat", the networked chat program for CraftOS ex: "chat host forgecraft" will create a chatroom with the name "forgecraft" -"chat join forgecraft direwolf20" will connect to the chatroom with the name "forgecraft", using the nickname "direwolf20" \ No newline at end of file +"chat join forgecraft direwolf20" will connect to the chatroom with the name "forgecraft", using the nickname "direwolf20" diff --git a/src/main/resources/assets/computercraft/lua/rom/help/clear.txt b/src/main/resources/assets/computercraft/lua/rom/help/clear.txt index ea517ddb3..6e3258411 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/clear.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/clear.txt @@ -1 +1 @@ -clear clears the screen. \ No newline at end of file +clear clears the screen. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/coroutine.txt b/src/main/resources/assets/computercraft/lua/rom/help/coroutine.txt index 12b366bd2..93d8f7b04 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/coroutine.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/coroutine.txt @@ -1,2 +1,2 @@ coroutine is a standard Lua5.1 API. -Refer to http://www.lua.org/manual/5.1/ for more information. \ No newline at end of file +Refer to http://www.lua.org/manual/5.1/ for more information. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/dance.txt b/src/main/resources/assets/computercraft/lua/rom/help/dance.txt index 3c776f8f0..cef419ab9 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/dance.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/dance.txt @@ -1 +1 @@ -dance is a program for Turtles. Turtles love to get funky. \ No newline at end of file +dance is a program for Turtles. Turtles love to get funky. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/disk.txt b/src/main/resources/assets/computercraft/lua/rom/help/disk.txt index f47bc625c..3c1348e6b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/disk.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/disk.txt @@ -14,4 +14,4 @@ disk.getID( drive ) Events fired by the disk API: "disk" when a disk or other item is inserted into a disk drive. Argument is the name of the drive "disk_eject" when a disk is removed from a disk drive. Argument is the name of the drive -Type "help events" to learn about the event system. \ No newline at end of file +Type "help events" to learn about the event system. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/dj.txt b/src/main/resources/assets/computercraft/lua/rom/help/dj.txt index 2c7a02bd1..c41dccc6e 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/dj.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/dj.txt @@ -3,4 +3,4 @@ dj plays Music Discs from disk drives attached to the computer. ex: "dj" or "dj play" plays a random disc. "dj play left" plays the disc in the drive on the left of the computer. -"dj stop" stops the current disc. \ No newline at end of file +"dj stop" stops the current disc. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/drive.txt b/src/main/resources/assets/computercraft/lua/rom/help/drive.txt index 397c98c30..eb6472a8e 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/drive.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/drive.txt @@ -2,4 +2,4 @@ drive tells you which disk drive the current or specified directory is located i ex: "drive" tell you the disk drive of the current directory. -"drive foo" tells you the disk drive of the subdirectory "foo" \ No newline at end of file +"drive foo" tells you the disk drive of the subdirectory "foo" diff --git a/src/main/resources/assets/computercraft/lua/rom/help/drives.txt b/src/main/resources/assets/computercraft/lua/rom/help/drives.txt index 6f15f40f3..b793f8e7d 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/drives.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/drives.txt @@ -16,4 +16,4 @@ getDiskID() Events fired by the Disk Drive: "disk" when a disk or other item is inserted into the drive. Argument is the name of the drive. "disk_eject" when a disk is removed from a drive. Argument is the name of the drive. -Type "help events" to learn about the event system. \ No newline at end of file +Type "help events" to learn about the event system. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/earth.txt b/src/main/resources/assets/computercraft/lua/rom/help/earth.txt index d1b452673..b9842d064 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/earth.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/earth.txt @@ -1 +1 @@ -Mostly harmless. \ No newline at end of file +Mostly harmless. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/edit.txt b/src/main/resources/assets/computercraft/lua/rom/help/edit.txt index 31d044496..6e89a7e08 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/edit.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/edit.txt @@ -1,4 +1,4 @@ edit is a text editor for creating or modifying programs or text files. After creating a program with edit, type its filename in the shell to run it. You can open any of the builtin programs with edit to learn how to program. ex: -"edit hello" opens a file called "hello" for editing. \ No newline at end of file +"edit hello" opens a file called "hello" for editing. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/exit.txt b/src/main/resources/assets/computercraft/lua/rom/help/exit.txt index 9b6c4347f..891745139 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/exit.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/exit.txt @@ -1 +1 @@ -exit will exit the current shell. \ No newline at end of file +exit will exit the current shell. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/gps.txt b/src/main/resources/assets/computercraft/lua/rom/help/gps.txt index e3e14b566..35b833580 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/gps.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/gps.txt @@ -4,7 +4,7 @@ Type "help gpsapi" for help using GPS functions in lua programs. ex: "gps locate" will connect to nearby GPS servers, and try to determine the position of the computer or turtle. "gps host" will try to determine the position, and host a GPS server if successful. -"gps host 10 20 30" will host a GPS server, using the manually entered position 10,20,30. +"gps host 10 20 30" will host a GPS server, using the manually entered position 10,20,30. Take care when manually entering host positions. If the positions entered into multiple GPS hosts -are not consistent, the results of locate calls will be incorrect. \ No newline at end of file +are not consistent, the results of locate calls will be incorrect. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/gpsapi.txt b/src/main/resources/assets/computercraft/lua/rom/help/gpsapi.txt index 7bb9fe601..83116f669 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/gpsapi.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/gpsapi.txt @@ -1,4 +1,4 @@ Functions in the GPS API: gps.locate( timeout ) -The locate function will send a signal to nearby gps servers, and wait for responses before the timeout. If it receives enough responses to determine this computers position then x, y and z co-ordinates will be returned, otherwise it will return nil. If GPS hosts do not have their positions configured correctly, results will be inaccurate. \ No newline at end of file +The locate function will send a signal to nearby gps servers, and wait for responses before the timeout. If it receives enough responses to determine this computers position then x, y and z co-ordinates will be returned, otherwise it will return nil. If GPS hosts do not have their positions configured correctly, results will be inaccurate. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/hello.txt b/src/main/resources/assets/computercraft/lua/rom/help/hello.txt index a58ae9908..0f2017aad 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/hello.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/hello.txt @@ -1 +1 @@ -hello prints the text "Hello World!" to the screen. \ No newline at end of file +hello prints the text "Hello World!" to the screen. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/help.txt b/src/main/resources/assets/computercraft/lua/rom/help/help.txt index fe85a2370..29c0b3a32 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/help.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/help.txt @@ -1,4 +1,4 @@ help is the help tool you're currently using. Type "help index" to see all help topics. Type "help" to see the help intro. -Type "help helpapi" for information on the help Lua API. \ No newline at end of file +Type "help helpapi" for information on the help Lua API. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/id.txt b/src/main/resources/assets/computercraft/lua/rom/help/id.txt index f87492bee..e23f1290a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/id.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/id.txt @@ -2,4 +2,4 @@ id prints the unique identifier of this computer, or a Disk in an attached Disk ex: "id" will print this Computers ID and label -"id left" will print the ID and label of the disk in the Disk Drive on the left \ No newline at end of file +"id left" will print the ID and label of the disk in the Disk Drive on the left diff --git a/src/main/resources/assets/computercraft/lua/rom/help/io.txt b/src/main/resources/assets/computercraft/lua/rom/help/io.txt index f71cca207..8da72cbd1 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/io.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/io.txt @@ -1,2 +1,2 @@ io is a standard Lua5.1 API, reimplemented for CraftOS. Not all the features are availiable. -Refer to http://www.lua.org/manual/5.1/ for more information. \ No newline at end of file +Refer to http://www.lua.org/manual/5.1/ for more information. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/keys.txt b/src/main/resources/assets/computercraft/lua/rom/help/keys.txt index 0adb2016c..a46f10731 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/keys.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/keys.txt @@ -3,7 +3,7 @@ The keys API contains constants for all the key codes that can be returned by th Example usage: local sEvent, nKey = os.pullEvent() if sEvent == "key" and nKey == keys.enter then - -- Do something + -- Do something end -See http://www.minecraftwiki.net/wiki/Key_codes, or the source code, for a complete reference. \ No newline at end of file +See http://www.minecraftwiki.net/wiki/Key_codes, or the source code, for a complete reference. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/list.txt b/src/main/resources/assets/computercraft/lua/rom/help/list.txt index 9ab8cd06c..16c0fdf52 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/list.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/list.txt @@ -1 +1 @@ -ls will list all the directories and files in the current location. Use "type" to find out if an item is a file or a directory. \ No newline at end of file +ls will list all the directories and files in the current location. Use "type" to find out if an item is a file or a directory. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/lua.txt b/src/main/resources/assets/computercraft/lua/rom/help/lua.txt index da1cb187a..4d164c8b0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/lua.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/lua.txt @@ -1 +1 @@ -lua is an interactive prompt for the lua programming language. It's a useful tool for learning the language. \ No newline at end of file +lua is an interactive prompt for the lua programming language. It's a useful tool for learning the language. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/math.txt b/src/main/resources/assets/computercraft/lua/rom/help/math.txt index ed3da1e49..35ed34827 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/math.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/math.txt @@ -1,2 +1,2 @@ math is a standard Lua5.1 API. -Refer to http://www.lua.org/manual/5.1/ for more information. \ No newline at end of file +Refer to http://www.lua.org/manual/5.1/ for more information. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/mkdir.txt b/src/main/resources/assets/computercraft/lua/rom/help/mkdir.txt index e7268dc63..e5e8fdab3 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/mkdir.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/mkdir.txt @@ -2,4 +2,4 @@ mkdir creates a directory in the current location. ex: "mkdir foo" creates a directory named "foo". -"mkdir ../foo" creates a directory named "foo" in the directory above the current directory. \ No newline at end of file +"mkdir ../foo" creates a directory named "foo" in the directory above the current directory. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/modems.txt b/src/main/resources/assets/computercraft/lua/rom/help/modems.txt index 960bc5058..281a87582 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/modems.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/modems.txt @@ -9,4 +9,4 @@ transmit( channel, replyChannel, message ) isWireless() Events fired by Modems: -"modem_message" when a message is received on an open channel. Arguments are name, channel, replyChannel, message, distance \ No newline at end of file +"modem_message" when a message is received on an open channel. Arguments are name, channel, replyChannel, message, distance diff --git a/src/main/resources/assets/computercraft/lua/rom/help/monitor.txt b/src/main/resources/assets/computercraft/lua/rom/help/monitor.txt index 6837b38d4..028a8bc80 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/monitor.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/monitor.txt @@ -3,4 +3,4 @@ Type "help monitors" for help using monitors as peripherals in lua programs. ex: "monitor left hello" will run the "hello" program on the monitor to the left of the computer. -"monitor top edit foo" will run the edit program on the top monitor, editing the file "foo". \ No newline at end of file +"monitor top edit foo" will run the edit program on the top monitor, editing the file "foo". diff --git a/src/main/resources/assets/computercraft/lua/rom/help/multishell.txt b/src/main/resources/assets/computercraft/lua/rom/help/multishell.txt index b16243112..8fbf8d75b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/multishell.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/multishell.txt @@ -1,2 +1,2 @@ multishell is the toplevel program on Advanced Computers which manages background tabs. -Type "help shellapi" for information about the shell lua api. \ No newline at end of file +Type "help shellapi" for information about the shell lua api. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/os.txt b/src/main/resources/assets/computercraft/lua/rom/help/os.txt index 442d4a89b..b57bef8da 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/os.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/os.txt @@ -23,4 +23,4 @@ os.reboot() Events emitted by the os API: "timer" when a timeout started by os.startTimer() completes. Argument is the token returned by os.startTimer(). "alarm" when a time passed to os.setAlarm() is reached. Argument is the token returned by os.setAlarm(). -Type "help events" to learn about the event system. \ No newline at end of file +Type "help events" to learn about the event system. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/parallel.txt b/src/main/resources/assets/computercraft/lua/rom/help/parallel.txt index 822dd47a7..4b958c442 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/parallel.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/parallel.txt @@ -1,4 +1,4 @@ Functions in the Parallel API: parallel.waitForAny( function1, function2, ... ) parallel.waitForAll( function1, function2, ... ) -These methods provide an easy way to run multiple lua functions simultaneously. \ No newline at end of file +These methods provide an easy way to run multiple lua functions simultaneously. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/pocket.txt b/src/main/resources/assets/computercraft/lua/rom/help/pocket.txt index fc498c669..72925bdea 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/pocket.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/pocket.txt @@ -3,4 +3,4 @@ Functions in the pocket API: pocket.equipBack() pocket.unequipBack() -When equipping upgrades, it will search your inventory for a suitable upgrade, starting in the selected slot. If one cannot be found then it will check your offhand. \ No newline at end of file +When equipping upgrades, it will search your inventory for a suitable upgrade, starting in the selected slot. If one cannot be found then it will check your offhand. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/printers.txt b/src/main/resources/assets/computercraft/lua/rom/help/printers.txt index a855ab972..73295344e 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/printers.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/printers.txt @@ -9,4 +9,4 @@ getPageSize() setCursorPos( x, y ) getCursorPos() write( text ) -endPage() \ No newline at end of file +endPage() diff --git a/src/main/resources/assets/computercraft/lua/rom/help/programming.txt b/src/main/resources/assets/computercraft/lua/rom/help/programming.txt index 68e8621fc..4a14fb2e3 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/programming.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/programming.txt @@ -8,4 +8,4 @@ To quickly shutdown a computer, hold Ctrl+S for 1 second. To quickly reboot a computer, hold Ctrl+R for 1 second. To learn about the programming APIs availiable, type "apis" or "help apis". -If you get stuck, visit the forums at http://www.computercraft.info/ for advice and tutorials. \ No newline at end of file +If you get stuck, visit the forums at http://www.computercraft.info/ for advice and tutorials. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/reboot.txt b/src/main/resources/assets/computercraft/lua/rom/help/reboot.txt index 6cae9184d..525c9c979 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/reboot.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/reboot.txt @@ -1,2 +1,2 @@ reboot will turn the computer off and on again. -You can also hold Ctrl+R at any time to quickly reboot. \ No newline at end of file +You can also hold Ctrl+R at any time to quickly reboot. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/redstone.txt b/src/main/resources/assets/computercraft/lua/rom/help/redstone.txt index ea76f7827..ce86bb6b4 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/redstone.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/redstone.txt @@ -6,4 +6,4 @@ ex: "redstone set right blue false" turns off the blue wire in the bundled cable on the right redstone output. "redstone pulse front 10 1" emits 10 one second redstone pulses on the front redstone output. -Type "help redstoneapi" or "help rs" for information on the redstone Lua API. \ No newline at end of file +Type "help redstoneapi" or "help rs" for information on the redstone Lua API. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/refuel.txt b/src/main/resources/assets/computercraft/lua/rom/help/refuel.txt index 9fab3fa18..86e806b2c 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/refuel.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/refuel.txt @@ -3,4 +3,4 @@ refuel is a program for Turtles. Refuel will consume items from the inventory as ex: "refuel" will refuel with at most one fuel item "refuel 10" will refuel with at most 10 fuel items -"refuel all" will refuel with as many fuel items as possible \ No newline at end of file +"refuel all" will refuel with as many fuel items as possible diff --git a/src/main/resources/assets/computercraft/lua/rom/help/repeat.txt b/src/main/resources/assets/computercraft/lua/rom/help/repeat.txt index f65697d4a..a08ebf8da 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/repeat.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/repeat.txt @@ -1 +1 @@ -repeat is a program for repeating rednet messages across long distances. To use, connect 2 or more modems to a computer and run the "repeat" program; from then on, any rednet message sent from any computer in wireless range or connected by networking cable to either of the modems will be repeated to those on the other side. \ No newline at end of file +repeat is a program for repeating rednet messages across long distances. To use, connect 2 or more modems to a computer and run the "repeat" program; from then on, any rednet message sent from any computer in wireless range or connected by networking cable to either of the modems will be repeated to those on the other side. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/shell.txt b/src/main/resources/assets/computercraft/lua/rom/help/shell.txt index 080b8390a..9be848561 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/shell.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/shell.txt @@ -1,2 +1,2 @@ shell is the toplevel program which interprets commands and runs program. -Type "help shellapi" for information about the shell lua api. \ No newline at end of file +Type "help shellapi" for information about the shell lua api. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/shutdown.txt b/src/main/resources/assets/computercraft/lua/rom/help/shutdown.txt index 4a63391db..8bc734fc9 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/shutdown.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/shutdown.txt @@ -1 +1 @@ -shutdown will turn off the computer. \ No newline at end of file +shutdown will turn off the computer. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/string.txt b/src/main/resources/assets/computercraft/lua/rom/help/string.txt index 5dfe5d501..ce30dc916 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/string.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/string.txt @@ -1,2 +1,2 @@ string is a standard Lua5.1 API. -Refer to http://www.lua.org/manual/5.1/ for more information. \ No newline at end of file +Refer to http://www.lua.org/manual/5.1/ for more information. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/table.txt b/src/main/resources/assets/computercraft/lua/rom/help/table.txt index 80760d0de..d6943d035 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/table.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/table.txt @@ -1,2 +1,2 @@ table is a standard Lua5.1 API. -Refer to http://www.lua.org/manual/5.1/ for more information. \ No newline at end of file +Refer to http://www.lua.org/manual/5.1/ for more information. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/time.txt b/src/main/resources/assets/computercraft/lua/rom/help/time.txt index ae5113599..4a67ab57d 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/time.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/time.txt @@ -1 +1 @@ -time prints the current time of day. \ No newline at end of file +time prints the current time of day. diff --git a/src/main/resources/assets/computercraft/lua/rom/help/type.txt b/src/main/resources/assets/computercraft/lua/rom/help/type.txt index 74a4e8e79..1c8cad512 100644 --- a/src/main/resources/assets/computercraft/lua/rom/help/type.txt +++ b/src/main/resources/assets/computercraft/lua/rom/help/type.txt @@ -1 +1 @@ -type determines the type of a file or directory. Prints "file", "directory" or "does not exist". \ No newline at end of file +type determines the type of a file or directory. Prints "file", "directory" or "does not exist". diff --git a/src/main/resources/assets/computercraft/lua/treasure/GopherAtl/battleship/battleship.lua b/src/main/resources/assets/computercraft/lua/treasure/GopherAtl/battleship/battleship.lua index 03431406c..8d48eaaf5 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/GopherAtl/battleship/battleship.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/GopherAtl/battleship/battleship.lua @@ -1,8 +1,8 @@ --[[ battleship, - + by GopherAtl, 2013 - + Do whatever you want, just don't judge me by what a mess this code is. --]] @@ -17,21 +17,21 @@ local myTurn local targetX,targetY local shipsLeft=5 local oppShipsLeft=5 - + local originalTerm = term.current() - + --bounding box of the target grid local targetGridBounds={ minX=16, maxX=25, minY=4, maxY=13 } - - + + local function doColor(text,background) term.setTextColor(text) term.setBackgroundColor(background) end - + local function doColor_mono(text,background) if text==colors.blue or text==colors.red or text==colors.black or text==colors.lime or background==colors.lightGray then term.setTextColor(colors.black) @@ -39,9 +39,9 @@ local function doColor_mono(text,background) else term.setTextColor(colors.white) term.setBackgroundColor(colors.black) - end + end end - + local function doScreenColor() if term.isColor() then doColor(colors.white,colors.lightGray) @@ -49,16 +49,16 @@ local function doScreenColor() doColor(colors.black,colors.white) end end - + local function toGridRef(x,y) return string.sub("ABCDEFGHIJ",x,x)..string.sub("1234567890",y,y) end - - + + if not term.isColor() then doColor=doColor_mono end - + local function quit() if openedSide then rednet.close(openedSide) @@ -68,7 +68,7 @@ local function quit() print() error() end - + local foundModem=false --find modem for k,v in pairs(redstone.getSides()) do @@ -81,17 +81,17 @@ for k,v in pairs(redstone.getSides()) do break end end - + if not foundModem then print("You must have a modem to play!") return end - + if action==nil or (action~="join" and action~="host") then print("Invalid parameters. Usage:\n> battleship host\nHosts a game, waits for another computer to join\n> battleship join\nLooks for another game to join") quit() end - + --get player name while true do doColor(colors.cyan,colors.black) @@ -108,13 +108,13 @@ while true do break end end - + if action=="join" then print("Attempting to join a game...\n(press q to cancel)") while true do local retryTimer=os.startTimer(1); rednet.broadcast("bs join "..myName); - + while true do local event,p1,p2,p3=os.pullEvent(); if event=="rednet_message" then @@ -131,7 +131,7 @@ if action=="join" then end end local joined=false - + if opponentID then print("Joining game!") rednet.send(opponentID,"bs start") @@ -142,7 +142,7 @@ elseif action=="host" then print("Waiting for challenger...\n(Press q to cancel)") while true do while true do - local event,p1,p2=os.pullEvent() + local event,p1,p2=os.pullEvent() if event=="rednet_message" then opponent=string.match(p2,"bs join %s*(.+)%s*") if opponent then print("found player, inviting..") @@ -154,7 +154,7 @@ elseif action=="host" then quit() end end - + if opponentID then rednet.send(opponentID,"bs accept "..myName) local timeout=os.startTimer(1) @@ -166,17 +166,17 @@ elseif action=="host" then elseif event=="timer" and p1==timeout then print("player joined another game. Waiting for another...") opponentID=nil - break + break end end - + if opponentID then break end end end end - + local ships={ {pos=nil,dir="h",size=5,name="carrier",hits=0}, {pos=nil,dir="h",size=4,name="battleship",hits=0}, @@ -184,12 +184,12 @@ local ships={ {pos=nil,dir="h",size=3,name="submarine",hits=0}, {pos=nil,dir="h",size=2,name="destroyer",hits=0}, } - + local myShotTable={ {1,1,true},{5,5,false} } local oppShotTable={ } - + local myGrid,oppGrid={title=myName},{title=opponent} - + --setup grids for i=1,10 do myGrid[i]={} @@ -199,8 +199,8 @@ for i=1,10 do oppGrid[i][j]={hit=false,ship=false} end end - -local function drawShipsToGrid(ships,grid) + +local function drawShipsToGrid(ships,grid) for i=1,#ships do local x,y=table.unpack(ships[i].pos) local stepX=ships[i].dir=="h" and 1 or 0 @@ -211,18 +211,18 @@ local function drawShipsToGrid(ships,grid) end end end - + local function drawShotToGrid(shot,grid) grid[shot[1]][shot[2]].shot=true - grid[shot[1]][shot[2]].hit=shot[3] + grid[shot[1]][shot[2]].hit=shot[3] end - + local function makeShot(x,y,grid) local tile=grid[x][y] if tile.shot==true then return nil --already shot here! - end - + end + local shot={x,y,tile.ship} drawShotToGrid(shot,grid) if tile.ship then @@ -232,12 +232,12 @@ local function makeShot(x,y,grid) end end return shot -end - - +end + + local function drawTile(scrX,scrY,tile) term.setCursorPos(scrX,scrY) - + if tile.ship then if tile.shot then doColor(colors.red,colors.gray) @@ -259,31 +259,31 @@ local function drawTile(scrX,scrY,tile) end end end - + local function drawGrid(scrX,scrY,grid) doColor(colors.white,colors.black) term.setCursorPos(scrX,scrY+1) term.write(" ") - doColor(colors.white,colors.gray) + doColor(colors.white,colors.gray) term.setCursorPos(scrX,scrY) local pad=11-#grid.title term.write(string.rep(" ",math.ceil(pad/2))..grid.title..string.rep(" ",math.floor(pad/2))) - + for gx=1,10 do term.setTextColor(colors.white) term.setBackgroundColor(colors.black) term.setCursorPos(scrX+gx,scrY+1) term.write(gx==10 and "0" or string.char(string.byte("0")+gx)) - + term.setCursorPos(scrX,scrY+gx+1) term.write(string.char(string.byte("A")+gx-1)) for gy=1,10 do - drawTile(scrX+gx,scrY+gy+1,grid[gx][gy]) + drawTile(scrX+gx,scrY+gy+1,grid[gx][gy]) end end doColor(colors.white,colors.black) end - + function moveTargetIndicator(newX,newY) --if x has changed... if targetX and targetY then @@ -303,8 +303,8 @@ function moveTargetIndicator(newX,newY) term.write("^") term.setCursorPos(targetGridBounds.minX+newX-1,targetGridBounds.minY-3) term.write("v") - - targetX=newX + + targetX=newX end if newY~=targetY then --space over old @@ -319,11 +319,11 @@ function moveTargetIndicator(newX,newY) term.write("<") term.setCursorPos(targetGridBounds.minX-2,targetGridBounds.minY+newY-1) term.write(">") - + targetY=newY end term.setCursorPos(15,15) - term.write("Target : "..toGridRef(targetX,targetY)) + term.write("Target : "..toGridRef(targetX,targetY)) --if the target tile is a valid target, draw a "+" if not oppGrid[targetX][targetY].shot then term.setCursorPos(targetX+targetGridBounds.minX-1,targetY+targetGridBounds.minY-1) @@ -331,18 +331,18 @@ function moveTargetIndicator(newX,newY) term.write("+") end end - + local log={} - + local termWidth,termHeight=term.getSize() - + local logHeight=termHeight-3 local logWidth=termWidth-28 - + for i=1,logHeight do log[i]="" end - + local function printLog() doColor(colors.white,colors.black) for i=1,logHeight do @@ -358,9 +358,9 @@ local function printLog() end end end - - - + + + --shipX/Y are the position of ship on grid; gridX/Y are the offset of the top-left of grid local function drawShip(size,align,x,y,char) local stepX=align=="h" and 1 or 0 @@ -371,31 +371,31 @@ local function drawShip(size,align,x,y,char) x,y=x+stepX,y+stepY end end - + local function setStatusLine(lineNum,text) doScreenColor() local pad=math.floor((termWidth-#text)/2) term.setCursorPos(1,16+lineNum) term.write((" "):rep(pad)..text..(" "):rep(termWidth-#text-pad)) end - - + + doScreenColor() term.clear() - + drawGrid(2,2,myGrid) - + setStatusLine(1,"Started game with "..opponent.." at computer #"..(opponentID or "nil")) - + local function getShipBounds(ship) return { minX=ship.pos[1], minY=ship.pos[2], maxX=ship.pos[1]+(ship.dir=="h" and ship.size-1 or 0), maxY=ship.pos[2]+(ship.dir=="v" and ship.size-1 or 0) - } + } end - + local function getPointBounds(x,y) return { minX=x, @@ -404,20 +404,20 @@ local function getPointBounds(x,y) maxY=y, } end - + local function boundsIntersect(boundsA,boundsB) return not ( boundsA.minX>boundsB.maxX or boundsA.maxXboundsB.maxY or boundsA.maxY="0" and p1<="9" then local t=string.byte(p1)-string.byte("0") if t==0 then t=10 end - moveTargetIndicator(t,targetY) + moveTargetIndicator(t,targetY) end elseif e=="key" then if p1==keys.enter or p1==keys.space and targetX and targetY then @@ -736,7 +736,7 @@ local function runGame() moveTargetIndicator(math.max(targetX-1,1),targetY) elseif p1==keys.right then moveTargetIndicator(math.min(targetX+1,10),targetY) - end + end end end --shot sent, wait for my turn to resolve (top coroutine will switch turns and draw the hit to the grid) @@ -746,12 +746,12 @@ local function runGame() end end end - + local gameRoutine=coroutine.create(runGame) --if advanced terminal, default focus to chat, can play with mouse local inChat=term.isColor() local savedCursorPos={7,19} - + --redirect just to block scroll local redir={} for k,v in pairs(originalTerm) do @@ -762,12 +762,12 @@ for k,v in pairs(originalTerm) do end end originalTerm = term.redirect(redir) - + --run the game routine once coroutine.resume(gameRoutine) --hide cursor term.setCursorBlink(false) - + while true do local e,p1,p2,p3,p4,p5=os.pullEventRaw() if e=="terminate" then @@ -791,7 +791,7 @@ while true do opponentReady=true os.queueEvent("kickcoroutine") elseif cmd=="cointoss" then - myTurn=args=="true" + myTurn=args=="true" if myTurn then setStatusLine(2,"Your turn, take your shot!") else @@ -807,11 +807,11 @@ while true do local tile=myGrid[tx][ty] local shot=makeShot(tx,ty,myGrid) rednet.send(opponentID,"bs result "..(shot[3] and "hit" or "miss")) - drawTile(2+tx,3+ty,tile) + drawTile(2+tx,3+ty,tile) myTurn=true os.queueEvent("kickcoroutine") displayGameHelp() - setStatusLine(1,opponent.." fired at "..toGridRef(tx,ty).." and "..(shot[3] and "hit" or "missed")) + setStatusLine(1,opponent.." fired at "..toGridRef(tx,ty).." and "..(shot[3] and "hit" or "missed")) setStatusLine(2,"Your turn, take your shot!") end elseif cmd=="sink" then @@ -838,7 +838,7 @@ while true do setStatusLine(2,"Waiting for opponent...") os.queueEvent("kickcoroutine") end - + elseif cmd=="win" then --we won! setStatusLine(3,"You won the game! Congratulations!") @@ -852,24 +852,24 @@ while true do print("game coroutine crashed with the following error: "..err) quit() end - + if coroutine.status(gameRoutine)=="dead" then --game over break end end - + end - + term.setCursorPos(1,19) term.clearLine() term.write(" Press any key to continue...") os.pullEvent("key") --if a char event was queued following the key event, this will eat it os.sleep(0) - + term.setTextColor(colors.white) term.setBackgroundColor(colors.black) term.clear() quit() --- \ No newline at end of file +-- diff --git a/src/main/resources/assets/computercraft/lua/treasure/GravityScore/LuaIDE/luaide.lua b/src/main/resources/assets/computercraft/lua/treasure/GravityScore/LuaIDE/luaide.lua index b8de7b147..7ec78c299 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/GravityScore/LuaIDE/luaide.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/GravityScore/LuaIDE/luaide.lua @@ -1,8 +1,8 @@ --- +-- -- Lua IDE -- Made by GravityScore --- +-- -- -------- Variables @@ -44,7 +44,7 @@ local function isAdvanced() return term.isColor and term.isColor() end local function modRead(properties) local w, h = term.getSize() - local defaults = {replaceChar = nil, history = nil, visibleLength = nil, textLength = nil, + local defaults = {replaceChar = nil, history = nil, visibleLength = nil, textLength = nil, liveUpdates = nil, exitOnKey = nil} if not properties then properties = {} end for k, v in pairs(defaults) do if not properties[k] then properties[k] = v end end @@ -58,7 +58,7 @@ local function modRead(properties) local function redraw(repl) local scroll = 0 - if properties.visibleLength and sx + pos > properties.visibleLength + 1 then + if properties.visibleLength and sx + pos > properties.visibleLength + 1 then scroll = (sx + pos) - (properties.visibleLength + 1) end @@ -133,9 +133,9 @@ local function modRead(properties) elseif (but == keys.up or but == keys.down) and properties.history then redraw(" ") if but == keys.up then - if historyPos == nil and #properties.history > 0 then + if historyPos == nil and #properties.history > 0 then historyPos = #properties.history - elseif historyPos > 1 then + elseif historyPos > 1 then historyPos = historyPos - 1 end elseif but == keys.down then @@ -173,9 +173,9 @@ local function modRead(properties) elseif but == keys["end"] then pos = line:len() redraw() - elseif properties.exitOnKey then - if but == properties.exitOnKey or (properties.exitOnKey == "control" and - (but == 29 or but == 157)) then + elseif properties.exitOnKey then + if but == properties.exitOnKey or (properties.exitOnKey == "control" and + (but == 29 or but == 157)) then term.setCursorBlink(false) return nil end @@ -296,7 +296,7 @@ end local function centerRead(wid, begt) local function liveUpdate(line, e, but, x, y, p4, p5) - if isAdvanced() and e == "mouse_click" and x >= w/2 - wid/2 and x <= w/2 - wid/2 + 10 + if isAdvanced() and e == "mouse_click" and x >= w/2 - wid/2 and x <= w/2 - wid/2 + 10 and y >= 13 and y <= 15 then return true, "" end @@ -707,7 +707,7 @@ languages.brainfuck.mapLoops = function(code) loopLocations[loc] = true elseif let == "]" then local found = false - for i = loc, 1, -1 do + for i = loc, 1, -1 do if loopLocations[i] == true then loopLocations[i] = loc found = true @@ -877,7 +877,7 @@ local function run(path, lines, useArgs) local s = centerRead(w - 13, fs.getName(path) .. " ") for m in string.gmatch(s, "[^ \t]+") do ar[#ar + 1] = m:gsub("^%s*(.-)%s*$", "%1") end end - + saveFile(path, lines) term.setCursorBlink(false) term.setBackgroundColor(colors.black) @@ -949,7 +949,7 @@ local function run(path, lines, useArgs) term.setCursorPos(5, 14) term.write(b) end - + local opt = prompt({{"Error Help", w/2 - 15, 17}, {"Go To Line", w/2 + 2, 17}}, "horizontal") if opt == "Error Help" then @@ -1459,7 +1459,7 @@ end local function writeHighlighted(line) if curLanguage == languages.lua then - while line:len() > 0 do + while line:len() > 0 do line = attemptToHighlight(line, "^%-%-%[%[.-%]%]", colors[theme.comment]) or attemptToHighlight(line, "^%-%-.*", colors[theme.comment]) or attemptToHighlight(line, "^\".*[^\\]\"", colors[theme.string]) or @@ -1491,7 +1491,7 @@ local function draw() for i = 1, edh do local a = lines[scrolly + i] if a then - local ln = string.rep(" ", offx - 1 - tostring(scrolly + i):len()) .. tostring(scrolly + i) + local ln = string.rep(" ", offx - 1 - tostring(scrolly + i):len()) .. tostring(scrolly + i) local l = a:sub(scrollx + 1, edw + scrollx + 1) ln = ln .. ":" @@ -1541,7 +1541,7 @@ local function drawLine(...) for _, ly in pairs(ls) do local a = lines[ly] if a then - local ln = string.rep(" ", offx - 1 - tostring(ly):len()) .. tostring(ly) + local ln = string.rep(" ", offx - 1 - tostring(ly):len()) .. tostring(ly) local l = a:sub(scrollx + 1, edw + scrollx + 1) ln = ln .. ":" @@ -1651,7 +1651,7 @@ local function edit(path) draw() term.setCursorPos(x + offx, y + offy) term.setCursorBlink(true) - + -- Main loop local tid = os.startTimer(3) while true do @@ -1692,8 +1692,8 @@ local function edit(path) if f then table.insert(lines, y + 1, string.rep(" ", spaces + 2)) if not f:find("else", 1, true) and not f:find("elseif", 1, true) then - table.insert(lines, y + 2, string.rep(" ", spaces) .. - (f:find("repeat", 1, true) and "until " or f:find("{", 1, true) and "}" or + table.insert(lines, y + 2, string.rep(" ", spaces) .. + (f:find("repeat", 1, true) and "until " or f:find("{", 1, true) and "}" or "end")) end x, y = spaces + 3, y + 1 @@ -1771,7 +1771,7 @@ local function edit(path) x = math.min(lines[y]:len() + 1, x) cursorLoc(x, y, true) end - elseif e == "char" and allowEditorEvent and (displayCode and true or + elseif e == "char" and allowEditorEvent and (displayCode and true or y + scrolly - 1 == liveErr.line) then local shouldIgnore = false for k, v in pairs(liveCompletions) do @@ -2083,7 +2083,7 @@ local function changeTheme() sleep(1.6) return "menu" end - + term.write("LuaIDE - Could Not Load Theme!") fs.delete("/.LuaIDE_temp_theme_file") sleep(1.6) @@ -2100,7 +2100,7 @@ local function settings() title("LuaIDE - Settings") local opt = prompt({{"Change Theme", w/2 - 17, 8}, {"Return to Menu", w/2 - 19, 13}, - --[[{"Check for Updates", w/2 + 2, 8},]] {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err], + --[[{"Check for Updates", w/2 + 2, 8},]] {"Exit IDE", w/2 + 2, 13, bg = colors[theme.err], highlight = colors[theme.errHighlight]}}, "vertical", true) if opt == "Change Theme" then return changeTheme() -- elseif opt == "Check for Updates" then return update() @@ -2189,7 +2189,7 @@ if err and not err:find("Terminated") then term.write("Please report this error to") term.setCursorPos(6, cy + 3) term.write("GravityScore! ") - + term.setBackgroundColor(colors[theme.background]) if isAdvanced() then centerPrint("Click to Exit...", h - 1) else centerPrint("Press Any Key to Exit...", h - 1) end diff --git a/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze2d.lua b/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze2d.lua index ccc85fb29..be942df95 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze2d.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze2d.lua @@ -1,6 +1,6 @@ --[[ Project info: - + Name: Maze Creator: Jesusthekiller Language: Lua (CC) @@ -24,7 +24,7 @@ --[[ LICENSE: - + Maze Copyright (c) 2013 Jesusthekiller @@ -73,7 +73,7 @@ repeat cwrite("Enter maze size (5-99):") size = read() - + size = tonumber(size) if not size then size = 0 @@ -82,18 +82,18 @@ until size > 4 and size < 100 -- The generate local function mazeGen(mx, my) - + --[[ Format: - + maze.x.y.(1/2/3/4) = true/false - + 1 - top 2 - bottom 3 - right 4 - left ]]-- - + local maze = {} for i = 1, mx do maze[i] = {} @@ -116,26 +116,26 @@ local function mazeGen(mx, my) local intact = {} local x = curr.x local y = curr.y - + if x - 1 >= 1 and maze[x-1][y][1] and maze[x-1][y][2] and maze[x-1][y][3] and maze[x-1][y][4] then -- Check for full cells intact[#intact+1] = {x-1, y, 1} end - + if x + 1 <= mx and maze[x+1][y][1] and maze[x+1][y][2] and maze[x+1][y][3] and maze[x+1][y][4] then intact[#intact+1] = {x+1, y, 2} end - + if y + 1 <= my and maze[x][y+1][1] and maze[x][y+1][2] and maze[x][y+1][3] and maze[x][y+1][4] then intact[#intact+1] = {x, y+1, 3} end - + if y - 1 >= 1 and maze[x][y-1][1] and maze[x][y-1][2] and maze[x][y-1][3] and maze[x][y-1][4] then intact[#intact+1] = {x, y-1, 4} end - + if #intact > 0 then local i = math.random(1, #intact) -- Choose random - + if intact[i][3] == 1 then -- Set intact's attached wall to false maze[intact[i][1]][intact[i][2]][2] = false elseif intact[i][3] == 2 then @@ -145,11 +145,11 @@ local function mazeGen(mx, my) elseif intact[i][3] == 4 then maze[intact[i][1]][intact[i][2]][3] = false end - + maze[x][y][intact[i][3]] = false -- Set attached wall to false - + vis = vis + 1 -- Increase vis - + stack[#stack+1] = intact[i] -- Add to stack else local tmp = table.remove(stack) -- Get last cell @@ -157,7 +157,7 @@ local function mazeGen(mx, my) curr.y = tmp[2] end end - + return maze end @@ -177,7 +177,7 @@ local tab = {} for x = 1, size * 2 + 1 do tab[x] = {} - + for y = 1, size * 2 + 1 do if x % 2 == 0 and y % 2 == 0 then -- Fill cells (empty) tab[x][y] = false @@ -201,7 +201,7 @@ repeat -- Print map term.setBackgroundColor(colors.white) term.clear() - + if posx == 2 and posy == 2 then term.setCursorPos(1, 1) term.setTextColor(colors.black) @@ -211,27 +211,27 @@ repeat print("Goal: Step on # (It's on bottom right corner)") print("\nGood Luck!") end - + --[[ term.setTextColor(colors.black) term.setCursorPos(1, 19) write("X: "..posx.." Y: "..posy) ]] - + for x, tV in ipairs(tab) do -- Print the map for y, v in ipairs(tV) do if offsety+y > 20 then break end - + term.setCursorPos(offsetx+x, offsety+y) - + if v then term.setBackgroundColor(colors.black) else term.setBackgroundColor(colors.white) end - + if offsety+y < 20 and offsety+y > 0 and offsetx+x < 52 and offsetx+x > 0 then if x == size*2 and y == size*2 then if term.isColor() then @@ -243,51 +243,51 @@ repeat end end end - + if offsetx+x > 51 then break end - end - + end + term.setCursorPos(51/2, 19/2) term.setBackgroundColor(colors.white) - + if term.isColor() then term.setTextColor(colors.red) else term.setTextColor(colors.black) end - + write("X") - + -- Wait for key - + local e, k = os.pullEvent("char") - + if k == "a" and (not tab[posx-1][posy]) then posx = posx - 1 offsetx = offsetx + 1 end - + if k == "d" and (not tab[posx+1][posy]) then posx = posx + 1 offsetx = offsetx - 1 end - + if k == "w" and (not tab[posx][posy-1]) then posy = posy - 1 offsety = offsety + 1 end - + if k == "s" and (not tab[posx][posy+1]) then posy = posy + 1 offsety = offsety - 1 end - + if k == "q" then break end - + if k == "r" then posx = 2 posy = 2 @@ -324,4 +324,4 @@ term.setBackgroundColor(colors.black) term.setTextColor(colors.white) term.clear() term.setCursorPos(1, 1) -cprint(" Maze by JTK. Thanks for playing!") \ No newline at end of file +cprint(" Maze by JTK. Thanks for playing!") diff --git a/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze3d.lua b/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze3d.lua index 85343aeab..a8a04ee85 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze3d.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/JTK/maze3d/maze3d.lua @@ -1,6 +1,6 @@ --[[ Project info: - + Name: Maze 3D Creator: Jesusthekiller Language: Lua (CC) @@ -28,7 +28,7 @@ --[[ LICENSE: - + Maze 3D Copyright (c) 2013 Jesusthekiller @@ -78,7 +78,7 @@ repeat cwrite("Enter maze size (5-99):") size = read() - + size = tonumber(size) if not size then size = 0 @@ -87,18 +87,18 @@ until size > 4 and size < 100 -- The generate local function mazeGen(mx, my) - + --[[ Format: - + maze.x.y.(1/2/3/4) = true/false - + 1 - top 2 - bottom 3 - right 4 - left ]]-- - + local maze = {} for i = 1, mx do maze[i] = {} @@ -121,26 +121,26 @@ local function mazeGen(mx, my) local intact = {} local x = curr.x local y = curr.y - + if x - 1 >= 1 and maze[x-1][y][1] and maze[x-1][y][2] and maze[x-1][y][3] and maze[x-1][y][4] then -- Check for full cells intact[#intact+1] = {x-1, y, 1} end - + if x + 1 <= mx and maze[x+1][y][1] and maze[x+1][y][2] and maze[x+1][y][3] and maze[x+1][y][4] then intact[#intact+1] = {x+1, y, 2} end - + if y + 1 <= my and maze[x][y+1][1] and maze[x][y+1][2] and maze[x][y+1][3] and maze[x][y+1][4] then intact[#intact+1] = {x, y+1, 3} end - + if y - 1 >= 1 and maze[x][y-1][1] and maze[x][y-1][2] and maze[x][y-1][3] and maze[x][y-1][4] then intact[#intact+1] = {x, y-1, 4} end - + if #intact > 0 then local i = math.random(1, #intact) -- Choose random - + if intact[i][3] == 1 then -- Set intact's attached wall to false maze[intact[i][1]][intact[i][2]][2] = false elseif intact[i][3] == 2 then @@ -150,11 +150,11 @@ local function mazeGen(mx, my) elseif intact[i][3] == 4 then maze[intact[i][1]][intact[i][2]][3] = false end - + maze[x][y][intact[i][3]] = false -- Set attached wall to false - + vis = vis + 1 -- Increase vis - + stack[#stack+1] = intact[i] -- Add to stack else local tmp = table.remove(stack) -- Get last cell @@ -162,7 +162,7 @@ local function mazeGen(mx, my) curr.y = tmp[2] end end - + return maze end @@ -180,7 +180,7 @@ local tab = {} for x = 1, size * 2 + 1 do tab[x] = {} - + for y = 1, size * 2 + 1 do if x % 2 == 0 and y % 2 == 0 then -- Fill cells (empty) tab[x][y] = " " @@ -263,13 +263,13 @@ if redirect then print("redirect API found, using buffer") else local pe=printError - rawset(_G,"printError",error) + rawset(_G,"printError",error) local ok, err=pcall(os.loadAPI,"redirect") if not ok then print("trying "..shell.dir().."/redirect") ok,err=pcall(os.loadAPI,shell.dir().."/redirect") end - if ok then + if ok then print("Loaded redirect API, using buffer") buffer=redirect.createRedirectBuffer() loadedAPI=true @@ -277,7 +277,7 @@ else print("redirect API not found or could not be loaded, drawing directly; this may cause flickering.") end rawset(_G,"printError",pe) -end +end local colorSchemes = { {0,8}, --white+gray @@ -338,10 +338,10 @@ local function cast(cx,cy,angle) end local wall=map[y]:sub(x,x) if wall~=" " then - + return colorSchemes[tonumber(wall)][isX and 1 or 2], hitD end - end + end end local w,h=term.getSize() @@ -370,7 +370,7 @@ for x=1,w do t.angle=math.atan2(x-centerX,screenDist) t.dist=((x-centerX)^2+screenDist^2)^.5/screenDist end - + local function redraw() local oldTerm if buffer.isBuffer then @@ -411,15 +411,15 @@ local function clampCollision(x,y,radius) --I am. Complete fail, do nothing. return x,y end - + --ok, check the neighbors. local right=math.floor(x+radius)>gx local left=math.floor(x-radius)gy - + local pushed=false - + if right and map[gy]:sub(gx+1,gx+1)~=" " then --push left pushed=true @@ -429,7 +429,7 @@ local function clampCollision(x,y,radius) pushed=true x=gx+radius end - + if front and map[gy-1]:sub(gx,gx)~=" " then --push back pushed=true @@ -442,7 +442,7 @@ local function clampCollision(x,y,radius) y=gy+1-radius end - + --if I wasn't pushed out on any side, I might be hitting a corner if not pushed then --square rad @@ -482,7 +482,7 @@ local function clampCollision(x,y,radius) x=x+pushx y=y+pushy end - + return x,y end @@ -533,7 +533,7 @@ while true do redraw() dirty=false end - + local e={os.pullEvent()} if e[1]=="key" then if e[2]==keys.left then @@ -565,7 +565,7 @@ while true do dir=startdir dirty=true end - + if px >= mapW-1 and py >= mapH-1 then win = true break diff --git a/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/get.lua b/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/get.lua index 6c95fbd6d..a8d7901d3 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/get.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/get.lua @@ -23,4 +23,4 @@ if fs.exists(args[1]) then else print("Client rejected file!") end -end \ No newline at end of file +end diff --git a/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/nsh.lua b/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/nsh.lua index a76aba8d6..df62694fc 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/nsh.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/nsh.lua @@ -718,4 +718,4 @@ elseif #args >= 1 then --either no server running or we are the local shell on t else print("Usage: nsh [resume]") print(" nsh host [remote [local [name]]]") -end \ No newline at end of file +end diff --git a/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/put.lua b/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/put.lua index 23e6a2e3c..a31fe0be9 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/put.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/Lyqyd/nsh/put.lua @@ -32,4 +32,4 @@ if message ~= "fileNotFound" then else print("Empty file not written!") end -end \ No newline at end of file +end diff --git a/src/main/resources/assets/computercraft/lua/treasure/TheOriginalBIT/tictactoe/tictactoe.lua b/src/main/resources/assets/computercraft/lua/treasure/TheOriginalBIT/tictactoe/tictactoe.lua index 8fdfbbf85..88e394b1e 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/TheOriginalBIT/tictactoe/tictactoe.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/TheOriginalBIT/tictactoe/tictactoe.lua @@ -124,7 +124,7 @@ end local function getAIMove() -- make it seem like the computer actually has to think about its move sleep(0.8) - + -- check if AI can win and return the 3rd tile to create a win, if it cannot, check for a human attempt at winning and stop it, if there is none, return a random return (search(currentPlayer) or search(opposites[currentPlayer])) or math.random(1,9) end @@ -177,7 +177,7 @@ local function modread( _mask, _history, _limit ) redraw(' ') if event[2] == keys.up then if not historyPos then - historyPos = #_history + historyPos = #_history elseif historyPos > 1 then historyPos = historyPos - 1 end @@ -308,14 +308,14 @@ local function render() writeAt('+'..string.rep('-', sw-2)..'+', 1, 3) writeAt('+'..string.rep('-', sw-2)..'+', 1, sh-2) writeAt('+'..string.rep('-', sw-2)..'+', 1, sh) - + if term.isColor and term.isColor() then term.setCursorPos(sw, 1) term.setBackgroundColor(colors.red) term.setTextColor(colors.black) writeWithFormat('X') end - + -- set our colours term.setBackgroundColor(colors.white) term.setTextColor(colors.black) @@ -359,7 +359,7 @@ local function render() writeAt((board[i + 6] == 'x' and '/\\' or '\\/'), 18+((i-1)*7), 14) end end - + -- draw the current player term.setCursorPos(3, sh - 3) if not winner then @@ -441,4 +441,4 @@ cwriteWithFormat('&4Thank you for playing CCTicTacToe v1.0', 1) cwriteWithFormat('&4By &8TheOriginal&3BIT\n', 2) -- restore the default terminal object -term.redirect( oldTermObj ) \ No newline at end of file +term.redirect( oldTermObj ) diff --git a/src/main/resources/assets/computercraft/lua/treasure/dan200/alongtimeago/alongtimeago.lua b/src/main/resources/assets/computercraft/lua/treasure/dan200/alongtimeago/alongtimeago.lua index bf126850d..dc1672f70 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/dan200/alongtimeago/alongtimeago.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/dan200/alongtimeago/alongtimeago.lua @@ -22,7 +22,7 @@ while not bFinished do break end - -- Get this every frame incase the monitor resizes + -- Get this every frame incase the monitor resizes local w,h = term.getSize() local startX = math.floor( (w - 65) / 2 ) local startY = math.floor( (h - 14) / 2 ) diff --git a/src/main/resources/assets/computercraft/lua/treasure/deprecated/GopherAtl/talk/talk.lua b/src/main/resources/assets/computercraft/lua/treasure/deprecated/GopherAtl/talk/talk.lua index ee7e8a8f8..c43fd6d4a 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/deprecated/GopherAtl/talk/talk.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/deprecated/GopherAtl/talk/talk.lua @@ -1 +1 @@ -print( "\"talk\" was removed in ComputerCraft 1.6, use the builtin \"chat\" program instead!" ) \ No newline at end of file +print( "\"talk\" was removed in ComputerCraft 1.6, use the builtin \"chat\" program instead!" ) diff --git a/src/main/resources/assets/computercraft/lua/treasure/fredthead/protector/protector.lua b/src/main/resources/assets/computercraft/lua/treasure/fredthead/protector/protector.lua index 255c238d1..bd88cee1d 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/fredthead/protector/protector.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/fredthead/protector/protector.lua @@ -27,11 +27,11 @@ function initVariables() human6x = 85 human6y = 18 human1 = true - human2 = true - human3 = true - human4 = true - human5 = true - human6 = true + human2 = true + human3 = true + human4 = true + human5 = true + human6 = true human1Abducted=false human2Abducted=false human3Abducted=false @@ -245,7 +245,7 @@ function printScore() end function rewriteScores() - if newScore > score1 then + if newScore > score1 then name5=name4 score5=score4 name4=name3 score4=score3 name3=name2 score3=score2 @@ -264,7 +264,7 @@ function rewriteScores() name5=name4 score5=score4 name4=newName score4=newScore elseif newScore > score5 then - name5=newName score5=newScore + name5=newName score5=newScore end local highScoreTable = {{name1, score1}, {name2,score2}, {name3,score3}, {name4,score4}, {name5,score5}} local newHighScoreStr = textutils.serialize(highScoreTable) @@ -289,7 +289,7 @@ function newHighScoreObtained() newNameStr=newNameStr..p1 newNameStrLen=newNameStrLen+1 printCent(14,newNameStr.." ") - + elseif event=="key" and p1 == 14 and newNameStrLen>0 then newNameStr=string.sub(newNameStr,1,string.len(newNameStr)-1) newNameStrLen=newNameStrLen-1 @@ -303,9 +303,9 @@ function newHighScoreObtained() printScore() end end - - + + end function highScore() @@ -327,8 +327,8 @@ function highScore() newHighScoreObtained() end printScore() - - + + end @@ -351,24 +351,24 @@ function gameOver(gameOverMsg) running=false sleep(1.5) highScore()-- new - --playAgain + --playAgain end function playAgain() sleep(1) printCent(12,"Play again (Y or N)") - + while true do local event,p1,p2,p3 = os.pullEvent() if event=="char" then - if string.lower(p1)=="y" then + if string.lower(p1)=="y" then runGame() - elseif string.lower(p1)=="n" then + elseif string.lower(p1)=="n" then os.shutdown() end end end - + end function killPlayer() @@ -378,7 +378,7 @@ function killPlayer() moveDown=false delShip(shipYPos) lives=lives-1 - if lives==0 then + if lives==0 then gameOver("OUT OF LIVES") end killedState=true @@ -438,7 +438,7 @@ function right() end function up() - if shipYPos > 2 then + if shipYPos > 2 then delShip(shipYPos) shipYPos=shipYPos-1 checkShipCollision() @@ -449,7 +449,7 @@ function up() end function down() - if shipYPos<17 then + if shipYPos<17 then delShip(shipYPos) shipYPos=shipYPos+1 checkShipCollision() @@ -480,7 +480,7 @@ function checkShipCollision() if human3x >=24 and human3x <= 26 then human3=false humanHitRoutine() - end + end elseif human4==true and human4y==shipYPos then if human4x >=24 and human4x <= 26 then human4=false @@ -495,7 +495,7 @@ function checkShipCollision() if human6x >=24 and human6x <= 26 then human6=false humanHitRoutine() - end + end end end end @@ -551,7 +551,7 @@ function alienDown() alien1Abduct=true alien1Carry=true alien1Step=17 - end + end end end @@ -564,74 +564,74 @@ function alienRoutine() end alien1Abduct=false alien1Carry=false - if humansLeft==0 then + if humansLeft==0 then gameOver("NO HUMANS LEFT") end - + end function alienUp() if alien1==true and alien1Abduct==true then - if alien1x+1 == human1x then + if alien1x+1 == human1x then human1Abducted=true alien1Step=alien1Step-stepValue alien1y=math.floor(alien1Step) human1y=math.floor(alien1Step)+1 human1x=alien1x+1 - if human1y<=2 then + if human1y<=2 then alienRoutine() human1=false end - elseif alien1x+1 == human2x then + elseif alien1x+1 == human2x then human2Abducted=true alien1Step=alien1Step-stepValue alien1y=math.floor(alien1Step) human2y=math.floor(alien1Step)+1 human2x=alien1x+1 - if human2y<=2 then + if human2y<=2 then alienRoutine() human2=false end - elseif alien1x+1 == human3x then + elseif alien1x+1 == human3x then human3Abducted=true alien1Step=alien1Step-stepValue alien1y=math.floor(alien1Step) human3y=math.floor(alien1Step)+1 human3x=alien1x+1 - if human3y<=2 then + if human3y<=2 then alienRoutine() human3=false end - elseif alien1x+1 == human4x then + elseif alien1x+1 == human4x then human4Abducted=true alien1Step=alien1Step-stepValue alien1y=math.floor(alien1Step) human4y=math.floor(alien1Step)+1 human4x=alien1x+1 - if human4y<=2 then + if human4y<=2 then alienRoutine() human4=false end - elseif alien1x+1 == human5x then + elseif alien1x+1 == human5x then human5Abducted=true alien1Step=alien1Step-stepValue alien1y=math.floor(alien1Step) human5y=math.floor(alien1Step)+1 human5x=alien1x+1 - if human5y<=2 then + if human5y<=2 then alienRoutine() human5=false end - elseif alien1x+1 == human6x then + elseif alien1x+1 == human6x then human6Abducted=true alien1Step=alien1Step-stepValue alien1y=math.floor(alien1Step) human6y=math.floor(alien1Step)+1 human6x=alien1x+1 - if human6y<=2 then + if human6y<=2 then alienRoutine() human6=false - end + end end end if alien1==false then alienGen() end @@ -645,7 +645,7 @@ function keyPress() -- 200 UP, 208 DOWN, 203 LEFT, 205 RIGHT, 57 SPACE, 16 Q moveDown=true moveUp=false elseif pressedKey==203 or pressedKey == 30 then -- left - moveLeft=true + moveLeft=true moveRight=false elseif pressedKey==205 or pressedKey == 32 then -- right moveRight=true @@ -654,7 +654,7 @@ function keyPress() -- 200 UP, 208 DOWN, 203 LEFT, 205 RIGHT, 57 SPACE, 16 Q if bulletState==false then bulletYPos=shipYPos bulletState=true - if shipFacingRight==true then + if shipFacingRight==true then bulletXPos=shipXPos+3 bulletGoingRight=true else @@ -665,13 +665,13 @@ function keyPress() -- 200 UP, 208 DOWN, 203 LEFT, 205 RIGHT, 57 SPACE, 16 Q elseif pressedKey==25 then -- q (use 25 if p for quit) gameOver("YOU QUIT") end - + --term.setCursorPos(30,1) --write(pressedKey.." ") end function removeBullet() - if bulletGoingRight==true then + if bulletGoingRight==true then bulletXPos = 60 else bulletXPos = -10 @@ -691,7 +691,7 @@ end function humanHitRoutine() score=score-50 humansLeft=humansLeft-1 - if humansLeft==0 then + if humansLeft==0 then gameOver("NO HUMANS LEFT") end if alien1Carry==true then alien1Carry=false end @@ -699,9 +699,9 @@ end function checkBulletCollision() - if alien1 == true and bulletYPos == alien1y then - if bulletXPos >= alien1x and bulletXPos <= alien1x + 3 then - alien1Hit() + if alien1 == true and bulletYPos == alien1y then + if bulletXPos >= alien1x and bulletXPos <= alien1x + 3 then + alien1Hit() end end if human1 == true and bulletYPos == human1y and bulletXPos == human1x then human1=false humanHitRoutine() end @@ -737,15 +737,15 @@ end function gameControl() gameTimer=os.startTimer(0.1) - + while running do local event,p1,p2,p3 = os.pullEvent() if score<0 then score=0 end term.setCursorPos(1,1) term.setBackgroundColour(colours.yellow) write(string.rep(" ",w)) - - + + term.setTextColour(colours.red) term.setCursorPos(5,1) write("Score: "..score.." ") @@ -753,15 +753,15 @@ function gameControl() write("Humans Left: "..humansLeft.." ") term.setCursorPos(40,1) write("Lives: "..lives.." ") - + term.setBackgroundColour(colours.black) term.setTextColour(colours.white) - + local newStepValue = (score+0.1)/1000 if newStepValue > stepValue then stepValue= newStepValue end if stepValue>0.4 then stepValue=0.4 end - - + + --[[DEBUG term.setCursorPos(2,2) write("human1x "..human1x.." ") @@ -776,10 +776,10 @@ function gameControl() term.setCursorPos(2,7) write("human6x "..human6x.." ") ]]-- - - + + if event=="timer" and gameTimer == p1 then - if killedState==true then + if killedState==true then delShip(shipYPos) delHumans() dropHumans() @@ -800,15 +800,15 @@ function gameControl() else moveLeft=true moveRight=false - end + end killedDelay=0 end else - + --alienGen() drawShip(shipYPos) delAliens() - + delHumans() dropHumans() alienDown() @@ -817,7 +817,7 @@ function gameControl() drawHumans() drawBorder() end - + if bulletState==true then if bulletGoingRight==true then delBullet() @@ -839,7 +839,7 @@ function gameControl() end end end - + if moveLeft==true then left() end @@ -852,16 +852,16 @@ function gameControl() if moveDown==true then down() end - + gameTimer=os.startTimer(0.1) - - elseif event=="key" and killedState==false then + + elseif event=="key" and killedState==false then pressedKey=p1 keyPress() end - - end - + + end + end function runGame() @@ -875,7 +875,7 @@ end function pix(xCo,yCo,text,col) - if col== nil then term.setBackgroundColour(colours.black) + if col== nil then term.setBackgroundColour(colours.black) elseif col =="white" then term.setBackgroundColour(colours.white) elseif col =="green" then term.setBackgroundColour(colours.green) elseif col =="pink" then term.setBackgroundColour(colours.pink) @@ -977,7 +977,7 @@ function line2() pix(38,5," ","white") pix(40,5," ","white") pix(42,5," ","white") - + end function line3() @@ -1030,7 +1030,7 @@ function startScreen() term.setCursorPos(1,h) write(string.rep(" ",w)) local screenStage=0 - + screenTimer=os.startTimer(0.1) while true do local event,p1,p2,p3=os.pullEvent() @@ -1039,12 +1039,12 @@ function startScreen() clear() runGame() elseif event=="timer" and screenTimer == p1 then - + --term.setCursorPos(1,1) write("screenStage: "..screenStage.." ") - + term.setBackgroundColour(colours.black) term.setCursorPos(35,1) write("SPACE WHEN READY") - + if screenStage>0 and screenStage<0.5 then humanPixY = 18 drawHumanPix() @@ -1055,17 +1055,17 @@ function startScreen() alienPixY = -2 delAlienPix() alienPixY = -1 - drawAlienPix() + drawAlienPix() elseif screenStage>4 and screenStage<4.9 then alienPixY = -1 delAlienPix() alienPixY = 0 - drawAlienPix() + drawAlienPix() elseif screenStage>5 and screenStage<5.9 then alienPixY = 0 delAlienPix() alienPixY = 1 - drawAlienPix() + drawAlienPix() elseif screenStage>6 and screenStage<6.9 then alienPixY = 1 delAlienPix() @@ -1080,7 +1080,7 @@ function startScreen() alienPixY = 3 delAlienPix() alienPixY = 4 - drawAlienPix() + drawAlienPix() elseif screenStage>8 and screenStage<9.9 then alienPixY = 4 delAlienPix() @@ -1120,7 +1120,7 @@ function startScreen() pix(22,17," ","yellow") pix(22,18," ","yellow") humanPixY = 18 - drawHumanPix() + drawHumanPix() elseif screenStage>10.8 and screenStage<11 then pix(25,8," ","yellow") pix(24,9," ","yellow") @@ -1134,7 +1134,7 @@ function startScreen() pix(20,17," ","yellow") pix(20,18," ","yellow") humanPixY = 18 - drawHumanPix() + drawHumanPix() elseif screenStage>11.9 and screenStage<12 then pix(1,6," ","yellow") elseif screenStage>12 and screenStage<12.1 then @@ -1142,7 +1142,7 @@ function startScreen() pix(3,6," ","yellow") elseif screenStage>12.1 and screenStage<12.2 then pix(3,6," ") - pix(5,6," ","yellow") + pix(5,6," ","yellow") elseif screenStage>12.2 and screenStage<12.3 then pix(5,6," ") pix(7,6," ","yellow") @@ -1166,102 +1166,102 @@ function startScreen() end humanPixY=18 drawHumanPix() - elseif screenStage>13 and screenStage<13.1 then + elseif screenStage>13 and screenStage<13.1 then shipPixX= -16 drawShipPix() - elseif screenStage>13 and screenStage<13.1 then + elseif screenStage>13 and screenStage<13.1 then delShipPix() shipPixX= -15 - drawShipPix() - elseif screenStage>13.1 and screenStage<13.2 then + drawShipPix() + elseif screenStage>13.1 and screenStage<13.2 then delShipPix() shipPixX= -12 - drawShipPix() - elseif screenStage>13.2 and screenStage<13.3 then + drawShipPix() + elseif screenStage>13.2 and screenStage<13.3 then delShipPix() shipPixX= -9 drawShipPix() - elseif screenStage>13.2 and screenStage<13.3 then + elseif screenStage>13.2 and screenStage<13.3 then delShipPix() shipPixX= -6 drawShipPix() - elseif screenStage>13.3 and screenStage<13.4 then + elseif screenStage>13.3 and screenStage<13.4 then delShipPix() shipPixX= -3 drawShipPix() - elseif screenStage>13.4 and screenStage<13.5 then + elseif screenStage>13.4 and screenStage<13.5 then delShipPix() shipPixX= 0 drawShipPix() - elseif screenStage>13.6 and screenStage<13.7 then + elseif screenStage>13.6 and screenStage<13.7 then delShipPix() shipPixX= 3 drawShipPix() - elseif screenStage>13.8 and screenStage<13.9 then + elseif screenStage>13.8 and screenStage<13.9 then delShipPix() shipPixX= 6 drawShipPix() - elseif screenStage>13.9 and screenStage<14 then + elseif screenStage>13.9 and screenStage<14 then delShipPix() shipPixX= 9 drawShipPix() - elseif screenStage>14.1 and screenStage<14.2 then + elseif screenStage>14.1 and screenStage<14.2 then delShipPix() shipPixX= 12 drawShipPix() - elseif screenStage>14.2 and screenStage<14.3 then + elseif screenStage>14.2 and screenStage<14.3 then delShipPix() shipPixX= 15 drawShipPix() - elseif screenStage>14.3 and screenStage<14.4 then + elseif screenStage>14.3 and screenStage<14.4 then delShipPix() shipPixX= 18 drawShipPix() - elseif screenStage>14.4 and screenStage<14.5 then + elseif screenStage>14.4 and screenStage<14.5 then delShipPix() shipPixX= 21 drawShipPix() - elseif screenStage>14.5 and screenStage<14.6 then + elseif screenStage>14.5 and screenStage<14.6 then delShipPix() shipPixX= 24 drawShipPix() - elseif screenStage>14.6 and screenStage<14.7 then + elseif screenStage>14.6 and screenStage<14.7 then delShipPix() shipPixX= 27 drawShipPix() - elseif screenStage>14.7 and screenStage<14.8 then + elseif screenStage>14.7 and screenStage<14.8 then delShipPix() shipPixX= 30 drawShipPix() - elseif screenStage>14.8 and screenStage<14.9 then + elseif screenStage>14.8 and screenStage<14.9 then delShipPix() shipPixX= 33 drawShipPix() - elseif screenStage>14.9 and screenStage<15 then + elseif screenStage>14.9 and screenStage<15 then delShipPix() shipPixX= 36 drawShipPix() - elseif screenStage>15 and screenStage<15.1 then + elseif screenStage>15 and screenStage<15.1 then delShipPix() shipPixX= 39 drawShipPix() - elseif screenStage>15.1 and screenStage<15.2 then + elseif screenStage>15.1 and screenStage<15.2 then delShipPix() shipPixX= 41 drawShipPix() - elseif screenStage>15.2 and screenStage<15.3 then + elseif screenStage>15.2 and screenStage<15.3 then delShipPix() shipPixX= 44 drawShipPix() - elseif screenStage>15.3 and screenStage<15.4 then + elseif screenStage>15.3 and screenStage<15.4 then delShipPix() shipPixX= 47 drawShipPix() - elseif screenStage>15.4 and screenStage<15.5 then + elseif screenStage>15.4 and screenStage<15.5 then delShipPix() shipPixX= 50 drawShipPix() - elseif screenStage>15.5 and screenStage<15.6 then + elseif screenStage>15.5 and screenStage<15.6 then delShipPix() elseif screenStage>16 and screenStage<16.9 then humanPixY=18 @@ -1284,7 +1284,7 @@ function startScreen() write("Fire when ready") elseif screenStage>22.1 and screenStage <27 then introHighScoreTable() - elseif screenStage>27 then + elseif screenStage>27 then term.setBackgroundColour(colours.black) for i = 2,h-1 do term.setCursorPos(1,i) @@ -1292,7 +1292,7 @@ function startScreen() end screenStage=0 end - + screenStage=screenStage+0.1 screenTimer=os.startTimer(0.025) end @@ -1308,4 +1308,4 @@ else term.setCursorPos(1,1) print("I'm sorry, Protector requires an Advanced Computer to run") print(" ") -end \ No newline at end of file +end diff --git a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/goldrunner/goldrunner.lua b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/goldrunner/goldrunner.lua index 2aeaf4072..b75a5ca79 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/goldrunner/goldrunner.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/goldrunner/goldrunner.lua @@ -89,7 +89,7 @@ local titleLevel = { local function parseValue(x, y, lchar) if tonumber(lchar, 16) then lchar = math.pow(2, tonumber(lchar,16)) - + if lchar == colours.blue then map[y][x] = 0 elseif lchar == colours.brown then @@ -146,7 +146,7 @@ local function loadMap(_sPath) goldMap = {} monks = {} goldCount = 0 - + local file = fs.open(_sPath, "r") local line = file:readLine() while line do @@ -279,14 +279,14 @@ local function resetMap() monk.x = monk.spawnX monk.y = monk.spawnY end - + for _,timer in pairs(blockTimers) do map[timer.y][timer.x] = 0 end blockTimers = {} plX = plspawnX plY = plspawnY - + moveTimer = -1 shootTimer = -1 spawnTimer = -1 @@ -320,27 +320,27 @@ end local function drawLevelList() local minLev = ((levelLot-1) * 10 + 1) local maxLev = minLev + math.min(10, #levelList - (levelLot-1) * 10) - 1 - + term.setCursorPos(7, 2) term.setBackgroundColour(colours.black) term.clearLine() for j = 1,49 do updateMap(j,2) end - + term.setBackgroundColour(colours.black) term.setTextColour(colours.white) term.setCursorPos(7, 2) local msg = "Levels "..minLev.." to "..maxLev.." of "..#levelList term.write(msg) - + term.setTextColour(colours.yellow) term.setCursorPos(4, 2) if levelLot > 1 then term.write("<-") - else term.write(" ") end - + else term.write(" ") end + term.setCursorPos(8 + #msg, 2) if maxLev < #levelList then term.write("->") else term.write(" ") end - + for i = 1,10 do term.setCursorPos(1, 3+i) for j = 1,49 do updateMap(j,3+i) end @@ -371,12 +371,12 @@ local function loadTitleScreen() if #map == 18 then break end end maxGoldCount = goldCount - + drawMap() term.setCursorPos(1,19) term.setBackgroundColour(colours.blue) term.clearLine() - + menIndex = 1 titleLoaded = true end @@ -384,22 +384,22 @@ end --Opens an in-game menu to display a series of options. local function inGameMenu(menuList) menIndex = 1 - + local squareTop,squareBottom = 4,6 + #menuList * 2 local squareSize = 0 for i=1,#menuList do squareSize = math.max(squareSize, #menuList[i] + 6) end - + for y=squareTop,squareBottom do term.setCursorPos(w/2 - squareSize/2, y) term.setBackgroundColour(colours.lightBlue) term.write(string.rep(" ", squareSize)) - + if y ~= squareTop and y ~= squareBottom then term.setCursorPos(w/2 - squareSize/2 + 1, y) term.setBackgroundColour(colours.black) term.write(string.rep(" ", squareSize - 2)) end - + if y ~= squareTop and y ~= squareBottom and y % 2 == 0 then local opt = menuList[(y - squareTop) / 2] term.setCursorPos(w/2 - #opt/2, y) @@ -407,7 +407,7 @@ local function inGameMenu(menuList) term.write(opt) end end - + local p1 = nil repeat for i=1,#menuList do @@ -426,20 +426,20 @@ local function inGameMenu(menuList) end end _,p1 = os.pullEvent("key") - + if p1 == keys.up and menIndex > 1 then menIndex = menIndex - 1 elseif p1 == keys.down and menIndex < #menuList then menIndex = menIndex + 1 end until p1 == keys.enter - + return menuList[menIndex] end --Checks to see if any given desired move is legal. Monks and players both use this. local function isLegalMove(initX,initY,finX,finY) - if finY < 1 or finY > #map or finX < 1 or finX > 49 then - return false + if finY < 1 or finY > #map or finX < 1 or finX > 49 then + return false end - + if map[finY][finX] ~= 0 and map[finY][finX] ~= '#' then --This reports 'self moves' as being illegal, but that's fine for _,monk in pairs(monks) do @@ -450,10 +450,10 @@ local function isLegalMove(initX,initY,finX,finY) then return true elseif finY == initY+1 and (map[finY][finX] == "H" or (map[finY][finX] == "h" and goldCount == 0) or (type(map[finY][finX]) == "number" and map[finY][finX] > 0) or map[finY][finX] == nil or - map[finY][finX] == "V" or map[finY][finX] == "-" or (map[finY][finX] == 'h' and goldCount ~= 0)) + map[finY][finX] == "V" or map[finY][finX] == "-" or (map[finY][finX] == 'h' and goldCount ~= 0)) then return true - elseif finX == initX-1 or finX == initX+1 then - return true + elseif finX == initX-1 or finX == initX+1 then + return true end end end @@ -461,12 +461,12 @@ end --Moves the player to a given step. local function movePlayer(x,y,ignoreLegal) if not ignoreLegal and not isLegalMove(plX,plY,x,y) then return false end - + local ox = plX local oy = plY plX = x plY = y - + updateMap(ox,oy) updateMap(x,y) if goldMap[y][x] == 1 then @@ -481,14 +481,14 @@ local function movePlayer(x,y,ignoreLegal) started = false nextLevel = true end - - pfalling = (y < #map and map[y][x] ~= '-' and map[y][x] ~= 'H' and not (map[y][x] == 'h' and goldCount == 0) + + pfalling = (y < #map and map[y][x] ~= '-' and map[y][x] ~= 'H' and not (map[y][x] == 'h' and goldCount == 0) and (map[y+1][x] == nil or map[y+1][x] == "V" or map[y+1][x] == 2 or map[y+1][x] == '-')) if (y < #map and map[y+1][x] == 'h' and goldCount ~= 0) then pfalling = true end for _,monk in pairs(monks) do if monk.x == plX and monk.y == plY + 1 then pfalling = false break end end - + return true end @@ -634,7 +634,7 @@ local function updateMonks() end end end - + if not (monk.trapped or monk.dead) then --Has the monk decided on moving left or right? If so we try to move him if monk.desX and not monk.falling then @@ -708,7 +708,7 @@ local function updateBlockTimer(tid) end local function shootBlock(x,y) - if y <= #map and map[y][x] == 0 and (map[y-1][x] == nil + if y <= #map and map[y][x] == 0 and (map[y-1][x] == nil or map[y-1][x] == 2 or (map[y-1][x] == 'h' and goldCount > 0)) then map[y][x] = 3 table.insert(blockTimers, {x = x; y = y; timer = os.startTimer(0.1);} ) @@ -718,14 +718,14 @@ end local function handleEvents() local id,p1,p2,p3 = os.pullEvent() - + if id == "key" then --Menu Handling if p1 == keys.up then if menIndex > 1 then menIndex = menIndex - 1 end elseif p1 == keys.down then - if inLevelSelect then - if menIndex < math.min(10, #levelList - (levelLot-1)*10) then + if inLevelSelect then + if menIndex < math.min(10, #levelList - (levelLot-1)*10) then menIndex = menIndex + 1 end elseif menIndex < #titleOptions then menIndex = menIndex + 1 end @@ -736,7 +736,7 @@ local function handleEvents() levelLot = levelLot + 1 drawLevelList() end - + --Game Handling if p1 == keys.a and moveTimer == -1 and spawnTimer == -1 then movePlayer(plX-1,plY) @@ -811,7 +811,7 @@ local function handleEvents() monk.trapped = nil monk.behaviour = "none" monk.justEscaped = true - + updateMap(monk.x, monk.y+1) drawMonk(monk) end @@ -859,12 +859,12 @@ local pallette = { { t = colours.black, b = colours.blue, s = " ", n = "Solid G local brushType = 1 local function getHexOf(colour) - if not colour or not tonumber(colour) then - return " " + if not colour or not tonumber(colour) then + return " " end local value = math.log(colour)/math.log(2) - if value > 9 then - value = hexnums[value] + if value > 9 then + value = hexnums[value] end return value end @@ -878,7 +878,7 @@ local function drawFooter() term.setCursorPos(w,i) term.write(" ") end - + term.setBackgroundColour(colours.black) term.setTextColour(colours.blue) term.setCursorPos(2,h) @@ -904,7 +904,7 @@ local function drawPallette(xpos,ypos) local top = ypos if xpos + xdim > w then left = left + (w - xpos - xdim) end if ypos + ydim > h then top = top + (h - ypos - ydim) end - + --There's no easy way to do this... so we draw it manually :( for i=0,4 do term.setCursorPos(left, top + i) @@ -913,25 +913,25 @@ local function drawPallette(xpos,ypos) if i == 0 or i == 4 then term.write("*-----*") else term.write("* *") end end - + for i=1,#pallette-1 do local ypl = 1 local xmv = i if i > 5 then ypl = 2 xmv = i - 5 end - + term.setCursorPos(left + xmv, top+ypl) term.setBackgroundColour(pallette[i].b) term.setTextColour(pallette[i].t) term.write(pallette[i].s) end - + term.setCursorPos(left + 1, top + 3) term.setBackgroundColour(colours.red) term.setTextColour(colours.black) term.write("ERASE") - + local _,button,x,y = os.pullEvent("mouse_click") - + if button == 1 then if y == top + 1 and x > left and x < left + 6 then brushType = x-left @@ -941,9 +941,9 @@ local function drawPallette(xpos,ypos) brushType = 11 end end - + for y = top,top+ydim do - for x = left,left+xdim do + for x = left,left+xdim do --Not sure why the -2 is necessary if map[y+drawOffsetY] then updateMap(x-2,y+drawOffsetY) end end @@ -955,7 +955,7 @@ end local function saveCurrentMap(path) local file = io.open(shell.resolve(".").."/levels/"..path, "w") if not file then return false end - + drawMap() drawFooter() local msg = "Saving.." @@ -968,7 +968,7 @@ local function saveCurrentMap(path) term.write(string.rep(" ", 18)) term.setCursorPos(w/2-9,6) term.setBackgroundColour(colours.lime) - + for y=1,#map do local xstr = "" for x=1,49 do @@ -1016,14 +1016,14 @@ local function runLevelEditor() end monks = {} end - + drawMap() drawFooter() - + while inLevelEditor do local id,button,x,y = os.pullEvent() if id == "mouse_click" or id == "mouse_drag" then - if button == 2 then + if button == 2 then drawPallette(x,y) elseif x > drawOffsetX and x <= 49 + drawOffsetX and y > drawOffsetY and y <= 18 + drawOffsetY then if pallette[brushType].v == "player" then @@ -1095,7 +1095,7 @@ local function runLevelSelect() if not titleLoaded then loadTitleScreen() monkTimer = os.startTimer(moveIntv * 1.5) - else + else drawMap() drawEndgameMap() term.setCursorPos(1,19) @@ -1103,11 +1103,11 @@ local function runLevelSelect() term.clearLine() end drawLevelList() - + menSel = "none" repeat handleEvents() - + term.setBackgroundColour(colours.black) term.setTextColour(colours.yellow) for i=1,10 do @@ -1131,23 +1131,23 @@ local function runTitle() term.write("Gold Runner") term.setCursorPos(16,4) term.write("By Nitrogen Fingers") - + term.setTextColour(colours.white) for i=1,#titleOptions do term.setCursorPos(19, 5 + (i*2)) term.write(titleOptions[i]) end - + term.setCursorPos(16, 7) term.setTextColour(colours.yellow) term.write("->") - + menSel = "none" monkTimer = os.startTimer(moveIntv * 1.5) - + repeat handleEvents() - + term.setBackgroundColour(colours.black) term.setTextColour(colours.yellow) for i=1,#titleOptions do @@ -1166,25 +1166,25 @@ local function playLevel() drawHUD() os.pullEvent("key") movePlayer(plX,plY,true) - + monkTimer = os.startTimer(moveIntv * 1.5) moveTimer = os.startTimer(moveIntv) shootTimer = -1 spawnTimer = -1 - + started = true while started do handleEvents() end - + if menSel == "Quit" or menSel == "Back to Title" or menSel == "Edit Level" then running = false return end menSel = "none" - + if nextLevel then - if currentLevel == #levelList then + if currentLevel == #levelList then started = false running = false break @@ -1198,12 +1198,12 @@ local function playLevel() else playerLives = playerLives-1 if playerLives > 0 then resetMap() - else - running = false + else + running = false end end end - + if nextLevel then local msg = "All levels defeated, Gold Runner!" term.setBackgroundColour(colours.black) @@ -1247,7 +1247,7 @@ while menSel ~= "Quit" do term.setCursorPos(1,19) term.setBackgroundColour(colours.blue) term.clearLine() - + term.setCursorPos(16,10) term.setBackgroundColour(colours.black) term.setTextColour(colours.white) @@ -1256,13 +1256,13 @@ while menSel ~= "Quit" do term.setCursorPos(17,11) term.setCursorBlink(true) local levelName = "" - + local id,p1 repeat id,p1 = os.pullEvent() if id == "key" and p1 == keys.backspace then levelName = string.sub(levelName, 1, #levelName - 1) - elseif id == "timer" and p1 == monkTimer then + elseif id == "timer" and p1 == monkTimer then updateMonks() monkTimer = os.startTimer(moveIntv * 2) elseif id == "char" and #levelName < 14 then @@ -1273,15 +1273,15 @@ while menSel ~= "Quit" do term.write(levelName..string.rep(" ",14 - #levelName)) term.setCursorPos(17 + #levelName ,11) until id == "key" and p1 == keys.enter and #levelName > 0 - + term.setCursorBlink(false) levelEditName = levelName runLevelEditor() - + if menSel == "Play Level" then currentLevel = nil levelList = fs.list(shell.resolve(".").."/levels") - for num,name in pairs(levelList) do + for num,name in pairs(levelList) do if name == levelName then currentLevel = num break @@ -1296,7 +1296,7 @@ while menSel ~= "Quit" do runLevelEditor() term.setBackgroundColour(colours.black) term.clear() - + if menSel == "Play Level" then menSel = "New Game" else diff --git a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/3dprint.lua b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/3dprint.lua index be40631ff..89dc031cc 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/3dprint.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/3dprint.lua @@ -1,7 +1,7 @@ --[[ 3D Print A printing program for use with NPaintPro - + By NitrogenFingers ]]-- @@ -30,7 +30,7 @@ local commandList = { ["DE"] = endPrint; } ---Splits a string according to a pattern into a table +--Splits a string according to a pattern into a table local function split(str, pattern) local t = { } local fpat = "(.-)" .. pattern @@ -56,11 +56,11 @@ local function respondToQuery() print("Listening for ACT/ID query") local id,key = rednet.receive() print("Received : "..key) - + if key == "$3DPRINT IDENTIFY" then print("Requested Identification") rednet.send(id, "$3DPRINT IDACK "..os.getComputerLabel()) - + elseif key == "$3DPRINT ACTIVATE" then print("Requested Activation") activeCommander = id @@ -76,10 +76,10 @@ local function performPrint() while operatingPrint do local id,msg = rednet.receive() print("Command : "..msg) - + if id == activeCommander and string.find(msg, "$PC") == 1 then local cmds = split(msg, " ") - + --It's a bit of a hack, but those are the 2 methods required for a refuel if turtle.getFuelLevel() == 0 and cmds[2] ~= "SS" and cmds[2] ~= "RF" then rednet.send(id, "$3DPRINT OOF") @@ -95,7 +95,7 @@ local function performPrint() commandList[cmds[2]][i](tonumber(cmds[3])) end end - + rednet.send(activeCommander, "$3DPRINT ACK") end end @@ -116,4 +116,4 @@ while true do respondToQuery() --Perform the print performPrint() -end \ No newline at end of file +end diff --git a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/gameutils.lua b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/gameutils.lua index 8a029e83b..dac2bcc78 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/gameutils.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/gameutils.lua @@ -25,7 +25,7 @@ function initializeBuffer(terminal) if not terminal.isColour() then error("Parameter does not represent an advanced computer.") end - + tw,th = terminal.getSize() backbuffer = { } for y=1,th do @@ -41,8 +41,8 @@ end function clearBuffer(colour) if not backbuffer then error("Back buffer not yet initialized!") - end - + end + for y=1,#backbuffer do backbuffer[y] = { } if colour then @@ -60,26 +60,26 @@ end function writeToBuffer(entity) if not backbuffer then error("Back buffer not yet initialized!") - end - + end + local image = nil if entity.type == "animation" then image = entity.frames[entity.currentFrame] else image = entity.image end - + for y=1,image.dimensions.height do for x=1,image.dimensions.width do if image[y][x] then local xpos,ypos = x,y if entity.mirror.x then xpos = image.dimensions.width - x + 1 end if entity.mirror.y then ypos = image.dimensions.height - y + 1 end - + --If the YPos doesn't exist, no need to loop through the rest of X! --Don't you love optimization? if not backbuffer[entity.y + ypos - 1] then break end - + backbuffer[entity.y + ypos - 1][entity.x + xpos - 1] = image[y][x] end end @@ -93,7 +93,7 @@ end function drawBuffer(terminal) if not backbuffer then error("Back buffer not yet initialized!") - end + end if not terminal then terminal = term end if not terminal.setCursorPos or not terminal.setBackgroundColour or not terminal.write then error("Parameter cannot be used to initialize the backbuffer.") @@ -101,7 +101,7 @@ function drawBuffer(terminal) if not terminal.isColour() then error("Parameter does not represent an advanced computer.") end - + for y=1,math.min(#backbuffer, th) do for x=1,tw do if backbuffer[y][x] then @@ -167,14 +167,14 @@ end ]]-- local function drawS(self) local image = self.image - + for y=1,image.dimensions.height do for x=1,image.dimensions.width do if image[y][x] then local xpos,ypos = x,y if self.mirror.x then xpos = image.dimensions.width - x + 1 end if self.mirror.y then ypos = image.dimensions.height - y + 1 end - + term.setBackgroundColour(image[y][x]) term.setCursorPos(self.x + xpos - 1, self.y + ypos - 1) term.write(" ") @@ -198,7 +198,7 @@ local function drawA(self, frame) local xpos,ypos = x,y if self.mirror.x then xpos = image.dimensions.width - x + 1 end if self.mirror.y then ypos = image.dimensions.height - y + 1 end - + term.setBackgroundColour(image[y][x]) term.setCursorPos(self.x + xpos - 1, self.y + ypos - 1) term.write(" ") @@ -259,16 +259,16 @@ local function drawBounds(entity, colour) local image = nil if entity.type == "animation" then image = entity.frames[entity.currentFrame] else image = entity.image end - + term.setBackgroundColour(colour) - + corners = { topleft = { x = entity.x + image.bounds.x - 1, y = entity.y + image.bounds.y - 1 }; topright = { x = entity.x + image.bounds.x + image.bounds.width - 2, y = entity.y + image.bounds.y - 1 }; botleft = { x = entity.x + image.bounds.x - 1, y = entity.y + image.bounds.y + image.bounds.height - 2 }; botright = { x = entity.x + image.bounds.x + image.bounds.width - 2, y = entity.y + image.bounds.y + image.bounds.height - 2 }; } - + term.setCursorPos(corners.topleft.x, corners.topleft.y) term.write(" ") term.setCursorPos(corners.topright.x, corners.topright.y) @@ -308,7 +308,7 @@ end local function rCollidesWith(self, other) --First we construct the rectangles local img1C, img2C = createRectangle(self), createRectangle(other) - + --We then determine the "relative position" , in terms of which is farther left or right leftmost,rightmost,topmost,botmost = nil,nil,nil,nil if img1C.left < img2C.left then @@ -325,13 +325,13 @@ local function rCollidesWith(self, other) topmost = img2C botmost = img1C end - + --Then we determine the distance between the "extreme" edges- --distance between leftmost/right edge and rightmost/left edge --distance between topmost/bottom edge and bottommost/top edge local xdist = rightmost.left - leftmost.right local ydist = botmost.top - topmost.bottom - + --If both are negative, our rectangles intersect! return xdist <= 0 and ydist <= 0 end @@ -352,13 +352,13 @@ local function pCollidesWith(self, other) else img1 = self.image end if other.type == "animation" then img2 = other.frames[other.currentFrame] else img2 = other.image end - + --...then we position them... leftmost,rightmost,topmost,botmost = nil,nil,nil,nil --We also keep track of which is left and which is right- it doesn't matter in a rectangle --collision but it does in a pixel collision. img1T,img2T = {},{} - + if img1C.left < img2C.left then leftmost = img1C rightmost = img2C @@ -377,15 +377,15 @@ local function pCollidesWith(self, other) botmost = img1C img2T.top = true end - + --...and we again find the distances between the extreme edges. local xdist = rightmost.left - leftmost.right local ydist = botmost.top - topmost.bottom - + --If these distances are > 0 then we stop- no need to go any farther. if xdist > 0 or ydist > 0 then return false end - - + + for x = rightmost.left, rightmost.left + math.abs(xdist) do for y = botmost.top, botmost.top + math.abs(ydist) do --We know a collision has occurred if a pixel is occupied by both images. We do this by @@ -398,16 +398,16 @@ local function pCollidesWith(self, other) else testX = x - img1C.left + 1 end if img1T.top then testY = y - img1C.top + 1 else testY = y - img1C.top + 1 end - + local occupy1 = img1[testY + img1.bounds.y-1][testX + img1.bounds.x-1] ~= nil - + if img2T.left then testX = x - img2C.left + 1 else testX = x - img2C.left + 1 end if img2T.top then testY = y - img2C.top + 1 else testY = y - img2C.top + 1 end - + local occupy2 = img2[testY + img2.bounds.y-1][testX + img2.bounds.x-1] ~= nil - + if occupy1 and occupy2 then return true end end end @@ -429,7 +429,7 @@ local function moveTo(self, x, y) else image = self.image end - + self.x = x - image.bounds.x + 1 self.y = y - image.bounds.y + 1 end @@ -448,7 +448,7 @@ image:table = a table of the image. Indexed by height, a series of sub-tables, e dimensions:table = width = the width of the entire image in pixels height = the height of the entire image in pixels - + mirror:table = x:bool = whether or not the image is mirrored on the X axis y:bool = whether or not the image is mirrored on the Y axis @@ -464,19 +464,19 @@ draw:function = see drawS (above) y:number = the initial Y position of the sprite ]]-- function loadSprite(path, x, y) - local sprite = { + local sprite = { type = "sprite", x = x, y = y, image = { }, mirror = { x = false, y = false } } - + if fs.exists(path) then local file = io.open(path, "r" ) local leftX, rightX = math.huge, 0 local topY, botY = nil,nil - + local lcount = 0 for line in file:lines() do lcount = lcount+1 @@ -492,7 +492,7 @@ function loadSprite(path, x, y) end end file:close() - + sprite.image.bounds = { x = leftX, width = rightX - leftX + 1, @@ -503,10 +503,10 @@ function loadSprite(path, x, y) width = rightX, height = botY } - + sprite.x = sprite.x - leftX + 1 sprite.y = sprite.y - topY + 1 - + sprite.repaint = repaintS sprite.rCollidesWith = rCollidesWith sprite.pCollidesWith = pCollidesWith @@ -536,13 +536,13 @@ function loadAnimation(path, x, y, currentFrame) mirror = { x = false, y = false }, currentFrame = currentFrame } - + table.insert(anim.frames, { }) if fs.exists(path) then local file = io.open(path, "r") local leftX, rightX = math.huge, 0 local topY, botY = nil,nil - + local lcount = 0 for line in file:lines() do lcount = lcount+1 @@ -589,17 +589,17 @@ function loadAnimation(path, x, y, currentFrame) } anim.x = anim.x - leftX + 1 anim.y = anim.y - topY + 1 - - if not currentFrame or type(currentFrame) ~= "number" or currentFrame < 1 or - currentFrame > #anim.frames then - anim.currentFrame = 1 + + if not currentFrame or type(currentFrame) ~= "number" or currentFrame < 1 or + currentFrame > #anim.frames then + anim.currentFrame = 1 end - + anim.timerID = nil anim.lowerBound = 1 anim.upperBound = #anim.frames anim.updating = false - + anim.repaint = repaintA anim.rCollidesWith = rCollidesWith anim.pCollidesWith = pCollidesWith @@ -612,4 +612,4 @@ function loadAnimation(path, x, y, currentFrame) else error(path.." not found!") end -end \ No newline at end of file +end diff --git a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/npaintpro.lua b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/npaintpro.lua index 0c214a60d..826468eb7 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/npaintpro.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/nitrogenfingers/npaintpro/npaintpro.lua @@ -68,7 +68,7 @@ local px,py,pz,pfx,pfz = 0,0,0,0,0 --The form of layering used local layering = "up" ---The animation state of the selection rectangle and image buffer +--The animation state of the selection rectangle and image buffer local rectblink = 0 --The ID for the timer local recttimer = nil @@ -268,7 +268,7 @@ local helpTopics = { message = "Clicking on the mode display at the bottom of the screen will open the options menu. Here you can".. " activate all of the modes in the program with a simple mouse click. Pressing left control will open up the".. " file menu automatically.", - controls = { + controls = { { "leftCtrl", "Opens the file menu" }, { "leftAlt", "Opens the paint menu" } } @@ -317,8 +317,8 @@ local toplim,botlim,leflim,riglim = nil,nil,nil,nil --The selected path local sPath = nil ---[[ - Section: Helpers +--[[ + Section: Helpers ]]-- --[[Converts a colour parameter into a single-digit hex coordinate for the colour @@ -326,12 +326,12 @@ local sPath = nil Returns:string A string conversion of the colour ]]-- local function getHexOf(colour) - if not colour or not tonumber(colour) then - return " " + if not colour or not tonumber(colour) then + return " " end local value = math.log(colour)/math.log(2) - if value > 9 then - value = hexnums[value] + if value > 9 then + value = hexnums[value] end return value end @@ -355,7 +355,7 @@ end local function updateImageLims(forAllFrames) local f,l = sFrame,sFrame if forAllFrames == true then f,l = 1,framecount end - + toplim,botlim,leflim,riglim = nil,nil,nil,nil for locf = f,l do for y,_ in pairs(frames[locf]) do @@ -371,7 +371,7 @@ local function updateImageLims(forAllFrames) end end end - + --There is just... no easier way to do this. It's horrible, but necessary if textEnabled then for locf = f,l do @@ -406,19 +406,19 @@ end function calculateMaterials() updateImageLims(animated) requiredMaterials = {} - for i=1,16 do - requiredMaterials[i] = 0 + for i=1,16 do + requiredMaterials[i] = 0 end - + if not toplim then return end - + for i=1,#frames do for y = toplim, botlim do for x = leflim, riglim do if type(frames[i][y][x]) == "number" then requiredMaterials[math.log(frames[i][y][x],10)/math.log(2,10) + 1] = requiredMaterials[math.log(frames[i][y][x],10)/math.log(2,10) + 1] + 1 - end + end end end end @@ -455,16 +455,16 @@ end local function rsTimeReceive(timeout) local timerID if timeout then timerID = os.startTimer(timeout) end - + local id,key,msg = nil,nil while true do id,key,msg = os.pullEvent() - + if id == "timer" then if key == timerID then return else updateTimer(key) end end - if id == "rednet_message" then + if id == "rednet_message" then return key,msg end end @@ -490,9 +490,9 @@ local function drawPictureTable(image, xinit, yinit, alpha) end end ---[[ - Section: Loading -]]-- +--[[ + Section: Loading +]]-- --[[Loads a non-animted paint file into the program Params: path:string = The path in which the file is located @@ -526,7 +526,7 @@ local function loadNFT(path) frames[sFrame] = { } frames[sFrame].text = { } frames[sFrame].textcol = { } - + if fs.exists(path) then local file = io.open(path, "r") local sLine = file:read() @@ -535,7 +535,7 @@ local function loadNFT(path) table.insert(frames[sFrame], num, {}) table.insert(frames[sFrame].text, num, {}) table.insert(frames[sFrame].textcol, num, {}) - + --As we're no longer 1-1, we keep track of what index to write to local writeIndex = 1 --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour @@ -614,13 +614,13 @@ local function saveNFP(path) local file = io.open(path, "w") updateImageLims(false) - if not toplim then + if not toplim then file:close() return end for y=1,botlim do local line = "" - if frames[sFrame][y] then + if frames[sFrame][y] then for x=1,riglim do line = line..getHexOf(frames[sFrame][y][x]) end @@ -639,7 +639,7 @@ local function saveNFT(path) if not fs.exists(sDir) then fs.makeDir(sDir) end - + local file = io.open(path, "w") updateImageLims(false) if not toplim then @@ -678,17 +678,17 @@ local function saveNFA(path) if not fs.exists(sDir) then fs.makeDir(sDir) end - + local file = io.open(path, "w") updateImageLims(true) - if not toplim then + if not toplim then file:close() return end for i=1,#frames do for y=1,botlim do local line = "" - if frames[i][y] then + if frames[i][y] then for x=1,riglim do line = line..getHexOf(frames[i][y][x]) end @@ -709,14 +709,14 @@ local function init() if textEnabled then loadNFT(sPath) table.insert(ddModes, 2, { "text", "textpaint", name = "text"}) - elseif animated then + elseif animated then loadNFA(sPath) table.insert(ddModes, #ddModes, { "record", "play", name = "anim" }) table.insert(ddModes, #ddModes, { "go to", "remove", name = "frames"}) table.insert(ddModes[2], #ddModes[2], "blueprint on") table.insert(ddModes[2], #ddModes[2], "layers on") - else - loadNFP(sPath) + else + loadNFP(sPath) table.insert(ddModes[2], #ddModes[2], "blueprint on") end @@ -725,8 +725,8 @@ local function init() end end ---[[ - Section: Drawing +--[[ + Section: Drawing ]]-- @@ -747,7 +747,7 @@ local function drawLogo() msg = "By NitrogenFingers" term.setCursorPos(w/2 - #msg/2, h-2) term.write(msg) - + os.pullEvent() end @@ -772,13 +772,13 @@ local function drawCanvas() if pz >= 1 and pz <= #frames then sFrame = pz end - + if py < sy then sy = py elseif py > sy + h - 1 then sy = py + h - 1 end if px < sx then sx = px elseif px > sx + w - 2 then sx = px + w - 2 end end - + if pfx == 1 then turtlechar = ">" elseif pfx == -1 then turtlechar = "<" elseif pfz == 1 then turtlechar = "V" @@ -794,10 +794,10 @@ local function drawCanvas() else topLayer,botLayer = sFrame,sFrame end - + for currframe = botLayer,topLayer,1 do for y=sy+1,sy+h-1 do - if frames[currframe][y] then + if frames[currframe][y] then for x=sx+1,sx+w-2 do term.setCursorPos(x-sx,y-sy) if frames[currframe][y][x] then @@ -808,7 +808,7 @@ local function drawCanvas() else term.write(" ") end - else + else tileExists = false for i=currframe-1,botLayer,-1 do if frames[i][y][x] then @@ -816,7 +816,7 @@ local function drawCanvas() break end end - + if not tileExists then if blueprint then term.setBackgroundColour(colours.blue) @@ -835,7 +835,7 @@ local function drawCanvas() term.write(" ") end else - term.setBackgroundColour(alphaC) + term.setBackgroundColour(alphaC) if textEnabled and frames[currframe].textcol[y][x] and frames[currframe].text[y][x] then term.setTextColour(frames[currframe].textcol[y][x]) term.write(frames[currframe].text[y][x]) @@ -849,7 +849,7 @@ local function drawCanvas() else for x=sx+1,sx+w-2 do term.setCursorPos(x-sx,y-sy) - + tileExists = false for i=currframe-1,botLayer,-1 do if frames[i][y] and frames[i][y][x] then @@ -857,7 +857,7 @@ local function drawCanvas() break end end - + if not tileExists then if blueprint then term.setBackgroundColour(colours.blue) @@ -876,7 +876,7 @@ local function drawCanvas() term.write(" ") end else - term.setBackgroundColour(alphaC) + term.setBackgroundColour(alphaC) term.write(" ") end end @@ -884,7 +884,7 @@ local function drawCanvas() end end end - + --Then the printer, if he's on if state == "active print" then local bgColour = alphaC @@ -899,14 +899,14 @@ local function drawCanvas() bgColour = frames[sFrame][py-sy][px-sx] elseif blueprint then bgColour = colours.blue end end - + term.setBackgroundColour(bgColour) if bgColour == colours.black then term.setTextColour(colours.white) else term.setTextColour(colours.black) end - + term.write(turtlechar) end - + --Then the buffer if selectrect then if buffer and rectblink == 1 then @@ -920,12 +920,12 @@ local function drawCanvas() end end end - + --This draws the "selection" box local add = nil if buffer then term.setBackgroundColour(colours.lightGrey) - else + else term.setBackgroundColour(colours.grey) end for i=selectrect.x1, selectrect.x2 do @@ -947,7 +947,7 @@ local function drawCanvas() end end ---[[Draws the colour picker on the right side of the screen, the colour pallette and the footer with any +--[[Draws the colour picker on the right side of the screen, the colour pallette and the footer with any messages currently being displayed Params: none Returns:nil @@ -1001,7 +1001,7 @@ local function drawInterface() end --Footer if inMenu then return end - + term.setCursorPos(1, h) term.setBackgroundColour(colours.lightGrey) term.setTextColour(colours.grey) @@ -1016,14 +1016,14 @@ local function drawInterface() term.setBackgroundColour(colours.lightGrey) term.setTextColour(colours.grey) term.write(getStateMessage()) - + local coords="X:"..sx.." Y:"..sy if animated then coords = coords.." Frame:"..sFrame.."/"..framecount.." " end term.setCursorPos(w-#coords+1,h) if state == "play" then term.setBackgroundColour(colours.lime) elseif record then term.setBackgroundColour(colours.red) end term.write(coords) - + if animated then term.setCursorPos(w-1,h) term.setBackgroundColour(colours.grey) @@ -1075,17 +1075,17 @@ local function drawHelpScreen() print(helpTopics[selectedHelp].controls[i][2]) end end - + local id,p1,p2,p3 = os.pullEvent() - + if id == "timer" then updateTimer(p1) - elseif id == "key" then + elseif id == "key" then if selectedHelp then selectedHelp = nil else break end elseif id == "mouse_click" then - if not selectedHelp then + if not selectedHelp then if p3 >=3 and p3 <= 2+#helpTopics then - selectedHelp = p3-2 + selectedHelp = p3-2 else break end else selectedHelp = nil @@ -1137,7 +1137,7 @@ local function wprintOffCenter(msg, height, width, offset) end term.setCursorPos(width/2 - #string.sub(msg, ops)/2 + offset, height + inc) term.write(string.sub(msg, ops)) - + return inc + 1 end @@ -1151,7 +1151,7 @@ local function displayConfirmDialogue(ctitle, msg) local dialogoffset = 8 --We actually print twice- once to get the lines, second time to print proper. Easier this way. local lines = wprintOffCenter(msg, 5, w - (dialogoffset+2) * 2, dialogoffset + 2) - + term.setCursorPos(dialogoffset, 3) term.setBackgroundColour(colours.grey) term.setTextColour(colours.lightGrey) @@ -1163,11 +1163,11 @@ local function displayConfirmDialogue(ctitle, msg) term.setCursorPos(dialogoffset, 4) term.write(string.rep(" ", w - dialogoffset * 2)) for i=5,5+lines do - term.setCursorPos(dialogoffset, i) + term.setCursorPos(dialogoffset, i) term.write(" "..string.rep(" ", w - (dialogoffset) * 2 - 2).." ") end wprintOffCenter(msg, 5, w - (dialogoffset+2) * 2, dialogoffset + 2) - + --In the event of a message, the player hits anything to continue while true do local id,key = os.pullEvent() @@ -1190,24 +1190,24 @@ local function displayDropDown(x, y, options) for i=1,#options do local currVal = options[i] if type(currVal) == "table" then currVal = currVal.name end - + longestX = math.max(longestX, #currVal) end local xOffset = math.max(0, longestX - ((w-2) - x) + 1) local yOffset = math.max(0, #options - ((h-1) - y)) - + local clickTimes = 0 local tid = nil local selection = nil while clickTimes < 2 do drawCanvas() drawInterface() - + term.setCursorPos(x-xOffset,y-yOffset) term.setBackgroundColour(colours.grey) term.setTextColour(colours.lightGrey) term.write(options.name..string.rep(" ", longestX-#options.name + 2)) - + for i=1,#options do term.setCursorPos(x-xOffset, y-yOffset+i) if i==selection and clickTimes % 2 == 0 then @@ -1218,7 +1218,7 @@ local function displayDropDown(x, y, options) term.setTextColour(colours.grey) end local currVal = options[i] - if type(currVal) == "table" then + if type(currVal) == "table" then term.write(currVal.name..string.rep(" ", longestX-#currVal.name + 1)) term.setBackgroundColour(colours.grey) term.setTextColour(colours.lightGrey) @@ -1227,18 +1227,18 @@ local function displayDropDown(x, y, options) term.write(currVal..string.rep(" ", longestX-#currVal + 2)) end end - + local id, p1, p2, p3 = os.pullEvent() if id == "timer" then - if p1 == tid then + if p1 == tid then clickTimes = clickTimes + 1 - if clickTimes > 2 then + if clickTimes > 2 then break - else - tid = os.startTimer(0.1) + else + tid = os.startTimer(0.1) end - else - updateTimer(p1) + else + updateTimer(p1) drawCanvas() drawInterface() end @@ -1252,15 +1252,15 @@ local function displayDropDown(x, y, options) end end end - + if type(selection) == "number" then selection = options[selection] end - - if type(selection) == "string" then + + if type(selection) == "string" then inDropDown = false return selection - elseif type(selection) == "table" then + elseif type(selection) == "table" then return displayDropDown(x, y, selection) end end @@ -1285,16 +1285,16 @@ local function readInput(lim) --As events queue immediately, we may get an unwanted key... this will solve that problem local inputTimer = os.startTimer(0.01) local keysAllowed = false - + while true do local id,key = os.pullEvent() - + if keysAllowed then if id == "key" and key == 14 and #inputString > 0 then inputString = string.sub(inputString, 1, #inputString-1) term.setCursorPos(ox + #inputString,oy) term.write(" ") - elseif id == "key" and key == 28 and inputString ~= string.rep(" ", #inputString) then + elseif id == "key" and key == 28 and inputString ~= string.rep(" ", #inputString) then break elseif id == "key" and key == keys.leftCtrl then return "" @@ -1302,9 +1302,9 @@ local function readInput(lim) inputString = inputString..key end end - + if id == "timer" then - if key == inputTimer then + if key == inputTimer then keysAllowed = true else updateTimer(key) @@ -1318,7 +1318,7 @@ local function readInput(lim) term.write(inputString) term.setCursorPos(ox + #inputString, oy) end - + while string.sub(inputString, 1, 1) == " " do inputString = string.sub(inputString, 2, #inputString) end @@ -1326,12 +1326,12 @@ local function readInput(lim) inputString = string.sub(inputString, 1, #inputString-1) end term.setCursorBlink(false) - + return inputString end ---[[ - Section: Image tools +--[[ + Section: Image tools ]]-- @@ -1341,13 +1341,13 @@ end ]]-- local function copyToBuffer(removeImage) buffer = { width = selectrect.x2 - selectrect.x1 + 1, height = selectrect.y2 - selectrect.y1 + 1, contents = { } } - + local containsSomething = false for y=1,buffer.height do buffer.contents[y] = { } local f,l = sFrame,sFrame if record then f,l = 1, framecount end - + for fra = f,l do if frames[fra][selectrect.y1 + y - 1] then for x=1,buffer.width do @@ -1372,7 +1372,7 @@ local function copyFromBuffer(removeBuffer) for y = 1, math.min(buffer.height,selectrect.y2-selectrect.y1+1) do local f,l = sFrame, sFrame if record then f,l = 1, framecount end - + for fra = f,l do if not frames[fra][selectrect.y1+y-1] then frames[fra][selectrect.y1+y-1] = { } end for x = 1, math.min(buffer.width,selectrect.x2-selectrect.x1+1) do @@ -1380,7 +1380,7 @@ local function copyFromBuffer(removeBuffer) end end end - + if removeBuffer then buffer = nil end end @@ -1394,7 +1394,7 @@ local function moveImage(newx,newy) if newx <=0 or newy <=0 then return end local f,l = sFrame,sFrame if record then f,l = 1,framecount end - + for i=f,l do local newlines = { } for y=toplim,botlim do @@ -1418,7 +1418,7 @@ local function moveImage(newx,newy) end end end - + newlines.textcol = { } for y=toplim,botlim do local line = frames[i].textcol[y] @@ -1430,7 +1430,7 @@ local function moveImage(newx,newy) end end end - + frames[i] = newlines end end @@ -1451,7 +1451,7 @@ local function clearImage() if string.find(string.upper(readInput(1)), "Y") then local f,l = sFrame,sFrame if record then f,l = 1,framecount end - + for i=f,l do frames[i] = { } end @@ -1470,9 +1470,9 @@ end local function floodFill(x, y, targetColour, newColour) if not newColour or not targetColour then return end local nodeList = { } - + table.insert(nodeList, {x = x, y = y}) - + while #nodeList > 0 do local node = nodeList[1] if frames[sFrame][node.y] and frames[sFrame][node.y][node.x] == targetColour then @@ -1486,8 +1486,8 @@ local function floodFill(x, y, targetColour, newColour) end end ---[[ - Section: Animation Tools +--[[ + Section: Animation Tools ]]-- --[[Enters play mode, allowing the animation to play through. Interface is restricted to allow this, @@ -1498,14 +1498,14 @@ end local function playAnimation() state = "play" selectedrect = nil - + local animt = os.startTimer(animtime) repeat drawCanvas() drawInterface() - + local id,key,_,y = os.pullEvent() - + if id=="timer" then if key == animt then animt = os.startTimer(animtime) @@ -1536,15 +1536,15 @@ local function changeFrame(newframe) term.setBackgroundColour(colours.lightGrey) term.setTextColour(colours.grey) term.clearLine() - + term.write("Go to frame: ") newframe = tonumber(readInput(2)) if not newframe or newframe <= 0 then inMenu = false - return + return end elseif newframe <= 0 then return end - + if newframe > framecount then for i=framecount+1,newframe do frames[i] = {} @@ -1570,12 +1570,12 @@ local function removeFramesAfter(frame) if frame==framecount then return end drawMessage("Remove frames "..(frame+1).."/"..framecount.."? Y/N :") local answer = string.upper(readInput(1)) - - if string.find(answer, string.upper("Y")) ~= 1 then + + if string.find(answer, string.upper("Y")) ~= 1 then inMenu = false - return + return end - + for i=frame+1, framecount do frames[i] = nil end @@ -1596,7 +1596,7 @@ end local function getLeft(curx, curz) local hand = "left" if layering == "up" then hand = "right" end - + if hand == "right" then if curx == 1 then return 0,-1 end if curx == -1 then return 0,1 end @@ -1619,7 +1619,7 @@ end local function getRight(curx, curz) local hand = "left" if layering == "up" then hand = "right" end - + if hand == "right" then if curx == 1 then return 0,1 end if curx == -1 then return 0,-1 end @@ -1647,7 +1647,7 @@ local function locatePrinters() drawCanvas() drawInterface() state = oldState - + local modemOpened = false for k,v in pairs(rs.getSides()) do if peripheral.isPresent(v) and peripheral.getType(v) == "modem" then @@ -1656,17 +1656,17 @@ local function locatePrinters() break end end - + if not modemOpened then displayConfirmDialogue("Modem not found!", "No modem peripheral. Must have network modem to locate printers.") return false end - + rednet.broadcast("$3DPRINT IDENTIFY") - + while true do local id, msg = rsTimeReceive(1) - + if not id then break end if string.find(msg, "$3DPRINT IDACK") == 1 then msg = string.gsub(msg, "$3DPRINT IDACK ", "") @@ -1674,7 +1674,7 @@ local function locatePrinters() table.insert(printerNames, msg) end end - + if #printerList == 0 then displayConfirmDialogue("Printers not found!", "No active printers found in proximity of this computer.") return false @@ -1692,7 +1692,7 @@ local function sendPC(command,param) local msg = "$PC "..command if param then msg = msg.." "..param end rednet.send(printerList[selectedPrinter], msg) - + while true do local id,key = rsTimeReceive() if id == printerList[selectedPrinter] then @@ -1713,7 +1713,7 @@ local function sendPC(command,param) end end end - + --Changes to position are handled after the event has been successfully completed if command == "FW" then px = px + pfx @@ -1724,13 +1724,13 @@ local function sendPC(command,param) elseif command == "UP" then if layering == "up" then py = py + 1 - else + else py = py - 1 end elseif command == "DW" then if layering == "up" then py = py - 1 - else + else py = py + 1 end elseif command == "TL" then @@ -1741,7 +1741,7 @@ local function sendPC(command,param) pfx = -pfx pfz = -pfz end - + drawCanvas() drawInterface() end @@ -1788,7 +1788,7 @@ local function performPrint() --An up layering starts our builder bot on the bottom left corner of our build px,py,pz = leflim, 0, botlim + 1 pfx,pfz = 0,-1 - + --We move him forward and up a bit from his original position. sendPC("FW") sendPC("UP") @@ -1801,7 +1801,7 @@ local function performPrint() else rowbot,rowtop,rowinc = toplim,botlim,1 end - + for rows = rowbot,rowtop,rowinc do --Then we decide if we're going left or right, depending on what side we're on local linebot,linetop,lineinc = nil,nil,nil @@ -1814,7 +1814,7 @@ local function performPrint() turnToFace(-1,0) linebot,linetop,lineinc = riglim,leflim,-1 end - + for lines = linebot,linetop,lineinc do --We move our turtle forward, placing the right material at each step local material = frames[py][pz][px] @@ -1827,7 +1827,7 @@ local function performPrint() sendPC("FW") end end - + --The printer then has to do a U-turn, depending on which way he's facing and --which way he needs to go local temppfx,temppfz = getLeft(pfx,pfz) @@ -1873,7 +1873,7 @@ local function performPrint() while pz < #frames do sendPC("FW") end - + --For each layer in the frame we build our wall, the move back for layers = 1,#frames do --We first decide if we're going left or right based on our position @@ -1883,7 +1883,7 @@ local function performPrint() else rowbot,rowtop,rowinc = riglim,leflim,-1 end - + for rows = rowbot,rowtop,rowinc do --Then we decide if we're going up or down, depending on our given altitude local linebot,linetop,lineinc = nil,nil,nil @@ -1892,7 +1892,7 @@ local function performPrint() else linebot,linetop,lineinc = toplim,botlim,1 end - + for lines = linebot,linetop,lineinc do --We move our turtle up/down, placing the right material at each step local material = frames[pz][py][px] @@ -1906,14 +1906,14 @@ local function performPrint() else sendPC("UP") end end end - + if rows ~= rowtop then turnToFace(rowinc,0) sendPC("FW") turnToFace(0,1) end end - + if layers ~= #frames then sendPC("TU") sendPC("FW") @@ -1927,14 +1927,14 @@ local function performPrint() end turnToFace(0,1) end - + sendPC("DE") - + displayConfirmDialogue("Print complete", "The 3D print was successful.") end ---[[ - Section: Interface +--[[ + Section: Interface ]]-- --[[Runs the printing interface. Allows users to find/select a printer, the style of printing to perform and to begin the operation @@ -1953,7 +1953,7 @@ local function runPrintInterface() if not locatePrinters() then return false end - + layering = "up" requirementsDisplayed = false selectedPrinter = 1 @@ -1967,7 +1967,7 @@ local function runPrintInterface() drawInterface() term.setBackgroundColour(colours.lightGrey) term.setTextColour(colours.black) - + local msg = "3D Printing" term.setCursorPos(w/2-#msg/2 - 2, 1) term.write(msg) @@ -1982,7 +1982,7 @@ local function runPrintInterface() term.write(msg) term.setBackgroundColour(colours.lightGrey) term.setTextColour(colours.black) - + term.setCursorPos(7, 2) term.write("Layering") drawPictureTable(layerUpIcon, 3, 3, colours.white) @@ -2001,7 +2001,7 @@ local function runPrintInterface() end term.setCursorPos(12, 9) term.write("Forward") - + term.setBackgroundColour(colours.lightGrey) term.setTextColour(colours.black) term.setCursorPos(31, 2) @@ -2014,16 +2014,16 @@ local function runPrintInterface() term.setTextColour(colours.red) end term.write(" "..printerNames[selectedPrinter].." ") - + term.setBackgroundColour(colours.grey) term.setTextColour(colours.lightGrey) term.setCursorPos(25, 10) term.write(" Cancel ") term.setCursorPos(40, 10) term.write(" Print ") - + local id, p1, p2, p3 = os.pullEvent() - + if id == "timer" then updateTimer(p1) elseif id == "mouse_click" then @@ -2052,7 +2052,7 @@ local function runPrintInterface() ready = false while true do local id,msg = rsTimeReceive(10) - + if id == printerList[selectedPrinter] and msg == "$3DPRINT ACTACK" then ready = true break @@ -2077,100 +2077,100 @@ end ]]-- local function performSelection(mode) if not mode or mode == "" then return - + elseif mode == "help" then drawHelpScreen() - + elseif mode == "blueprint on" then blueprint = true ddModes[2][3] = "blueprint off" - + elseif mode == "blueprint off" then blueprint = false ddModes[2][3] = "blueprint on" - + elseif mode == "layers on" then layerDisplay = true ddModes[2][4] = "layers off" - + elseif mode == "layers off" then layerDisplay = false ddModes[2][4] = "layers on" - + elseif mode == "direction on" then printDirection = true ddModes[2][5] = "direction off" - + elseif mode == "direction off" then printDirection = false ddModes[2][5] = "direction on" - + elseif mode == "go to" then changeFrame() - + elseif mode == "remove" then removeFramesAfter(sFrame) - + elseif mode == "play" then playAnimation() - + elseif mode == "copy" then if selectrect and selectrect.x1 ~= selectrect.x2 then copyToBuffer(false) end - + elseif mode == "cut" then - if selectrect and selectrect.x1 ~= selectrect.x2 then + if selectrect and selectrect.x1 ~= selectrect.x2 then copyToBuffer(true) end - + elseif mode == "paste" then - if selectrect and selectrect.x1 ~= selectrect.x2 then + if selectrect and selectrect.x1 ~= selectrect.x2 then copyFromBuffer(false) end - + elseif mode == "hide" then selectrect = nil if state == "select" then state = "corner select" end - + elseif mode == "alpha to left" then if lSel then alphaC = lSel end - + elseif mode == "alpha to right" then if rSel then alphaC = rSel end - + elseif mode == "record" then record = not record - + elseif mode == "clear" then if state=="select" then buffer = nil else clearImage() end - + elseif mode == "select" then if state=="corner select" or state=="select" then state = "paint" elseif selectrect and selectrect.x1 ~= selectrect.x2 then state = "select" else - state = "corner select" + state = "corner select" end - + elseif mode == "print" then state = "print" runPrintInterface() state = "paint" - + elseif mode == "save" then if animated then saveNFA(sPath) elseif textEnabled then saveNFT(sPath) else saveNFP(sPath) end - + elseif mode == "exit" then isRunning = false - + elseif mode ~= state then state = mode else state = "paint" - + end end @@ -2184,12 +2184,12 @@ local function handleEvents() while isRunning do drawCanvas() drawInterface() - + if state == "text" then term.setCursorPos(textCurX - sx, textCurY - sy) term.setCursorBlink(true) end - + local id,p1,p2,p3 = os.pullEvent() term.setCursorBlink(false) if id=="timer" then @@ -2212,20 +2212,20 @@ local function handleEvents() if state=="pippette" then if p1==1 then if frames[sFrame][p3+sy] and frames[sFrame][p3+sy][p2+sx] then - lSel = frames[sFrame][p3+sy][p2+sx] + lSel = frames[sFrame][p3+sy][p2+sx] end elseif p1==2 then if frames[sFrame][p3+sy] and frames[sFrame][p3+sy][p2+sx] then - rSel = frames[sFrame][p3+sy][p2+sx] + rSel = frames[sFrame][p3+sy][p2+sx] end end elseif state=="move" then updateImageLims(record) moveImage(p2,p3) elseif state=="flood" then - if p1 == 1 and lSel and frames[sFrame][p3+sy] then + if p1 == 1 and lSel and frames[sFrame][p3+sy] then floodFill(p2,p3,frames[sFrame][p3+sy][p2+sx],lSel) - elseif p1 == 2 and rSel and frames[sFrame][p3+sy] then + elseif p1 == 2 and rSel and frames[sFrame][p3+sy] then floodFill(p2,p3,frames[sFrame][p3+sy][p2+sx],rSel) end elseif state=="corner select" then @@ -2234,10 +2234,10 @@ local function handleEvents() elseif selectrect.x1 ~= p2+sx and selectrect.y1 ~= p3+sy then if p2+sx w + sx - 2 then sx = textCurX - w + 2 end elseif tonumber(p1) then @@ -2335,16 +2335,16 @@ local function handleEvents() textCurY = textCurY+1 if textCurY > h + sy - 1 then sy = textCurY - h + 1 end end - + elseif p1==keys.leftCtrl then - local sel = displayDropDown(1, h-1, ddModes[#ddModes]) + local sel = displayDropDown(1, h-1, ddModes[#ddModes]) performSelection(sel) elseif p1==keys.leftAlt then - local sel = displayDropDown(1, h-1, ddModes[1]) + local sel = displayDropDown(1, h-1, ddModes[1]) performSelection(sel) - elseif p1==keys.h then + elseif p1==keys.h then performSelection("help") - elseif p1==keys.x then + elseif p1==keys.x then performSelection("cut") elseif p1==keys.c then performSelection("copy") @@ -2415,7 +2415,7 @@ local function handleEvents() selectrect.y1 = selectrect.y1-1 selectrect.y2 = selectrect.y2-1 elseif sy > 0 then sy=sy-1 end - elseif p1==keys.down then + elseif p1==keys.down then if state == "move" then updateImageLims(record) if toplim and leflim then @@ -2431,7 +2431,7 @@ local function handleEvents() end --[[ - Section: Main + Section: Main ]]-- if not term.isColour() then @@ -2475,15 +2475,15 @@ if fs.exists(sPath) then print("Can only edit .nfp, .nft and .nfa files:",string.find(sPath, ".nfp"),#sPath-3) return end - + if string.find(sPath, ".nfa") == #sPath-3 then animated = true end - + if string.find(sPath, ".nft") == #sPath-3 then textEnabled = true - end - + end + if string.find(sPath, ".nfp") == #sPath-3 and animated then print("Convert to nfa? Y/N") if string.find(string.lower(io.read()), "y") then @@ -2494,24 +2494,24 @@ if fs.exists(sPath) then animated = false end end - + --Again this is possible, I just haven't done it. Maybe I will? if textEnabled and (string.find(sPath, ".nfp") == #sPath-3 or string.find(sPath, ".nfa") == #sPath-3) then print("Cannot convert to nft") end else - if not animated and not textEnabled and string.find(sPath, ".nfp") ~= #sPath-3 then + if not animated and not textEnabled and string.find(sPath, ".nfp") ~= #sPath-3 then sPath = sPath..".nfp" - elseif animated and string.find(sPath, ".nfa") ~= #sPath-3 then + elseif animated and string.find(sPath, ".nfa") ~= #sPath-3 then sPath = sPath..".nfa" elseif textEnabled and string.find(sPath, ".nft") ~= #sPath-3 then sPath = sPath..".nft" end -end +end drawLogo() init() handleEvents() term.setBackgroundColour(colours.black) -shell.run("clear") \ No newline at end of file +shell.run("clear") diff --git a/src/main/resources/assets/computercraft/lua/treasure/vilsol/gameoflife/gameoflife.lua b/src/main/resources/assets/computercraft/lua/treasure/vilsol/gameoflife/gameoflife.lua index bcd57c718..2a1a447a4 100644 --- a/src/main/resources/assets/computercraft/lua/treasure/vilsol/gameoflife/gameoflife.lua +++ b/src/main/resources/assets/computercraft/lua/treasure/vilsol/gameoflife/gameoflife.lua @@ -175,4 +175,4 @@ drawScreen() while true do loop() parallel.waitForAny(loop, compute) -end \ No newline at end of file +end diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua index d1b0ad599..ac64ad565 100644 --- a/src/test/resources/test-rom/mcfly.lua +++ b/src/test/resources/test-rom/mcfly.lua @@ -13,7 +13,7 @@ -- -- @tparam string func The function's name -- @tparam int idx The argument index to this function --- @tparam string ty The type this argument should have. May be 'value' for +-- @tparam string ty The type this argument should have. May be 'value' for -- any non-nil value. -- @param val val The value to check -- @throws If this value doesn't match the expected type. diff --git a/src/test/resources/test-rom/spec/programs/delete_spec.lua b/src/test/resources/test-rom/spec/programs/delete_spec.lua index 5036f7eae..1eec95cc4 100644 --- a/src/test/resources/test-rom/spec/programs/delete_spec.lua +++ b/src/test/resources/test-rom/spec/programs/delete_spec.lua @@ -39,7 +39,7 @@ describe("The rm program", function() expect(capture(stub, "rm")) :matches { ok = true, output = "Usage: rm \n", error = "" } end) - + it("errors when trying to delete a read-only file", function() expect(capture(stub, "rm /rom/startup.lua")) :matches { ok = true, output = "", error = "/rom/startup.lua: Access denied\n" } diff --git a/src/test/resources/test-rom/spec/programs/edit_spec.lua b/src/test/resources/test-rom/spec/programs/edit_spec.lua index 7ca0b04f2..e58938e23 100644 --- a/src/test/resources/test-rom/spec/programs/edit_spec.lua +++ b/src/test/resources/test-rom/spec/programs/edit_spec.lua @@ -2,7 +2,7 @@ local capture = require "test_helpers".capture_program describe("The edit program", function() - it("displays its usage when given no argument", function() + it("displays its usage when given no argument", function() expect(capture(stub, "edit")) :matches { ok = true, output = "Usage: edit \n", error = "" } end) diff --git a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua index 5b13d59a9..831d88fc1 100644 --- a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua +++ b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua @@ -51,7 +51,7 @@ describe("The pastebin program", function() local file = fs.open( "testup", "w" ) file.close() - + expect(capture(stub, "pastebin", "put", "testup" )) :matches { ok = true, output = "Connecting to pastebin.com... Success.\nUploaded as https://pastebin.com/abcde\nRun \"pastebin get abcde\" to download anywhere\n", error = "" } end) diff --git a/src/test/resources/test-rom/spec/programs/id_spec.lua b/src/test/resources/test-rom/spec/programs/id_spec.lua index 73206e7fb..7c5d47da7 100644 --- a/src/test/resources/test-rom/spec/programs/id_spec.lua +++ b/src/test/resources/test-rom/spec/programs/id_spec.lua @@ -1,7 +1,7 @@ local capture = require "test_helpers".capture_program describe("The id program", function() - + it("displays computer id", function() local id = os.getComputerID() diff --git a/src/test/resources/test-rom/spec/programs/motd_spec.lua b/src/test/resources/test-rom/spec/programs/motd_spec.lua index 3781e2e16..72becbf30 100644 --- a/src/test/resources/test-rom/spec/programs/motd_spec.lua +++ b/src/test/resources/test-rom/spec/programs/motd_spec.lua @@ -7,7 +7,7 @@ describe("The motd program", function() file.write("Hello World!") file.close() settings.set("motd.path", "/modt_check.txt") - + expect(capture(stub, "motd")) :matches { ok = true, output = "Hello World!\n", error = "" } end) diff --git a/src/test/resources/test-rom/spec/programs/set_spec.lua b/src/test/resources/test-rom/spec/programs/set_spec.lua index 64dba1447..46cf0b84d 100644 --- a/src/test/resources/test-rom/spec/programs/set_spec.lua +++ b/src/test/resources/test-rom/spec/programs/set_spec.lua @@ -6,7 +6,7 @@ describe("The set program", function() settings.clear() settings.set("Test", "Hello World!") settings.set("123", 456) - + expect(capture(stub, "set")) :matches { ok = true, output = '"123" is 456\n"Test" is "Hello World!"\n', error = "" } end) @@ -15,12 +15,12 @@ describe("The set program", function() settings.clear() settings.set("Test", "Hello World!") settings.set("123", 456) - + expect(capture(stub, "set Test")) :matches { ok = true, output = '"Test" is "Hello World!"\n', error = "" } end) - it("set a setting", function() + it("set a setting", function() expect(capture(stub, "set Test Hello")) :matches { ok = true, output = '"Test" set to "Hello"\n', error = "" } diff --git a/src/test/resources/test-rom/spec/programs/time_spec.lua b/src/test/resources/test-rom/spec/programs/time_spec.lua index e9c1eff31..91f82fef0 100644 --- a/src/test/resources/test-rom/spec/programs/time_spec.lua +++ b/src/test/resources/test-rom/spec/programs/time_spec.lua @@ -5,7 +5,7 @@ describe("The time program", function() it("displays time", function() local time = textutils.formatTime(os.time()) local day = os.day() - + expect(capture(stub, "time")) :matches { ok = true, output = "The time is " .. time .. " on Day " .. day .. "\n", error = "" } end) diff --git a/tools/check-lines.py b/tools/check-lines.py new file mode 100644 index 000000000..e1fcba620 --- /dev/null +++ b/tools/check-lines.py @@ -0,0 +1,28 @@ +import pathlib, sys + +problems = False + +# Skip images and files without extensions +exclude = [ ".png", "" ] + +for path in pathlib.Path(".").glob("src/**/*"): + if path.is_dir() or path.suffix in exclude: + continue + + with path.open(encoding="utf-8") as file: + has_dos, has_trailing, needs_final = False, False, False + for i, line in enumerate(file): + if len(line) >= 2 and line[-2] == "\r" and line[-1] == "\n" and not has_line: + print("%s has contains '\\r\\n' on line %d" % (path, i + 1)) + problems = has_dos = True + + if len(line) >= 2 and line[-2] in " \t" and line[-1] == "\n" and not has_trailing: + print("%s has trailing whitespace on line %d" % (path, i + 1)) + problems = has_trailing = True + + if line is not None and len(line) >= 1 and line[-1] != "\n": + print("%s should end with '\\n'" % path) + problems = True + +if problems: + sys.exit(1) From bf13bac1527a696eca90c1599a59920e66103c21 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 4 Feb 2020 16:41:29 +0000 Subject: [PATCH 06/35] Update illuaminate config --- illuaminate.sexp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/illuaminate.sexp b/illuaminate.sexp index d6865618c..cda3d0a85 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -13,7 +13,12 @@ ;; It's useful to name arguments for documentation, so we allow this. It'd ;; be good to find a compromise in the future, but this works for now. - -var:unused-arg)) + -var:unused-arg + + ;; Suppress a couple of documentation comments warnings for now. We'll + ;; hopefully be able to remove them in the coming weeks. + -doc:detached-comment -doc:undocumented -doc:undocumented-arg + -doc:unresolved-reference)) ;; We disable the unused global linter in bios.lua and the APIs. In the future ;; hopefully we'll get illuaminate to handle this. From 930fd59298748d283b1200ed682b275db2e695dc Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 4 Feb 2020 16:53:22 +0000 Subject: [PATCH 07/35] Remove dvs1 as a maven repository This thing consistently goes down and breaks the build. For now, let's just rehost it on squiddev.cc/maven. --- build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle b/build.gradle index 3ada5933e..f2fa11e96 100644 --- a/build.gradle +++ b/build.gradle @@ -43,10 +43,6 @@ minecraft { } repositories { - maven { - name "JEI" - url "https://dvs1.progwml6.com/files/maven" - } maven { name "SquidDev" url "https://squiddev.cc/maven" From 8eae02c03735a324afc67e955d930b6a43bcfe69 Mon Sep 17 00:00:00 2001 From: Fatboychummy <39929480+fatboychummy@users.noreply.github.com> Date: Tue, 4 Feb 2020 10:00:49 -0700 Subject: [PATCH 08/35] Fire mouse_up events in monitor.lua (#358) We schedule a mouse_up event 0.1 seconds after receiving a monitor_touch event. --- .../assets/computercraft/lua/rom/programs/monitor.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua b/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua index 16afddd29..8860d8bde 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua @@ -39,6 +39,8 @@ local function resume( ... ) return param end +local timers = {} + local ok, param = pcall( function() local sFilter = resume() while coroutine.status( co ) ~= "dead" do @@ -48,6 +50,7 @@ local ok, param = pcall( function() end if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then if tEvent[1] == "monitor_touch" and tEvent[2] == sName then + timers[os.startTimer( 0.1 )] = { tEvent[3], tEvent[4] } sFilter = resume( "mouse_click", 1, table.unpack( tEvent, 3, tEvent.n ) ) end end @@ -56,6 +59,12 @@ local ok, param = pcall( function() sFilter = resume( "term_resize" ) end end + if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then + if tEvent[1] == "timer" and timers[tEvent[2]] then + sFilter = resume( "mouse_up", 1, table.unpack( timers[tEvent[2]], 1, 2 ) ) + timers[tEvent[2]] = nil + end + end end end ) From be89fc25f9dbb80206b4d5aab4f8ea44e22d81ab Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 7 Feb 2020 14:17:09 +0000 Subject: [PATCH 09/35] Prevent copying folders inside themselves - contains now performs a case-insensitive comparison. While this is a little dubious, it's required for systems like Windows, where foo and Foo are the same folder. - Impose a depth limit on copyRecursive. If there are any other cases where we may try to copy a folder into itself, this should prevent the computer entirely crashing. --- .../filesystem/FileOperationException.java | 2 +- .../core/filesystem/FileSystem.java | 25 ++++++++++++------- .../core/computer/BasicEnvironment.java | 2 +- .../resources/test-rom/spec/apis/fs_spec.lua | 15 +++++++++++ 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/main/java/dan200/computercraft/api/filesystem/FileOperationException.java b/src/main/java/dan200/computercraft/api/filesystem/FileOperationException.java index 703a95911..70a25fe5f 100644 --- a/src/main/java/dan200/computercraft/api/filesystem/FileOperationException.java +++ b/src/main/java/dan200/computercraft/api/filesystem/FileOperationException.java @@ -27,7 +27,7 @@ public class FileOperationException extends IOException this.filename = filename; } - public FileOperationException( String message ) + public FileOperationException( @Nonnull String message ) { super( Objects.requireNonNull( message, "message cannot be null" ) ); this.filename = null; diff --git a/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java b/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java index 4f6f84cd3..eda0361bd 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java +++ b/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java @@ -29,6 +29,14 @@ import java.util.regex.Pattern; public class FileSystem { + /** + * Maximum depth that {@link #copyRecursive(String, MountWrapper, String, MountWrapper, int)} will descend into. + * + * This is a pretty arbitrary value, though hopefully it is large enough that it'll never be normally hit. This + * exists to prevent it overflowing if it ever gets into an infinite loop. + */ + private static final int MAX_COPY_DEPTH = 128; + private static class MountWrapper { private String m_label; @@ -611,15 +619,13 @@ public class FileSystem { throw new FileSystemException( "/" + sourcePath + ": Can't copy a directory inside itself" ); } - copyRecursive( sourcePath, getMount( sourcePath ), destPath, getMount( destPath ) ); + copyRecursive( sourcePath, getMount( sourcePath ), destPath, getMount( destPath ), 0 ); } - private synchronized void copyRecursive( String sourcePath, MountWrapper sourceMount, String destinationPath, MountWrapper destinationMount ) throws FileSystemException + private synchronized void copyRecursive( String sourcePath, MountWrapper sourceMount, String destinationPath, MountWrapper destinationMount, int depth ) throws FileSystemException { - if( !sourceMount.exists( sourcePath ) ) - { - return; - } + if( !sourceMount.exists( sourcePath ) ) return; + if( depth >= MAX_COPY_DEPTH ) throw new FileSystemException( "Too many directories to copy" ); if( sourceMount.isDirectory( sourcePath ) ) { @@ -634,7 +640,8 @@ public class FileSystem { copyRecursive( combine( sourcePath, child ), sourceMount, - combine( destinationPath, child ), destinationMount + combine( destinationPath, child ), destinationMount, + depth + 1 ); } } @@ -854,8 +861,8 @@ public class FileSystem public static boolean contains( String pathA, String pathB ) { - pathA = sanitizePath( pathA ); - pathB = sanitizePath( pathB ); + pathA = sanitizePath( pathA ).toLowerCase( Locale.ROOT ); + pathB = sanitizePath( pathB ).toLowerCase( Locale.ROOT ); if( pathB.equals( ".." ) ) { diff --git a/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java b/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java index cc6cebd3f..f3caa0856 100644 --- a/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java +++ b/src/test/java/dan200/computercraft/core/computer/BasicEnvironment.java @@ -116,7 +116,7 @@ public class BasicEnvironment implements IComputerEnvironment while( baseFile != null && !wholeFile.exists() ) { baseFile = baseFile.getParentFile(); - wholeFile = new File( baseFile, "resources/" + fallback + "/" + path ); + wholeFile = new File( baseFile, "src/" + fallback + "/resources/" + path ); } if( !wholeFile.exists() ) throw new IllegalStateException( "Cannot find ROM mount at " + file ); diff --git a/src/test/resources/test-rom/spec/apis/fs_spec.lua b/src/test/resources/test-rom/spec/apis/fs_spec.lua index ad1bfb41c..f6ccac6bc 100644 --- a/src/test/resources/test-rom/spec/apis/fs_spec.lua +++ b/src/test/resources/test-rom/spec/apis/fs_spec.lua @@ -125,6 +125,21 @@ describe("The fs library", function() it("fails on read-only mounts", function() expect.error(fs.copy, "rom", "rom/startup"):eq("/rom/startup: Access denied") end) + + it("fails to copy a folder inside itself", function() + fs.makeDir("some-folder") + expect.error(fs.copy, "some-folder", "some-folder/x"):eq("/some-folder: Can't copy a directory inside itself") + expect.error(fs.copy, "some-folder", "Some-Folder/x"):eq("/some-folder: Can't copy a directory inside itself") + end) + + it("copies folders", function() + fs.delete("some-folder") + fs.delete("another-folder") + + fs.makeDir("some-folder") + fs.copy("some-folder", "another-folder") + expect(fs.isDir("another-folder")):eq(true) + end) end) describe("fs.move", function() From 79f42e35ce8b60f16b77082f6dab63ecf365f188 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 7 Feb 2020 14:50:51 +0000 Subject: [PATCH 10/35] Add a timeout to websocket.receive - Move timer handling to IAPIEnvironment, rather than OSAPI. This means the environment is reset on startup/shutdown, much like normal APIs. - Websocket.receive now accepts an optional timetout (much like rednet.receive). This starts a timer, and waits for it to complete. Closes #201 --- .../core/apis/IAPIEnvironment.java | 6 ++ .../dan200/computercraft/core/apis/OSAPI.java | 70 +++--------------- .../apis/http/websocket/WebsocketHandle.java | 24 +++++++ .../computercraft/core/computer/Computer.java | 2 +- .../core/computer/ComputerExecutor.java | 2 + .../core/computer/Environment.java | 72 ++++++++++++++++++- 6 files changed, 111 insertions(+), 65 deletions(-) diff --git a/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java b/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java index 00b5ebf8d..0a4315960 100644 --- a/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java +++ b/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java @@ -18,6 +18,8 @@ import javax.annotation.Nullable; public interface IAPIEnvironment { + String TIMER_EVENT = "timer"; + @FunctionalInterface interface IPeripheralChangeListener { @@ -64,6 +66,10 @@ public interface IAPIEnvironment void setLabel( @Nullable String label ); + int startTimer( long ticks ); + + void cancelTimer( int id ); + void addTrackingChange( @Nonnull TrackingField field, long change ); default void addTrackingChange( @Nonnull TrackingField field ) diff --git a/src/main/java/dan200/computercraft/core/apis/OSAPI.java b/src/main/java/dan200/computercraft/core/apis/OSAPI.java index e1bc5c1ab..71577a127 100644 --- a/src/main/java/dan200/computercraft/core/apis/OSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/OSAPI.java @@ -9,6 +9,8 @@ import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.shared.util.StringUtil; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import javax.annotation.Nonnull; import java.time.Instant; @@ -24,24 +26,12 @@ public class OSAPI implements ILuaAPI { private IAPIEnvironment m_apiEnvironment; - private final Map m_timers; - private final Map m_alarms; + private final Int2ObjectMap m_alarms = new Int2ObjectOpenHashMap<>(); private int m_clock; private double m_time; private int m_day; - private int m_nextTimerToken; - private int m_nextAlarmToken; - - private static class Timer - { - int m_ticksLeft; - - Timer( int ticksLeft ) - { - m_ticksLeft = ticksLeft; - } - } + private int m_nextAlarmToken = 0; private static class Alarm implements Comparable { @@ -66,10 +56,6 @@ public class OSAPI implements ILuaAPI public OSAPI( IAPIEnvironment environment ) { m_apiEnvironment = environment; - m_nextTimerToken = 0; - m_nextAlarmToken = 0; - m_timers = new HashMap<>(); - m_alarms = new HashMap<>(); } // ILuaAPI implementation @@ -87,11 +73,6 @@ public class OSAPI implements ILuaAPI m_day = m_apiEnvironment.getComputerEnvironment().getDay(); m_clock = 0; - synchronized( m_timers ) - { - m_timers.clear(); - } - synchronized( m_alarms ) { m_alarms.clear(); @@ -101,26 +82,7 @@ public class OSAPI implements ILuaAPI @Override public void update() { - synchronized( m_timers ) - { - // Update the clock - m_clock++; - - // Countdown all of our active timers - Iterator> it = m_timers.entrySet().iterator(); - while( it.hasNext() ) - { - Map.Entry entry = it.next(); - Timer timer = entry.getValue(); - timer.m_ticksLeft--; - if( timer.m_ticksLeft <= 0 ) - { - // Queue the "timer" event - queueLuaEvent( "timer", new Object[] { entry.getKey() } ); - it.remove(); - } - } - } + m_clock++; // Wait for all of our alarms synchronized( m_alarms ) @@ -155,11 +117,6 @@ public class OSAPI implements ILuaAPI @Override public void shutdown() { - synchronized( m_timers ) - { - m_timers.clear(); - } - synchronized( m_alarms ) { m_alarms.clear(); @@ -229,11 +186,8 @@ public class OSAPI implements ILuaAPI { // startTimer double timer = getFiniteDouble( args, 0 ); - synchronized( m_timers ) - { - m_timers.put( m_nextTimerToken, new Timer( (int) Math.round( timer / 0.05 ) ) ); - return new Object[] { m_nextTimerToken++ }; - } + int id = m_apiEnvironment.startTimer( Math.round( timer / 0.05 ) ); + return new Object[] { id }; } case 2: { @@ -278,10 +232,7 @@ public class OSAPI implements ILuaAPI return null; } case 10: // clock - synchronized( m_timers ) - { - return new Object[] { m_clock * 0.05 }; - } + return new Object[] { m_clock * 0.05 }; case 11: { // time @@ -345,10 +296,7 @@ public class OSAPI implements ILuaAPI { // cancelTimer int token = getInt( args, 0 ); - synchronized( m_timers ) - { - m_timers.remove( token ); - } + m_apiEnvironment.cancelTimer( token ); return null; } case 14: diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java index 976b526c2..8433d30c3 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java @@ -7,6 +7,7 @@ package dan200.computercraft.core.apis.http.websocket; import com.google.common.base.Objects; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.lua.ArgumentHelper; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaObject; import dan200.computercraft.api.lua.LuaException; @@ -23,6 +24,7 @@ import java.io.Closeable; import java.util.Arrays; import static dan200.computercraft.api.lua.ArgumentHelper.optBoolean; +import static dan200.computercraft.core.apis.IAPIEnvironment.TIMER_EVENT; import static dan200.computercraft.core.apis.http.websocket.Websocket.CLOSE_EVENT; import static dan200.computercraft.core.apis.http.websocket.Websocket.MESSAGE_EVENT; @@ -53,7 +55,21 @@ public class WebsocketHandle implements ILuaObject, Closeable switch( method ) { case 0: // receive + { checkOpen(); + int timeoutId; + if( arguments.length <= 0 || arguments[0] == null ) + { + // We do this rather odd argument validation to ensure we can tell the difference between a + // negative timeout and an absent one. + timeoutId = -1; + } + else + { + double timeout = ArgumentHelper.getFiniteDouble( arguments, 0 ); + timeoutId = websocket.environment().startTimer( Math.round( timeout / 0.05 ) ); + } + while( true ) { Object[] event = context.pullEvent( null ); @@ -63,9 +79,17 @@ public class WebsocketHandle implements ILuaObject, Closeable } else if( event.length >= 2 && Objects.equal( event[0], CLOSE_EVENT ) && Objects.equal( event[1], websocket.address() ) && closed ) { + // If the socket is closed abort. + return null; + } + else if( event.length >= 2 && timeoutId != -1 && Objects.equal( event[0], TIMER_EVENT ) + && event[1] instanceof Number && ((Number) event[1]).intValue() == timeoutId ) + { + // If we received a matching timer event then abort. return null; } } + } case 1: // send { diff --git a/src/main/java/dan200/computercraft/core/computer/Computer.java b/src/main/java/dan200/computercraft/core/computer/Computer.java index cacb755d1..316917535 100644 --- a/src/main/java/dan200/computercraft/core/computer/Computer.java +++ b/src/main/java/dan200/computercraft/core/computer/Computer.java @@ -183,7 +183,7 @@ public class Computer executor.tick(); // Update the environment's internal state. - internalEnvironment.update(); + internalEnvironment.tick(); // Propagate the environment's output to the world. if( internalEnvironment.updateOutput() ) externalOutputChanged.set( true ); diff --git a/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java b/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java index 0da3b2d65..a50b390e4 100644 --- a/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java +++ b/src/main/java/dan200/computercraft/core/computer/ComputerExecutor.java @@ -435,6 +435,7 @@ final class ComputerExecutor } // Init APIs + computer.getEnvironment().reset(); for( ILuaAPI api : apis ) api.startup(); // Init lua @@ -478,6 +479,7 @@ final class ComputerExecutor // Shutdown our APIs for( ILuaAPI api : apis ) api.shutdown(); + computer.getEnvironment().reset(); // Unload filesystem if( fileSystem != null ) diff --git a/src/main/java/dan200/computercraft/core/computer/Environment.java b/src/main/java/dan200/computercraft/core/computer/Environment.java index 35a79ee4d..819997c92 100644 --- a/src/main/java/dan200/computercraft/core/computer/Environment.java +++ b/src/main/java/dan200/computercraft/core/computer/Environment.java @@ -5,6 +5,7 @@ */ package dan200.computercraft.core.computer; +import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IWorkMonitor; import dan200.computercraft.core.apis.IAPIEnvironment; @@ -12,9 +13,12 @@ import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.core.tracking.TrackingField; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import javax.annotation.Nonnull; import java.util.Arrays; +import java.util.Iterator; /** * Represents the "environment" that a {@link Computer} exists in. @@ -53,6 +57,9 @@ public final class Environment implements IAPIEnvironment private final IPeripheral[] peripherals = new IPeripheral[ComputerSide.COUNT]; private IPeripheralChangeListener peripheralListener = null; + private final Int2ObjectMap timers = new Int2ObjectOpenHashMap<>(); + private int nextTimerToken = 0; + Environment( Computer computer ) { this.computer = computer; @@ -198,17 +205,47 @@ public final class Environment implements IAPIEnvironment } /** - * Called on the main thread to update the internal state of the computer. + * Called when the computer starts up or shuts down, to reset any internal state. * - * This just queues a {@code redstone} event if the input has changed. + * @see ILuaAPI#startup() + * @see ILuaAPI#shutdown() */ - void update() + void reset() + { + synchronized( timers ) + { + timers.clear(); + } + } + + /** + * Called on the main thread to update the internal state of the computer. + */ + void tick() { if( inputChanged ) { inputChanged = false; queueEvent( "redstone", null ); } + + synchronized( timers ) + { + // Countdown all of our active timers + Iterator> it = timers.int2ObjectEntrySet().iterator(); + while( it.hasNext() ) + { + Int2ObjectMap.Entry entry = it.next(); + Timer timer = entry.getValue(); + timer.ticksLeft--; + if( timer.ticksLeft <= 0 ) + { + // Queue the "timer" event + queueEvent( TIMER_EVENT, new Object[] { entry.getIntKey() } ); + it.remove(); + } + } + } } /** @@ -303,9 +340,38 @@ public final class Environment implements IAPIEnvironment computer.setLabel( label ); } + @Override + public int startTimer( long ticks ) + { + synchronized( timers ) + { + timers.put( nextTimerToken, new Timer( ticks ) ); + return nextTimerToken++; + } + } + + @Override + public void cancelTimer( int id ) + { + synchronized( timers ) + { + timers.remove( id ); + } + } + @Override public void addTrackingChange( @Nonnull TrackingField field, long change ) { Tracking.addValue( computer, field, change ); } + + private static class Timer + { + long ticksLeft; + + Timer( long ticksLeft ) + { + this.ticksLeft = ticksLeft; + } + } } From 239bd769df2bb5b29b97ccccc512f761e08ad6b4 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 8 Feb 2020 11:17:45 +0000 Subject: [PATCH 11/35] Cache the gradle dependency cache This'll preserve the FG and general module cache. Note, this doesn't include gradle wrappers, but that's normally pretty fast to download. --- .github/workflows/main-ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index d499cd33f..924abb37a 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -15,6 +15,14 @@ jobs: with: java-version: 1.8 + - name: Cache gradle dependencies + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('gradle.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Build with Gradle run: ./gradlew build --no-daemon From 0ffd5fcf85c58cf4508ab2c69cd1339cd4c5f89d Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sat, 8 Feb 2020 21:04:58 +0000 Subject: [PATCH 12/35] Add fs.getCapacity and fs.attributes (#365) - fs.getCapacity just returns the capacity of the current drive, if available. This will be nil on rom mounts. - fs.attributes returns an lfs like table of various file attributes. Currently, this contains: - access, modification, created: When this file was last accessed, modified and created. Time is measured in milliseconds since the epoch, same as os.epoch("utc") and what is accepted by os.date. - size: Same as fs.getSize - isDir: same as fs.isDir Closes #262 --- .../api/filesystem/FileAttributes.java | 81 ++++ .../computercraft/api/filesystem/IMount.java | 15 + .../api/filesystem/IWritableMount.java | 13 + .../dan200/computercraft/core/apis/FSAPI.java | 43 ++- .../core/filesystem/ComboMount.java | 16 + .../core/filesystem/FileMount.java | 22 ++ .../core/filesystem/FileSystem.java | 350 ++---------------- .../core/filesystem/JarMount.java | 80 ++++ .../core/filesystem/MountWrapper.java | 312 ++++++++++++++++ .../core/filesystem/SubMount.java | 39 +- .../core/ComputerTestDelegate.java | 2 +- .../resources/test-rom/spec/apis/fs_spec.lua | 40 ++ 12 files changed, 664 insertions(+), 349 deletions(-) create mode 100644 src/main/java/dan200/computercraft/api/filesystem/FileAttributes.java create mode 100644 src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java diff --git a/src/main/java/dan200/computercraft/api/filesystem/FileAttributes.java b/src/main/java/dan200/computercraft/api/filesystem/FileAttributes.java new file mode 100644 index 000000000..5dcf3e964 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/filesystem/FileAttributes.java @@ -0,0 +1,81 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ +package dan200.computercraft.api.filesystem; + +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.time.Instant; + +/** + * A simple version of {@link BasicFileAttributes}, which provides what information a {@link IMount} already exposes. + */ +final class FileAttributes implements BasicFileAttributes +{ + private static final FileTime EPOCH = FileTime.from( Instant.EPOCH ); + + private final boolean isDirectory; + private final long size; + + FileAttributes( boolean isDirectory, long size ) + { + this.isDirectory = isDirectory; + this.size = size; + } + + @Override + public FileTime lastModifiedTime() + { + return EPOCH; + } + + @Override + public FileTime lastAccessTime() + { + return EPOCH; + } + + @Override + public FileTime creationTime() + { + return EPOCH; + } + + @Override + public boolean isRegularFile() + { + return !isDirectory; + } + + @Override + public boolean isDirectory() + { + return isDirectory; + } + + @Override + public boolean isSymbolicLink() + { + return false; + } + + @Override + public boolean isOther() + { + return false; + } + + @Override + public long size() + { + return size; + } + + @Override + public Object fileKey() + { + return null; + } +} diff --git a/src/main/java/dan200/computercraft/api/filesystem/IMount.java b/src/main/java/dan200/computercraft/api/filesystem/IMount.java index 41fb51b78..a0aa661b7 100644 --- a/src/main/java/dan200/computercraft/api/filesystem/IMount.java +++ b/src/main/java/dan200/computercraft/api/filesystem/IMount.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; +import java.nio.file.attribute.BasicFileAttributes; import java.util.List; /** @@ -93,4 +94,18 @@ public interface IMount { return Channels.newChannel( openForRead( path ) ); } + + /** + * Get attributes about the given file. + * + * @param path The path to query. + * @return File attributes for the given file. + * @throws IOException If the file does not exist, or attributes could not be fetched. + */ + @Nonnull + default BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException + { + if( !exists( path ) ) throw new FileOperationException( path, "No such file" ); + return new FileAttributes( isDirectory( path ), getSize( path ) ); + } } diff --git a/src/main/java/dan200/computercraft/api/filesystem/IWritableMount.java b/src/main/java/dan200/computercraft/api/filesystem/IWritableMount.java index a778f423d..43bf5ae30 100644 --- a/src/main/java/dan200/computercraft/api/filesystem/IWritableMount.java +++ b/src/main/java/dan200/computercraft/api/filesystem/IWritableMount.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; +import java.util.OptionalLong; /** * Represents a part of a virtual filesystem that can be mounted onto a computer using {@link IComputerAccess#mount(String, IMount)} @@ -105,4 +106,16 @@ public interface IWritableMount extends IMount * @throws IOException If the remaining space could not be computed. */ long getRemainingSpace() throws IOException; + + /** + * Get the capacity of this mount. This should be equal to the size of all files/directories on this mount, minus + * the {@link #getRemainingSpace()}. + * + * @return The capacity of this mount, in bytes. + */ + @Nonnull + default OptionalLong getCapacity() + { + return OptionalLong.empty(); + } } diff --git a/src/main/java/dan200/computercraft/core/apis/FSAPI.java b/src/main/java/dan200/computercraft/core/apis/FSAPI.java index c99273763..74f786f23 100644 --- a/src/main/java/dan200/computercraft/core/apis/FSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/FSAPI.java @@ -22,6 +22,10 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalLong; import java.util.function.Function; import static dan200.computercraft.api.lua.ArgumentHelper.getString; @@ -76,6 +80,8 @@ public class FSAPI implements ILuaAPI "getFreeSpace", "find", "getDir", + "getCapacity", + "attributes", }; } @@ -315,9 +321,8 @@ public class FSAPI implements ILuaAPI throw new LuaException( e.getMessage() ); } } - case 14: + case 14: // find { - // find String path = getString( args, 0 ); try { @@ -329,12 +334,42 @@ public class FSAPI implements ILuaAPI throw new LuaException( e.getMessage() ); } } - case 15: + case 15: // getDir { - // getDir String path = getString( args, 0 ); return new Object[] { FileSystem.getDirectory( path ) }; } + case 16: // getCapacity + { + String path = getString( args, 0 ); + try + { + OptionalLong capacity = m_fileSystem.getCapacity( path ); + return new Object[] { capacity.isPresent() ? capacity.getAsLong() : null }; + } + catch( FileSystemException e ) + { + throw new LuaException( e.getMessage() ); + } + } + case 17: // attributes + { + String path = getString( args, 0 ); + try + { + BasicFileAttributes attributes = m_fileSystem.getAttributes( path ); + Map result = new HashMap<>(); + result.put( "modification", attributes.lastModifiedTime().toMillis() ); + result.put( "created", attributes.creationTime().toMillis() ); + result.put( "size", attributes.isDirectory() ? 0 : attributes.size() ); + result.put( "isDir", attributes.isDirectory() ); + return new Object[] { result }; + } + catch( FileSystemException e ) + { + throw new LuaException( e.getMessage() ); + } + } default: assert false; return null; diff --git a/src/main/java/dan200/computercraft/core/filesystem/ComboMount.java b/src/main/java/dan200/computercraft/core/filesystem/ComboMount.java index 53e119954..9114c3522 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/ComboMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/ComboMount.java @@ -12,6 +12,7 @@ import javax.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; import java.nio.channels.ReadableByteChannel; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -143,4 +144,19 @@ public class ComboMount implements IMount } throw new FileOperationException( path, "No such file" ); } + + @Nonnull + @Override + public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException + { + for( int i = m_parts.length - 1; i >= 0; --i ) + { + IMount part = m_parts[i]; + if( part.exists( path ) && !part.isDirectory( path ) ) + { + return part.getAttributes( path ); + } + } + throw new FileOperationException( path, "No such file" ); + } } diff --git a/src/main/java/dan200/computercraft/core/filesystem/FileMount.java b/src/main/java/dan200/computercraft/core/filesystem/FileMount.java index eb0f39ff8..13cf9b062 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/FileMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/FileMount.java @@ -16,8 +16,10 @@ import java.nio.channels.*; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; import java.util.List; +import java.util.OptionalLong; import java.util.Set; public class FileMount implements IWritableMount @@ -224,6 +226,19 @@ public class FileMount implements IWritableMount throw new FileOperationException( path, "No such file" ); } + @Nonnull + @Override + public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException + { + if( created() ) + { + File file = getRealPath( path ); + if( file.exists() ) return Files.readAttributes( file.toPath(), BasicFileAttributes.class ); + } + + throw new FileOperationException( path, "No such file" ); + } + // IWritableMount implementation @Override @@ -360,6 +375,13 @@ public class FileMount implements IWritableMount return Math.max( m_capacity - m_usedSpace, 0 ); } + @Nonnull + @Override + public OptionalLong getCapacity() + { + return OptionalLong.of( m_capacity - MINIMUM_FILE_SIZE ); + } + private File getRealPath( String path ) { return new File( m_rootPath, path ); diff --git a/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java b/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java index eda0361bd..0403f98ac 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java +++ b/src/main/java/dan200/computercraft/core/filesystem/FileSystem.java @@ -7,7 +7,6 @@ package dan200.computercraft.core.filesystem; import com.google.common.io.ByteStreams; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.filesystem.FileOperationException; import dan200.computercraft.api.filesystem.IFileSystem; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IWritableMount; @@ -23,6 +22,7 @@ import java.nio.channels.Channel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.AccessDeniedException; +import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.function.Function; import java.util.regex.Pattern; @@ -37,303 +37,8 @@ public class FileSystem */ private static final int MAX_COPY_DEPTH = 128; - private static class MountWrapper - { - private String m_label; - private String m_location; - - private IMount m_mount; - private IWritableMount m_writableMount; - - MountWrapper( String label, String location, IMount mount ) - { - m_label = label; - m_location = location; - m_mount = mount; - m_writableMount = null; - } - - MountWrapper( String label, String location, IWritableMount mount ) - { - this( label, location, (IMount) mount ); - m_writableMount = mount; - } - - public String getLabel() - { - return m_label; - } - - public String getLocation() - { - return m_location; - } - - public long getFreeSpace() - { - if( m_writableMount == null ) - { - return 0; - } - - try - { - return m_writableMount.getRemainingSpace(); - } - catch( IOException e ) - { - return 0; - } - } - - public boolean isReadOnly( String path ) - { - return m_writableMount == null; - } - - // IMount forwarders: - - public boolean exists( String path ) throws FileSystemException - { - path = toLocal( path ); - try - { - return m_mount.exists( path ); - } - catch( IOException e ) - { - throw new FileSystemException( e.getMessage() ); - } - } - - public boolean isDirectory( String path ) throws FileSystemException - { - path = toLocal( path ); - try - { - return m_mount.exists( path ) && m_mount.isDirectory( path ); - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - public void list( String path, List contents ) throws FileSystemException - { - path = toLocal( path ); - try - { - if( m_mount.exists( path ) && m_mount.isDirectory( path ) ) - { - m_mount.list( path, contents ); - } - else - { - throw localExceptionOf( path, "Not a directory" ); - } - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - public long getSize( String path ) throws FileSystemException - { - path = toLocal( path ); - try - { - if( m_mount.exists( path ) ) - { - if( m_mount.isDirectory( path ) ) - { - return 0; - } - else - { - return m_mount.getSize( path ); - } - } - else - { - throw localExceptionOf( path, "No such file" ); - } - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - public ReadableByteChannel openForRead( String path ) throws FileSystemException - { - path = toLocal( path ); - try - { - if( m_mount.exists( path ) && !m_mount.isDirectory( path ) ) - { - return m_mount.openChannelForRead( path ); - } - else - { - throw localExceptionOf( path, "No such file" ); - } - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - // IWritableMount forwarders: - - public void makeDirectory( String path ) throws FileSystemException - { - if( m_writableMount == null ) throw exceptionOf( path, "Access denied" ); - - path = toLocal( path ); - try - { - if( m_mount.exists( path ) ) - { - if( !m_mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" ); - } - else - { - m_writableMount.makeDirectory( path ); - } - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - public void delete( String path ) throws FileSystemException - { - if( m_writableMount == null ) throw exceptionOf( path, "Access denied" ); - - try - { - path = toLocal( path ); - if( m_mount.exists( path ) ) - { - m_writableMount.delete( path ); - } - } - catch( AccessDeniedException e ) - { - throw new FileSystemException( "Access denied" ); - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - public WritableByteChannel openForWrite( String path ) throws FileSystemException - { - if( m_writableMount == null ) throw exceptionOf( path, "Access denied" ); - - path = toLocal( path ); - try - { - if( m_mount.exists( path ) && m_mount.isDirectory( path ) ) - { - throw localExceptionOf( path, "Cannot write to directory" ); - } - else - { - if( !path.isEmpty() ) - { - String dir = getDirectory( path ); - if( !dir.isEmpty() && !m_mount.exists( path ) ) - { - m_writableMount.makeDirectory( dir ); - } - } - return m_writableMount.openChannelForWrite( path ); - } - } - catch( AccessDeniedException e ) - { - throw new FileSystemException( "Access denied" ); - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - public WritableByteChannel openForAppend( String path ) throws FileSystemException - { - if( m_writableMount == null ) throw exceptionOf( path, "Access denied" ); - - path = toLocal( path ); - try - { - if( !m_mount.exists( path ) ) - { - if( !path.isEmpty() ) - { - String dir = getDirectory( path ); - if( !dir.isEmpty() && !m_mount.exists( path ) ) - { - m_writableMount.makeDirectory( dir ); - } - } - return m_writableMount.openChannelForWrite( path ); - } - else if( m_mount.isDirectory( path ) ) - { - throw localExceptionOf( path, "Cannot write to directory" ); - } - else - { - return m_writableMount.openChannelForAppend( path ); - } - } - catch( AccessDeniedException e ) - { - throw new FileSystemException( "Access denied" ); - } - catch( IOException e ) - { - throw localExceptionOf( e ); - } - } - - private String toLocal( String path ) - { - return FileSystem.toLocal( path, m_location ); - } - - private FileSystemException localExceptionOf( IOException e ) - { - if( !m_location.isEmpty() && e instanceof FileOperationException ) - { - FileOperationException ex = (FileOperationException) e; - if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() ); - } - - return new FileSystemException( e.getMessage() ); - } - - private FileSystemException localExceptionOf( String path, String message ) - { - if( !m_location.isEmpty() ) path = path.isEmpty() ? m_location : m_location + "/" + path; - return exceptionOf( path, message ); - } - - private static FileSystemException exceptionOf( String path, String message ) - { - return new FileSystemException( "/" + path + ": " + message ); - } - } - private final FileSystemWrapperMount m_wrapper = new FileSystemWrapperMount( this ); - private final Map m_mounts = new HashMap<>(); + private final Map mounts = new HashMap<>(); private final HashMap>, ChannelWrapper> m_openFiles = new HashMap<>(); private final ReferenceQueue> m_openFileQueue = new ReferenceQueue<>(); @@ -363,10 +68,7 @@ public class FileSystem { if( mount == null ) throw new NullPointerException(); location = sanitizePath( location ); - if( location.contains( ".." ) ) - { - throw new FileSystemException( "Cannot mount below the root" ); - } + if( location.contains( ".." ) ) throw new FileSystemException( "Cannot mount below the root" ); mount( new MountWrapper( label, location, mount ) ); } @@ -387,14 +89,13 @@ public class FileSystem private synchronized void mount( MountWrapper wrapper ) { String location = wrapper.getLocation(); - m_mounts.remove( location ); - m_mounts.put( location, wrapper ); + mounts.remove( location ); + mounts.put( location, wrapper ); } public synchronized void unmount( String path ) { - path = sanitizePath( path ); - m_mounts.remove( path ); + mounts.remove( sanitizePath( path ) ); } public synchronized String combine( String path, String childPath ) @@ -438,27 +139,20 @@ public class FileSystem public static String getName( String path ) { path = sanitizePath( path, true ); - if( path.isEmpty() ) - { - return "root"; - } + if( path.isEmpty() ) return "root"; int lastSlash = path.lastIndexOf( '/' ); - if( lastSlash >= 0 ) - { - return path.substring( lastSlash + 1 ); - } - else - { - return path; - } + return lastSlash >= 0 ? path.substring( lastSlash + 1 ) : path; } public synchronized long getSize( String path ) throws FileSystemException { - path = sanitizePath( path ); - MountWrapper mount = getMount( path ); - return mount.getSize( path ); + return getMount( sanitizePath( path ) ).getSize( sanitizePath( path ) ); + } + + public synchronized BasicFileAttributes getAttributes( String path ) throws FileSystemException + { + return getMount( sanitizePath( path ) ).getAttributes( sanitizePath( path ) ); } public synchronized String[] list( String path ) throws FileSystemException @@ -471,7 +165,7 @@ public class FileSystem mount.list( path, list ); // Add any mounts that are mounted at this location - for( MountWrapper otherMount : m_mounts.values() ) + for( MountWrapper otherMount : mounts.values() ) { if( getDirectory( otherMount.getLocation() ).equals( path ) ) { @@ -733,17 +427,25 @@ public class FileSystem return null; } - public long getFreeSpace( String path ) throws FileSystemException + public synchronized long getFreeSpace( String path ) throws FileSystemException { path = sanitizePath( path ); MountWrapper mount = getMount( path ); return mount.getFreeSpace(); } - private MountWrapper getMount( String path ) throws FileSystemException + @Nonnull + public synchronized OptionalLong getCapacity( String path ) throws FileSystemException + { + path = sanitizePath( path ); + MountWrapper mount = getMount( path ); + return mount.getCapacity(); + } + + private synchronized MountWrapper getMount( String path ) throws FileSystemException { // Return the deepest mount that contains a given path - Iterator it = m_mounts.values().iterator(); + Iterator it = mounts.values().iterator(); MountWrapper match = null; int matchLength = 999; while( it.hasNext() ) diff --git a/src/main/java/dan200/computercraft/core/filesystem/JarMount.java b/src/main/java/dan200/computercraft/core/filesystem/JarMount.java index 336d4ddcc..2cf55a747 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/JarMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/JarMount.java @@ -23,6 +23,8 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; import java.util.Enumeration; import java.util.HashMap; import java.util.List; @@ -221,6 +223,20 @@ public class JarMount implements IMount throw new FileOperationException( path, "No such file" ); } + @Nonnull + @Override + public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException + { + FileEntry file = get( path ); + if( file != null ) + { + ZipEntry entry = zip.getEntry( file.path ); + if( entry != null ) return new ZipEntryAttributes( entry ); + } + + throw new FileOperationException( path, "No such file" ); + } + private static class FileEntry { String path; @@ -261,4 +277,68 @@ public class JarMount implements IMount Reference next; while( (next = MOUNT_QUEUE.poll()) != null ) IoUtil.closeQuietly( ((MountReference) next).file ); } + + private static class ZipEntryAttributes implements BasicFileAttributes + { + private final ZipEntry entry; + + ZipEntryAttributes( ZipEntry entry ) + { + this.entry = entry; + } + + @Override + public FileTime lastModifiedTime() + { + return entry.getLastModifiedTime(); + } + + @Override + public FileTime lastAccessTime() + { + return entry.getLastAccessTime(); + } + + @Override + public FileTime creationTime() + { + return entry.getCreationTime(); + } + + @Override + public boolean isRegularFile() + { + return !entry.isDirectory(); + } + + @Override + public boolean isDirectory() + { + return entry.isDirectory(); + } + + @Override + public boolean isSymbolicLink() + { + return false; + } + + @Override + public boolean isOther() + { + return false; + } + + @Override + public long size() + { + return entry.getSize(); + } + + @Override + public Object fileKey() + { + return null; + } + } } diff --git a/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java b/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java new file mode 100644 index 000000000..5cc4ee958 --- /dev/null +++ b/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java @@ -0,0 +1,312 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.core.filesystem; + +import dan200.computercraft.api.filesystem.FileOperationException; +import dan200.computercraft.api.filesystem.IMount; +import dan200.computercraft.api.filesystem.IWritableMount; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.nio.file.AccessDeniedException; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.List; +import java.util.OptionalLong; + +class MountWrapper +{ + private String label; + private String location; + + private IMount mount; + private IWritableMount writableMount; + + MountWrapper( String label, String location, IMount mount ) + { + this.label = label; + this.location = location; + this.mount = mount; + writableMount = null; + } + + MountWrapper( String label, String location, IWritableMount mount ) + { + this( label, location, (IMount) mount ); + writableMount = mount; + } + + public String getLabel() + { + return label; + } + + public String getLocation() + { + return location; + } + + public long getFreeSpace() + { + if( writableMount == null ) return 0; + + try + { + return writableMount.getRemainingSpace(); + } + catch( IOException e ) + { + return 0; + } + } + + public OptionalLong getCapacity() + { + return writableMount == null ? OptionalLong.empty() : writableMount.getCapacity(); + } + + public boolean isReadOnly( String path ) + { + return writableMount == null; + } + + public boolean exists( String path ) throws FileSystemException + { + path = toLocal( path ); + try + { + return mount.exists( path ); + } + catch( IOException e ) + { + throw new FileSystemException( e.getMessage() ); + } + } + + public boolean isDirectory( String path ) throws FileSystemException + { + path = toLocal( path ); + try + { + return mount.exists( path ) && mount.isDirectory( path ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public void list( String path, List contents ) throws FileSystemException + { + path = toLocal( path ); + try + { + if( !mount.exists( path ) || !mount.isDirectory( path ) ) + { + throw localExceptionOf( path, "Not a directory" ); + } + + mount.list( path, contents ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public long getSize( String path ) throws FileSystemException + { + path = toLocal( path ); + try + { + if( !mount.exists( path ) ) throw localExceptionOf( path, "No such file" ); + return mount.isDirectory( path ) ? 0 : mount.getSize( path ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + @Nonnull + public BasicFileAttributes getAttributes( String path ) throws FileSystemException + { + path = toLocal( path ); + try + { + if( !mount.exists( path ) ) throw localExceptionOf( path, "No such file" ); + return mount.getAttributes( path ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public ReadableByteChannel openForRead( String path ) throws FileSystemException + { + path = toLocal( path ); + try + { + if( mount.exists( path ) && !mount.isDirectory( path ) ) + { + return mount.openChannelForRead( path ); + } + else + { + throw localExceptionOf( path, "No such file" ); + } + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public void makeDirectory( String path ) throws FileSystemException + { + if( writableMount == null ) throw exceptionOf( path, "Access denied" ); + + path = toLocal( path ); + try + { + if( mount.exists( path ) ) + { + if( !mount.isDirectory( path ) ) throw localExceptionOf( path, "File exists" ); + } + else + { + writableMount.makeDirectory( path ); + } + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public void delete( String path ) throws FileSystemException + { + if( writableMount == null ) throw exceptionOf( path, "Access denied" ); + + try + { + path = toLocal( path ); + if( mount.exists( path ) ) + { + writableMount.delete( path ); + } + } + catch( AccessDeniedException e ) + { + throw new FileSystemException( "Access denied" ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public WritableByteChannel openForWrite( String path ) throws FileSystemException + { + if( writableMount == null ) throw exceptionOf( path, "Access denied" ); + + path = toLocal( path ); + try + { + if( mount.exists( path ) && mount.isDirectory( path ) ) + { + throw localExceptionOf( path, "Cannot write to directory" ); + } + else + { + if( !path.isEmpty() ) + { + String dir = FileSystem.getDirectory( path ); + if( !dir.isEmpty() && !mount.exists( path ) ) + { + writableMount.makeDirectory( dir ); + } + } + return writableMount.openChannelForWrite( path ); + } + } + catch( AccessDeniedException e ) + { + throw new FileSystemException( "Access denied" ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + public WritableByteChannel openForAppend( String path ) throws FileSystemException + { + if( writableMount == null ) throw exceptionOf( path, "Access denied" ); + + path = toLocal( path ); + try + { + if( !mount.exists( path ) ) + { + if( !path.isEmpty() ) + { + String dir = FileSystem.getDirectory( path ); + if( !dir.isEmpty() && !mount.exists( path ) ) + { + writableMount.makeDirectory( dir ); + } + } + return writableMount.openChannelForWrite( path ); + } + else if( mount.isDirectory( path ) ) + { + throw localExceptionOf( path, "Cannot write to directory" ); + } + else + { + return writableMount.openChannelForAppend( path ); + } + } + catch( AccessDeniedException e ) + { + throw new FileSystemException( "Access denied" ); + } + catch( IOException e ) + { + throw localExceptionOf( e ); + } + } + + private String toLocal( String path ) + { + return FileSystem.toLocal( path, location ); + } + + private FileSystemException localExceptionOf( IOException e ) + { + if( !location.isEmpty() && e instanceof FileOperationException ) + { + FileOperationException ex = (FileOperationException) e; + if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() ); + } + + return new FileSystemException( e.getMessage() ); + } + + private FileSystemException localExceptionOf( String path, String message ) + { + if( !location.isEmpty() ) path = path.isEmpty() ? location : location + "/" + path; + return exceptionOf( path, message ); + } + + private static FileSystemException exceptionOf( String path, String message ) + { + return new FileSystemException( "/" + path + ": " + message ); + } +} diff --git a/src/main/java/dan200/computercraft/core/filesystem/SubMount.java b/src/main/java/dan200/computercraft/core/filesystem/SubMount.java index 1cdac4a6a..d59aeab36 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/SubMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/SubMount.java @@ -11,43 +11,42 @@ import javax.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; import java.nio.channels.ReadableByteChannel; +import java.nio.file.attribute.BasicFileAttributes; import java.util.List; public class SubMount implements IMount { - private IMount m_parent; - private String m_subPath; + private IMount parent; + private String subPath; public SubMount( IMount parent, String subPath ) { - m_parent = parent; - m_subPath = subPath; + this.parent = parent; + this.subPath = subPath; } - // IMount implementation - @Override public boolean exists( @Nonnull String path ) throws IOException { - return m_parent.exists( getFullPath( path ) ); + return parent.exists( getFullPath( path ) ); } @Override public boolean isDirectory( @Nonnull String path ) throws IOException { - return m_parent.isDirectory( getFullPath( path ) ); + return parent.isDirectory( getFullPath( path ) ); } @Override public void list( @Nonnull String path, @Nonnull List contents ) throws IOException { - m_parent.list( getFullPath( path ), contents ); + parent.list( getFullPath( path ), contents ); } @Override public long getSize( @Nonnull String path ) throws IOException { - return m_parent.getSize( getFullPath( path ) ); + return parent.getSize( getFullPath( path ) ); } @Nonnull @@ -55,25 +54,25 @@ public class SubMount implements IMount @Deprecated public InputStream openForRead( @Nonnull String path ) throws IOException { - return m_parent.openForRead( getFullPath( path ) ); + return parent.openForRead( getFullPath( path ) ); } @Nonnull @Override public ReadableByteChannel openChannelForRead( @Nonnull String path ) throws IOException { - return m_parent.openChannelForRead( getFullPath( path ) ); + return parent.openChannelForRead( getFullPath( path ) ); + } + + @Nonnull + @Override + public BasicFileAttributes getAttributes( @Nonnull String path ) throws IOException + { + return parent.getAttributes( getFullPath( path ) ); } private String getFullPath( String path ) { - if( path.isEmpty() ) - { - return m_subPath; - } - else - { - return m_subPath + "/" + path; - } + return path.isEmpty() ? subPath : subPath + "/" + path; } } diff --git a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java index c5d9e02ef..6413d5ef0 100644 --- a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java +++ b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java @@ -79,7 +79,7 @@ public class ComputerTestDelegate ComputerCraft.log = LogManager.getLogger( ComputerCraft.MOD_ID ); Terminal term = new Terminal( 78, 20 ); - IWritableMount mount = new FileMount( new File( "test-files/mount" ), Long.MAX_VALUE ); + IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 ); // Remove any existing files List children = new ArrayList<>(); diff --git a/src/test/resources/test-rom/spec/apis/fs_spec.lua b/src/test/resources/test-rom/spec/apis/fs_spec.lua index f6ccac6bc..5288aa9c8 100644 --- a/src/test/resources/test-rom/spec/apis/fs_spec.lua +++ b/src/test/resources/test-rom/spec/apis/fs_spec.lua @@ -149,4 +149,44 @@ describe("The fs library", function() expect.error(fs.move, "rom", "test-files"):eq("Access denied") end) end) + + describe("fs.getCapacity", function() + it("returns nil on read-only mounts", function() + expect(fs.getCapacity("rom")):eq(nil) + end) + + it("returns the capacity on the root mount", function() + expect(fs.getCapacity("")):eq(10000000) + end) + end) + + describe("fs.attributes", function() + it("errors on non-existent files", function() + expect.error(fs.attributes, "xuxu_nao_existe"):eq("/xuxu_nao_existe: No such file") + end) + + it("returns information about read-only mounts", function() + expect(fs.attributes("rom")):matches { isDir = true, size = 0 } + end) + + it("returns information about files", function() + local now = os.epoch("utc") + + fs.delete("/tmp/basic-file") + local h = fs.open("/tmp/basic-file", "w") + h.write("A reasonably sized string") + h.close() + + local attributes = fs.attributes("tmp/basic-file") + expect(attributes):matches { isDir = false, size = 25 } + + if attributes.created - now >= 1000 then + fail(("Expected created time (%d) to be within 1000ms of now (%d"):format(attributes.created, now)) + end + + if attributes.modification - now >= 1000 then + fail(("Expected modification time (%d) to be within 1000ms of now (%d"):format(attributes.modification, now)) + end + end) + end) end) From 68762fe84c1eff4f3ce32f23c01c597fc408222d Mon Sep 17 00:00:00 2001 From: SquidDev Date: Thu, 9 Apr 2020 22:06:53 +0100 Subject: [PATCH 13/35] Switch FileMount to use Files.walkFileTree This means we are already provided with file attributes, which halfs[^1] the time taken to scan folders. [^1]: This dropped the fastest scan time from ~1.3s to ~0.6. It's by no means a perfect benchmark, but this is still an obvious improvement. --- .../core/filesystem/FileMount.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/main/java/dan200/computercraft/core/filesystem/FileMount.java b/src/main/java/dan200/computercraft/core/filesystem/FileMount.java index 13cf9b062..9cf1ecd3e 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/FileMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/FileMount.java @@ -6,6 +6,7 @@ package dan200.computercraft.core.filesystem; import com.google.common.collect.Sets; +import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.FileOperationException; import dan200.computercraft.api.filesystem.IWritableMount; @@ -13,9 +14,7 @@ import javax.annotation.Nonnull; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.*; -import java.nio.file.Files; -import java.nio.file.OpenOption; -import java.nio.file.StandardOpenOption; +import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.Collections; import java.util.List; @@ -404,23 +403,46 @@ public class FileMount implements IWritableMount } } + private static class Visitor extends SimpleFileVisitor + { + long size; + + @Override + public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs ) + { + size += MINIMUM_FILE_SIZE; + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile( Path file, BasicFileAttributes attrs ) + { + size += Math.max( attrs.size(), MINIMUM_FILE_SIZE ); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed( Path file, IOException exc ) + { + ComputerCraft.log.error( "Error computing file size for {}", file, exc ); + return FileVisitResult.CONTINUE; + } + } + private static long measureUsedSpace( File file ) { if( !file.exists() ) return 0; - if( file.isDirectory() ) + try { - long size = MINIMUM_FILE_SIZE; - String[] contents = file.list(); - for( String content : contents ) - { - size += measureUsedSpace( new File( file, content ) ); - } - return size; + Visitor visitor = new Visitor(); + Files.walkFileTree( file.toPath(), visitor ); + return visitor.size; } - else + catch( IOException e ) { - return Math.max( file.length(), MINIMUM_FILE_SIZE ); + ComputerCraft.log.error( "Error computing file size for {}", file, e ); + return 0; } } } From 68e6bc464b69c55dd64e7d17d12a34a6160e8d8f Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Thu, 9 Apr 2020 22:15:06 +0100 Subject: [PATCH 14/35] Bump illuaminate version For now, we just perform the minimal number of changes to get this working. We'll switch to a more strict bracket handling system in the future. --- .github/workflows/main-ci.yml | 2 +- .../resources/assets/computercraft/lua/bios.lua | 8 ++++---- .../assets/computercraft/lua/rom/apis/term.lua | 2 +- .../computercraft/lua/rom/apis/textutils.lua | 6 +++--- .../lua/rom/modules/main/cc/pretty.lua | 2 +- .../lua/rom/modules/main/cc/shell/completion.lua | 2 +- .../assets/computercraft/lua/rom/programs/edit.lua | 4 ++-- .../lua/rom/programs/fun/advanced/paint.lua | 2 +- .../lua/rom/programs/fun/advanced/redirection.lua | 2 +- .../lua/rom/programs/fun/adventure.lua | 2 +- .../computercraft/lua/rom/programs/fun/dj.lua | 2 +- .../computercraft/lua/rom/programs/fun/worm.lua | 2 +- .../lua/rom/programs/pocket/falling.lua | 2 +- .../computercraft/lua/rom/programs/rednet/chat.lua | 2 +- .../assets/computercraft/lua/rom/startup.lua | 4 ++-- src/test/resources/test-rom/mcfly.lua | 2 +- .../resources/test-rom/spec/apis/http_spec.lua | 14 +++++++------- .../spec/modules/cc/shell/completion_spec.lua | 2 +- .../test-rom/spec/programs/http/pastebin_spec.lua | 4 ++-- .../test-rom/spec/programs/peripherals_spec.lua | 2 +- 20 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 924abb37a..0aee2ef4f 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -42,7 +42,7 @@ jobs: - name: Lint Lua code run: | test -d bin || mkdir bin - test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/bin/illuaminate + test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate chmod +x bin/illuaminate bin/illuaminate lint diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index 500276da4..a7f7a9d21 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -253,7 +253,7 @@ end function print( ... ) local nLinesPrinted = 0 - local nLimit = select("#", ... ) + local nLimit = select("#", ...) for n = 1, nLimit do local s = tostring( select( n, ... ) ) if n < nLimit then @@ -695,7 +695,7 @@ if http then end local function checkOptions( options, body ) - checkKey( options, "url", "string") + checkKey( options, "url", "string" ) if body == false then checkKey( options, "body", "nil" ) else @@ -725,7 +725,7 @@ if http then return nil, err end - http.get = function( _url, _headers, _binary) + http.get = function(_url, _headers, _binary) if type( _url ) == "table" then checkOptions( _url, false ) return wrapRequest( _url.url, _url ) @@ -737,7 +737,7 @@ if http then return wrapRequest( _url, _url, nil, _headers, _binary ) end - http.post = function( _url, _post, _headers, _binary) + http.post = function(_url, _post, _headers, _binary) if type( _url ) == "table" then checkOptions( _url, true ) return wrapRequest( _url.url, _url ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua index 706bd16f6..c6f9c2a9b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua @@ -43,7 +43,7 @@ end -- Some methods shouldn't go through redirects, so we move them to the main -- term API. -for _, method in ipairs { "nativePaletteColor", "nativePaletteColour"} do +for _, method in ipairs { "nativePaletteColor", "nativePaletteColour" } do term[method] = native[method] native[method] = nil end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua index 38360ea71..de7cd4e95 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua @@ -346,10 +346,10 @@ function urlEncode( str ) else -- Non-ASCII (encode as UTF-8) return - string.format("%%%02X", 192 + bit32.band( bit32.arshift(n, 6), 31 ) ) .. - string.format("%%%02X", 128 + bit32.band( n, 63 ) ) + string.format("%%%02X", 192 + bit32.band( bit32.arshift(n, 6), 31 )) .. + string.format("%%%02X", 128 + bit32.band( n, 63 )) end - end ) + end) str = string.gsub(str, " ", "+") end return str diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua index cf0007568..2fdaee187 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -393,7 +393,7 @@ end -- @treturn Doc The object formatted as a document. -- @usage Display a table on the screen -- local pretty = require "cc.pretty" --- pretty.print(pretty.pretty({ 1, 2, 3 }) +-- pretty.print(pretty.pretty({ 1, 2, 3 })) local function pretty(obj) return pretty_impl(obj, {}) end diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua index 865e54dcb..d89c72a91 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/shell/completion.lua @@ -97,7 +97,7 @@ end -- complete.build( -- { complete.choice, { "get", "put" } }, -- complete.dir, --- } complete.file, many = true } +-- { complete.file, many = true } -- ) local function build(...) local arguments = table.pack(...) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua b/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua index 5e6cfee1c..0125d5b12 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua @@ -15,7 +15,7 @@ end -- Create .lua files by default if not fs.exists( sPath ) and not string.find( sPath, "%." ) then - local sExtension = settings.get("edit.default_extension", "" ) + local sExtension = settings.get("edit.default_extension", "") if sExtension ~= "" and type( sExtension ) == "string" then sPath = sPath .. "." .. sExtension end @@ -85,7 +85,7 @@ end local function save( _sPath ) -- Create intervening folder - local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) + local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len()) if not fs.exists( sDir ) then fs.makeDir( sDir ) end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua index 758e82074..d2f3a5159 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua @@ -46,7 +46,7 @@ end -- Create .nfp files by default if not fs.exists( sPath ) and not string.find( sPath, "%." ) then - local sExtension = settings.get("paint.default_extension", "" ) + local sExtension = settings.get("paint.default_extension", "") if sExtension ~= "" and type( sExtension ) == "string" then sPath = sPath .. "." .. sExtension end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua index fc6e0f038..3f68e3859 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua @@ -525,7 +525,7 @@ function InterFace.drawBar() term.setCursorPos( TermW - 8, TermH ) term.setBackgroundColor( colors.black ) term.setTextColour(InterFace.cSpeedD) - write(" <<" ) + write(" <<") if bPaused then term.setTextColour(InterFace.cSpeedA) else diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua index 17c2b9a2b..48e06a950 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua @@ -1001,7 +1001,7 @@ function commands.cbreak( _sItem, _sTool ) end end else - print( "You can't break " .. sItem .. " with " .. sTool .. ".") + print("You can't break " .. sItem .. " with " .. sTool .. ".") end elseif tItem.creature == true then diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua index 78b259fe0..5ec03d2ce 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua @@ -1,7 +1,7 @@ local tArgs = { ... } local function printUsage() - print( "Usages:") + print( "Usages:" ) print( "dj play" ) print( "dj play " ) print( "dj stop" ) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua index 507763cb4..e02654d1f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua @@ -89,7 +89,7 @@ local function drawMenu() term.setTextColour( headingColour ) term.setCursorPos(w - 11, 1) - term.write( "DIFFICULTY ") + term.write( "DIFFICULTY " ) term.setTextColour( textColour ) term.setCursorPos(w, 1) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua index bdc922e4f..330042936 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua @@ -158,7 +158,7 @@ local block_T = { bg = colorass(colors.purple, colors.white), } -local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T} +local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T } local points = {4, 10, 30, 120} diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua index 4c4ae75c0..ac8f4bc65 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua @@ -387,7 +387,7 @@ elseif sCommand == "join" then promptWindow.setCursorPos( 1, 1 ) promptWindow.clearLine() promptWindow.setTextColor( highlightColour ) - promptWindow.write( ": ") + promptWindow.write( ": " ) promptWindow.setTextColor( textColour ) local sChat = read( nil, tSendHistory ) diff --git a/src/main/resources/assets/computercraft/lua/rom/startup.lua b/src/main/resources/assets/computercraft/lua/rom/startup.lua index dcefffb93..e19eb0317 100644 --- a/src/main/resources/assets/computercraft/lua/rom/startup.lua +++ b/src/main/resources/assets/computercraft/lua/rom/startup.lua @@ -43,7 +43,7 @@ end local function completePastebinPut(shell, text, previous) if previous[2] == "put" then - return fs.complete(text, shell.dir(), true, false ) + return fs.complete( text, shell.dir(), true, false ) end end @@ -106,7 +106,7 @@ if turtle then ) ) shell.setCompletionFunction( "rom/programs/turtle/turn.lua", completion.build( { completion.choice, { "left", "right" }, true, many = true } - )) + ) ) shell.setCompletionFunction( "rom/programs/turtle/equip.lua", completion.build( nil, { completion.choice, { "left", "right" } } diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua index ac64ad565..20dcd4cd2 100644 --- a/src/test/resources/test-rom/mcfly.lua +++ b/src/test/resources/test-rom/mcfly.lua @@ -356,7 +356,7 @@ function expect_mt:called_with_matching(...) return called_with_check(matches, self, ...) end -local expect = setmetatable( { +local expect = setmetatable({ --- Construct an expectation on the error message calling this function -- produces -- diff --git a/src/test/resources/test-rom/spec/apis/http_spec.lua b/src/test/resources/test-rom/spec/apis/http_spec.lua index a6774a48c..ecaf1d307 100644 --- a/src/test/resources/test-rom/spec/apis/http_spec.lua +++ b/src/test/resources/test-rom/spec/apis/http_spec.lua @@ -1,19 +1,19 @@ describe("The http library", function() describe("http.checkURL", function() it("accepts well formed domains", function() - expect({ http.checkURL("https://google.com")}):same({ true }) + expect({ http.checkURL("https://google.com") }):same({ true }) end) it("rejects malformed URLs", function() - expect({ http.checkURL("google.com")}):same({ false, "Must specify http or https" }) - expect({ http.checkURL("https:google.com")}):same({ false, "URL malformed" }) - expect({ http.checkURL("https:/google.com")}):same({ false, "URL malformed" }) - expect({ http.checkURL("wss://google.com")}):same({ false, "Invalid protocol 'wss'" }) + expect({ http.checkURL("google.com") }):same({ false, "Must specify http or https" }) + expect({ http.checkURL("https:google.com") }):same({ false, "URL malformed" }) + expect({ http.checkURL("https:/google.com") }):same({ false, "URL malformed" }) + expect({ http.checkURL("wss://google.com") }):same({ false, "Invalid protocol 'wss'" }) end) it("rejects local domains", function() - expect({ http.checkURL("http://localhost")}):same({ false, "Domain not permitted" }) - expect({ http.checkURL("http://127.0.0.1")}):same({ false, "Domain not permitted" }) + expect({ http.checkURL("http://localhost") }):same({ false, "Domain not permitted" }) + expect({ http.checkURL("http://127.0.0.1") }):same({ false, "Domain not permitted" }) end) end) end) diff --git a/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua b/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua index ffcd3b7f3..6b2c0aaa1 100644 --- a/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua +++ b/src/test/resources/test-rom/spec/modules/cc/shell/completion_spec.lua @@ -22,7 +22,7 @@ describe("cc.shell.completion", function() local spec = c.build( function() return { "a", "b", "c" } end, nil, - { c.choice, { "d", "e", "f"} } + { c.choice, { "d", "e", "f" } } ) expect(spec(shell, 1, "")):same { "a", "b", "c" } diff --git a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua index 831d88fc1..61bfe833b 100644 --- a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua +++ b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua @@ -52,14 +52,14 @@ describe("The pastebin program", function() local file = fs.open( "testup", "w" ) file.close() - expect(capture(stub, "pastebin", "put", "testup" )) + expect(capture(stub, "pastebin", "put", "testup")) :matches { ok = true, output = "Connecting to pastebin.com... Success.\nUploaded as https://pastebin.com/abcde\nRun \"pastebin get abcde\" to download anywhere\n", error = "" } end) it("upload a not existing program to pastebin", function() setup_request() - expect(capture(stub, "pastebin", "put", "nothing" )) + expect(capture(stub, "pastebin", "put", "nothing")) :matches { ok = true, output = "No such file\n", error = "" } end) diff --git a/src/test/resources/test-rom/spec/programs/peripherals_spec.lua b/src/test/resources/test-rom/spec/programs/peripherals_spec.lua index f52961a13..6c9d433bd 100644 --- a/src/test/resources/test-rom/spec/programs/peripherals_spec.lua +++ b/src/test/resources/test-rom/spec/programs/peripherals_spec.lua @@ -2,7 +2,7 @@ local capture = require "test_helpers".capture_program describe("The peripherals program", function() it("says when there are no peripherals", function() - expect(capture(stub, "peripherals" )) + expect(capture(stub, "peripherals")) :matches { ok = true, output = "Attached Peripherals:\nNone\n", error = "" } end) end) From a8ce5a5b2015f65f71f1ae2e75944cb57844736a Mon Sep 17 00:00:00 2001 From: "Christian L.W" Date: Thu, 9 Apr 2020 23:30:20 +0200 Subject: [PATCH 15/35] Add Danish translation (#395) --- .../assets/computercraft/lang/da_dk.lang | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/main/resources/assets/computercraft/lang/da_dk.lang diff --git a/src/main/resources/assets/computercraft/lang/da_dk.lang b/src/main/resources/assets/computercraft/lang/da_dk.lang new file mode 100644 index 000000000..3da64d2a4 --- /dev/null +++ b/src/main/resources/assets/computercraft/lang/da_dk.lang @@ -0,0 +1,48 @@ +tile.computercraft:computer.name=Computer +tile.computercraft:advanced_computer.name=Avanceret Computer +tile.computercraft:drive.name=Diskdrev +tile.computercraft:printer.name=Printer +tile.computercraft:monitor.name=Skærm +tile.computercraft:advanced_monitor.name=Avanceret Skærm +tile.computercraft:wireless_modem.name=TrÃ¥dløst Modem +tile.computercraft:wired_modem.name=Kablet Modem +tile.computercraft:cable.name=Netværkskabel +tile.computercraft:command_computer.name=Kommandocomputer +tile.computercraft:advanced_modem.name=Endermodem +tile.computercraft:speaker.name=Højttaler + +tile.computercraft:turtle.name=Turtle +tile.computercraft:turtle.upgraded.name=%s Turtle +tile.computercraft:turtle.upgraded_twice.name=%s %s Turtle +tile.computercraft:advanced_turtle.name=Avanceret Turtle +tile.computercraft:advanced_turtle.upgraded.name=Avanceret %s Turtle +tile.computercraft:advanced_turtle.upgraded_twice.name=Avanceret %s %s Turtle + +item.computercraft:disk.name=Floppydisk +item.computercraft:treasure_disk.name=Floppydisk +item.computercraft:page.name=Printet Side +item.computercraft:pages.name=Printede Sider +item.computercraft:book.name=Printet Bog + +item.computercraft:pocket_computer.name=Lommecomputer +item.computercraft:pocket_computer.upgraded.name=%s Lommecomputer +item.computercraft:advanced_pocket_computer.name=Avanceret Lommecomputer +item.computercraft:advanced_pocket_computer.upgraded.name=Avanceret %s Lommecomputer + +upgrade.minecraft:diamond_sword.adjective=Kæmpende +upgrade.minecraft:diamond_shovel.adjective=Gravende +upgrade.minecraft:diamond_pickaxe.adjective=Brydende +upgrade.minecraft:diamond_axe.adjective=Fældende +upgrade.minecraft:diamond_hoe.adjective=Dyrkende +upgrade.computercraft:wireless_modem.adjective=TrÃ¥dløs +upgrade.minecraft:crafting_table.adjective=Fremstillende +upgrade.computercraft:advanced_modem.adjective=EndertrÃ¥dløs +upgrade.computercraft:speaker.adjective=Larmende + +chat.computercraft.wired_modem.peripheral_connected=Perifer enhed "%s" koblet til netværk +chat.computercraft.wired_modem.peripheral_disconnected=Perifer enhed "%s" koblet fra netværk + +# Misc tooltips +gui.computercraft.tooltip.copy=Kopier til udklipsholder +gui.computercraft.tooltip.computer_id=(Computer-ID: %s) +gui.computercraft.tooltip.disk_id=(Disk-ID: %s) From 1ccd687c009f3a915ce76cfa36bcbffc4bbd474b Mon Sep 17 00:00:00 2001 From: "E. Kim" Date: Fri, 10 Apr 2020 06:31:20 +0900 Subject: [PATCH 16/35] Create ko_kr.lang (#381) --- .../assets/computercraft/lang/ko_kr.lang | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 src/main/resources/assets/computercraft/lang/ko_kr.lang diff --git a/src/main/resources/assets/computercraft/lang/ko_kr.lang b/src/main/resources/assets/computercraft/lang/ko_kr.lang new file mode 100644 index 000000000..784bb4b31 --- /dev/null +++ b/src/main/resources/assets/computercraft/lang/ko_kr.lang @@ -0,0 +1,195 @@ +itemGroup.computercraft=컴퓨터í¬ëž˜í”„트 + +tile.computercraft:computer.name=컴퓨터 +tile.computercraft:advanced_computer.name=고급 컴퓨터 +tile.computercraft:drive.name=ë””ìŠ¤í¬ ë“œë¼ì´ë¸Œ +tile.computercraft:printer.name=프린터 +tile.computercraft:monitor.name=모니터 +tile.computercraft:advanced_monitor.name=고급 모니터 +tile.computercraft:wireless_modem.name=무선 모뎀 +tile.computercraft:wired_modem.name=유선 모뎀 +tile.computercraft:cable.name=ë„¤íŠ¸ì›Œí¬ ì¼€ì´ë¸” +tile.computercraft:command_computer.name=명령 컴퓨터 +tile.computercraft:advanced_modem.name=ì—”ë” ëª¨ëŽ€ +tile.computercraft:speaker.name=스피커 + +tile.computercraft:turtle.name=í„°í‹€ +tile.computercraft:turtle.upgraded.name=%s í„°í‹€ +tile.computercraft:turtle.upgraded_twice.name=%s %s í„°í‹€ +tile.computercraft:advanced_turtle.name=고급 í„°í‹€ +tile.computercraft:advanced_turtle.upgraded.name=고급 %s í„°í‹€ +tile.computercraft:advanced_turtle.upgraded_twice.name=고급 %s %s í„°í‹€ + +item.computercraft:disk.name=플로피 ë””ìŠ¤í¬ +item.computercraft:treasure_disk.name=플로피 ë””ìŠ¤í¬ +item.computercraft:page.name=ì¸ì‡„ëœ íŽ˜ì´ì§€ +item.computercraft:pages.name=ì¸ì‡„ëœ íŽ˜ì´ì§€ ëª¨ìŒ +item.computercraft:book.name=ì¸ì‡„ëœ ì±… + +item.computercraft:pocket_computer.name=í¬ì¼“ 컴퓨터 +item.computercraft:pocket_computer.upgraded.name=%s í¬ì¼“ 컴퓨터 +item.computercraft:advanced_pocket_computer.name=고급 í¬ì¼“ 컴퓨터 +item.computercraft:advanced_pocket_computer.upgraded.name=고급 %s í¬ì¼“ 컴퓨터 + +upgrade.minecraft:diamond_sword.adjective=난투 +upgrade.minecraft:diamond_shovel.adjective=êµ´ì°© +upgrade.minecraft:diamond_pickaxe.adjective=채굴 +upgrade.minecraft:diamond_axe.adjective=벌목 +upgrade.minecraft:diamond_hoe.adjective=ë†ì—… +upgrade.computercraft:wireless_modem.adjective=무선 +upgrade.minecraft:crafting_table.adjective=ì¡°í•© +upgrade.computercraft:advanced_modem.adjective=ì—”ë” +upgrade.computercraft:speaker.adjective=ì†ŒìŒ + +chat.computercraft.wired_modem.peripheral_connected=주변 "%s"ì´ ë„¤íŠ¸ì›Œí¬ì— ì—°ê²°ë˜ì—ˆìŠµë‹ˆë‹¤. +chat.computercraft.wired_modem.peripheral_disconnected=주변 "%s"ì´ ë„¤íŠ¸ì›Œí¬ë¡œë¶€í„° 분리ë˜ì—ˆìŠµë‹ˆë‹¤. + +# Command descriptions, usage and any additional messages. +commands.computercraft.synopsis=컴퓨터를 제어하기 위한 다양한 명령어 +commands.computercraft.desc=/computercraft 명령어는 컴퓨터를 제어하고 ìƒí˜¸ìž‘용하기 위한 다양한 디버깅 ë° ê´€ë¦¬ìž ë„구를 제공합니다. + +commands.computercraft.help.synopsis=특정 ëª…ë ¹ì–´ì— ëŒ€í•œ ë„움ë§ì„ 제공하기 +commands.computercraft.help.desc= +commands.computercraft.help.usage=[command] +commands.computercraft.help.no_children=%sì—는 하위 명령어가 없습니다. +commands.computercraft.help.no_command='%s'ë¼ëŠ” 명령어가 없습니다. + +commands.computercraft.dump.synopsis=ì»´í“¨í„°ì˜ ìƒíƒœë¥¼ 보여주기 +commands.computercraft.dump.desc=모든 ì‹œìŠ¤í…œì˜ ìƒíƒœ ë˜ëŠ” í•œ ì‹œìŠ¤í…œì— ëŒ€í•œ 특정 정보를 표시합니다. ì»´í“¨í„°ì˜ ì¸ìŠ¤í„´ìŠ¤ ID(예: 123)나 컴퓨터 ID(예: #123) ë˜ëŠ” ë¼ë²¨(예: "@My Computer")ì„ ì§€ì •í•  수 있습니다. +commands.computercraft.dump.usage=[id] +commands.computercraft.dump.action=ì´ ì»´í“¨í„°ì— ëŒ€í•œ 추가 정보를 봅니다. + +commands.computercraft.shutdown.synopsis=ì‹œìŠ¤í…œì„ ì›ê²©ìœ¼ë¡œ 종료하기 +commands.computercraft.shutdown.desc=ë‚˜ì—´ëœ ì‹œìŠ¤í…œ ë˜ëŠ” ì§€ì •ëœ ì‹œìŠ¤í…œì´ ì—†ëŠ” 경우 ëª¨ë‘ ì¢…ë£Œí•©ë‹ˆë‹¤. ì»´í“¨í„°ì˜ ì¸ìŠ¤í„´ìŠ¤ ID(예: 123)나 컴퓨터 ID(예: #123) ë˜ëŠ” ë¼ë²¨(예: "@My Computer")ì„ ì§€ì •í•  수 있습니다. +commands.computercraft.shutdown.usage=[ids...] +commands.computercraft.shutdown.done=%s/%s 컴퓨터 시스템 종료 + +commands.computercraft.turn_on.synopsis=ì‹œìŠ¤í…œì„ ì›ê²©ìœ¼ë¡œ 실행하기 +commands.computercraft.turn_on.desc=ë‚˜ì—´ëœ ì»´í“¨í„°ë¥¼ 실행합니다. ì»´í“¨í„°ì˜ ì¸ìŠ¤í„´ìŠ¤ ID(예: 123)나 컴퓨터 ID(예: #123) ë˜ëŠ” ë¼ë²¨(예: "@My Computer")ì„ ì§€ì •í•  수 있습니다. +commands.computercraft.turn_on.usage=[ids...] +commands.computercraft.turn_on.done=%s/%s 컴퓨터 시스템 실행 + +commands.computercraft.tp.synopsis=특정 컴퓨터로 순간ì´ë™í•˜ê¸° +commands.computercraft.tp.desc=ì»´í“¨í„°ì˜ ìœ„ì¹˜ë¡œ 순간ì´ë™í•©ë‹ˆë‹¤. ì»´í“¨í„°ì˜ ì¸ìŠ¤í„´ìŠ¤ ID(예: 123) ë˜ëŠ” 컴퓨터 ID(예: #123)를 지정할 수 있습니다. +commands.computercraft.tp.usage= +commands.computercraft.tp.action=ì´ ì»´í“¨í„°ë¡œ 순간ì´ë™í•˜ê¸° +commands.computercraft.tp.not_entity=비플레ì´ì–´í•œí…Œ 터미ë„ì„ ì—´ 수 없습니다. +commands.computercraft.tp.not_there=월드ì—ì„œ 컴퓨터를 위치시킬 수 없습니다. + +commands.computercraft.view.synopsis=ì»´í“¨í„°ì˜ í„°ë¯¸ë„ì„ ë³´ê¸° +commands.computercraft.view.desc=ì»´í“¨í„°ì˜ ì›ê²© 제어를 허용하는 ì»´í“¨í„°ì˜ í„°ë¯¸ë„ì„ ì—½ë‹ˆë‹¤. ì´ê²ƒì€ í„°í‹€ì˜ ì¸ë²¤í† ë¦¬ì— 대한 ì ‘ê·¼ì„ ì œê³µí•˜ì§€ 않습니다. ì»´í“¨í„°ì˜ ì¸ìŠ¤í„´ìŠ¤ ID(예: 123) ë˜ëŠ” 컴퓨터 ID(예: #123)를 지정할 수 있습니다. +commands.computercraft.view.usage= +commands.computercraft.view.action=ì´ ì»´í“¨í„°ë¥¼ 봅니다. +commands.computercraft.view.not_player=비플레ì´ì–´í•œí…Œ 터미ë„ì„ ì—´ 수 없습니다. + +commands.computercraft.track.synopsis=ì»´í“¨í„°ì˜ ì‹¤í–‰ ì‹œê°„ì„ ì¶”ì í•˜ê¸° +commands.computercraft.track.desc=컴퓨터가 실행ë˜ëŠ” 기간과 처리ë˜ëŠ” ì´ë²¤íŠ¸ 수를 추ì í•©ë‹ˆë‹¤. ì´ëŠ” /forge 트랙과 유사한 방법으로 정보를 제공하며 지연 ë¡œê·¸ì— ìœ ìš©í•  수 있습니다. + +commands.computercraft.track.start.synopsis=모든 ì»´í“¨í„°ì˜ ì¶”ì ì„ 시작하기 +commands.computercraft.track.start.desc=모든 ì»´í“¨í„°ì˜ ì´ë²¤íŠ¸ ë° ì‹¤í–‰ 시간 추ì ì„ 시작합니다. ì´ëŠ” ì´ì „ ì‹¤í–‰ì˜ ê²°ê³¼ë¥¼ í기할 것입니다. +commands.computercraft.track.start.usage= +commands.computercraft.track.start.stop=%sì„(를) 실행하여 추ì ì„ 중지하고 결과를 확ì¸í•©ë‹ˆë‹¤. + +commands.computercraft.track.stop.synopsis=모든 ì»´í“¨í„°ì˜ ì¶”ì ì„ 중지하기 +commands.computercraft.track.stop.desc=모든 ì»´í“¨í„°ì˜ ì´ë²¤íŠ¸ ë° ì‹¤í–‰ 시간 추ì ì„ 중지합니다. +commands.computercraft.track.stop.usage= +commands.computercraft.track.stop.action=추ì ì„ 중지하려면 í´ë¦­í•˜ì„¸ìš”. +commands.computercraft.track.stop.not_enabled=현재 추ì í•˜ëŠ” 컴퓨터가 없습니다. + +commands.computercraft.track.dump.synopsis=최신 ì¶”ì  ê²°ê³¼ë¥¼ ë¤í”„하기 +commands.computercraft.track.dump.desc=최신 컴퓨터 추ì ì˜ 결과를 ë¤í”„합니다. +commands.computercraft.track.dump.usage=[kind] +commands.computercraft.track.dump.no_timings=사용가능한 ì‹œê°„ì´ ì—†ìŠµë‹ˆë‹¤. +commands.computercraft.track.dump.no_field=ì•Œ 수 없는 í•„ë“œ '%s' +commands.computercraft.track.dump.computer=컴퓨터 + +commands.computercraft.reload.synopsis=컴퓨터í¬ëž˜í”„트 구성파ì¼ì„ 리로드하기 +commands.computercraft.reload.desc=컴퓨터í¬ëž˜í”„트 구성파ì¼ì„ 리로드합니다. +commands.computercraft.reload.usage= +commands.computercraft.reload.done=ë¦¬ë¡œë“œëœ êµ¬ì„± + +commands.computercraft.queue.synopsis=computer_command ì´ë²¤íŠ¸ë¥¼ 명령 ì»´í“¨í„°ì— ë³´ë‚´ê¸° +commands.computercraft.queue.desc=computer_command ì´ë²¤íŠ¸ë¥¼ 명령 컴퓨터로 전송하여 추가 ì¸ìˆ˜ë¥¼ 전달합니다. ì´ëŠ” 대부분 ì§€ë„ ì œìž‘ìžë¥¼ 위해 설계ë˜ì—ˆìœ¼ë©°, 보다 컴퓨터 친화ì ì¸ ë²„ì „ì˜ /trigger ì—­í• ì„ í•©ë‹ˆë‹¤. ì–´ë–¤ 플레ì´ì–´ë“  ëª…ë ¹ì„ ì‹¤í–‰í•  수 있으며, ì´ëŠ” í…스트 구성 ìš”ì†Œì˜ í´ë¦­ ì´ë²¤íŠ¸ë¥¼ 통해 ìˆ˜í–‰ë  ê°€ëŠ¥ì„±ì´ ê°€ìž¥ 높습니다. +commands.computercraft.queue.usage= [args...] + +commands.computercraft.generic.no_position= +commands.computercraft.generic.position=%s, %s, %s +commands.computercraft.generic.yes=Y +commands.computercraft.generic.no=N +commands.computercraft.generic.exception=처리ë˜ì§€ ì•Šì€ ì˜ˆì™¸ (%s) +commands.computercraft.generic.additional_rows=%dê°œì˜ ì¶”ê°€ í–‰... + +commands.computercraft.argument.no_matching='%s'와 ì¼ì¹˜í•˜ëŠ” 컴퓨터가 없습니다. +commands.computercraft.argument.many_matching='%s'와 ì¼ì¹˜í•˜ëŠ” 여러 컴퓨터 (ì¸ìŠ¤í„´ìŠ¤ %s) +commands.computercraft.argument.not_number='%s'는 숫ìžê°€ 아닙니다. + +# Names for the various tracking fields. +tracking_field.computercraft.tasks.name=ìž‘ì—… +tracking_field.computercraft.total.name=ì „ì²´ 시간 +tracking_field.computercraft.average.name=í‰ê·  시간 +tracking_field.computercraft.max.name=최대 시간 + +tracking_field.computercraft.server_count.name=서버 ìž‘ì—… 수 +tracking_field.computercraft.server_time.name=서버 ìž‘ì—… 시간 + +tracking_field.computercraft.peripheral.name=주변 호출 +tracking_field.computercraft.fs.name=파ì¼ì‹œìŠ¤í…œ ìž‘ì—… +tracking_field.computercraft.turtle.name=í„°í‹€ ìž‘ì—… + +tracking_field.computercraft.http.name=HTTP 요청 +tracking_field.computercraft.http_upload.name=HTTP 업로드 +tracking_field.computercraft.http_download.name=HTTT 다운로드 + +tracking_field.computercraft.websocket_incoming.name=웹소켓 수신 +tracking_field.computercraft.websocket_outgoing.name=웹소켓 송신 + +tracking_field.computercraft.coroutines_created.name=코루틴 ìƒì„±ë¨ +tracking_field.computercraft.coroutines_dead.name=코루틴 ì²˜ë¦¬ë¨ + +# Misc tooltips +gui.computercraft.tooltip.copy=í´ë¦½ë³´ë“œì— 복사 +gui.computercraft.tooltip.computer_id=(컴퓨터 ID: %s) +gui.computercraft.tooltip.disk_id=(ë””ìŠ¤í¬ ID: %s) + +# Config options +gui.computercraft:config.computer_space_limit=컴퓨터 공간 제한 (ë°”ì´íŠ¸) +gui.computercraft:config.floppy_space_limit=플로피 ë””ìŠ¤í¬ ê³µê°„ 제한 (ë°”ì´íŠ¸) +gui.computercraft:config.maximum_open_files=컴퓨터당 최대 íŒŒì¼ ì—´ê¸° +gui.computercraft:config.disable_lua51_features=Lua 5.1 기능 미사용 +gui.computercraft:config.default_computer_settings=기본 컴퓨터 설정 +gui.computercraft:config.debug_enabled=디버그 ë¼ì´ë¸ŒëŸ¬ë¦¬ 사용 +gui.computercraft:config.log_computer_errors=컴퓨터 오류 로그 + +gui.computercraft:config.execution=실행 +gui.computercraft:config.execution.computer_threads=컴퓨터 쓰레드 +gui.computercraft:config.execution.max_main_global_time=ì „ì—­ 시간 당 서버 제한 +gui.computercraft:config.execution.max_main_computer_time=컴퓨터 시간 당 서버 제한 + +gui.computercraft:config.http=HTTP +gui.computercraft:config.http.enabled=HTTP API 사용하기 +gui.computercraft:config.http.websocket_enabled=웹소켓 사용 +gui.computercraft:config.http.allowed_domains=í—ˆìš©ëœ ë„ë©”ì¸ +gui.computercraft:config.http.blocked_domains=ì°¨ë‹¨ëœ ë„ë©”ì¸ + +gui.computercraft:config.http.timeout=타임아웃 +gui.computercraft:config.http.max_requests=최대 ë™ì‹œ 요청 수 +gui.computercraft:config.http.max_download=최대 ì‘답 í¬ê¸° +gui.computercraft:config.http.max_upload=최대 요청 í¬ê¸° +gui.computercraft:config.http.max_websockets=최대 ë™ì‹œ 웹소켓 수 +gui.computercraft:config.http.max_websocket_message=최대 웹 í¬ì¼“ 메시지 í¬ê¸° + +gui.computercraft:config.peripheral=주변 +gui.computercraft:config.peripheral.command_block_enabled=명령 ë¸”ë¡ ì£¼ë³€ 장치 사용 +gui.computercraft:config.peripheral.modem_range=모뎀 범위(기본값) +gui.computercraft:config.peripheral.modem_high_altitude_range=모뎀 범위(ë†’ì€ ê³ ë„) +gui.computercraft:config.peripheral.modem_range_during_storm=모뎀 범위(ë‚˜ìœ ë‚ ì”¨) +gui.computercraft:config.peripheral.modem_high_altitude_range_during_storm=모뎀 범위(ë†’ì€ ê³ ë„, ë‚˜ìœ ë‚ ì”¨) +gui.computercraft:config.peripheral.max_notes_per_tick=컴퓨터가 í•œ ë²ˆì— ìž¬ìƒí•  수 있는 최대 소리 수 + +gui.computercraft:config.turtle=í„°í‹€ +gui.computercraft:config.turtle.need_fuel=연료 사용 +gui.computercraft:config.turtle.normal_fuel_limit=í„°í‹€ 연료 제한 +gui.computercraft:config.turtle.advanced_fuel_limit=고급 í„°í‹€ 연료 제한 +gui.computercraft:config.turtle.obey_block_protection=í„°í‹€ì´ ë¸”ë¡ ë³´í˜¸ì— ë”°ë¥´ê¸° +gui.computercraft:config.turtle.can_push=í„°í‹€ì´ ì—”í‹°í‹° 밀어내기 +gui.computercraft:config.turtle.disabled_actions=í„°í‹€ ì•¡ì…˜ 미사용 From ef8da8054f9b774d5f85b06c9a3f76de8be59b17 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 10 Apr 2020 10:27:53 +0100 Subject: [PATCH 17/35] An initial stab at documentation generation (#360) This adds documentation comments to many of CC's Lua APIs, and a couple of the Java ones, through the use of stubs. We then export these to HTML using illuaminate [1] and upload them to our documentation site [2]. Uploads currently occur on pushes to master and any release/tag. The site is entirely static - there is no way to switch between versions, etc... but hopefully we can improve this in the future. [1]: github.com/SquidDev/illuaminate/ [2]: https://tweaked.cc/ --- .editorconfig | 4 + .github/workflows/make-doc.sh | 16 ++ .github/workflows/make-doc.yml | 29 ++ .gitignore | 1 + README.md | 2 +- doc/index.md | 13 + logo.png => doc/logo.png | Bin doc/stub/commands.lua | 6 + doc/stub/fs.lua | 42 +++ doc/stub/http.lua | 213 +++++++++++++++ doc/stub/os.lua | 17 ++ doc/stub/redstone.lua | 14 + doc/stub/term.lua | 52 ++++ doc/stub/turtle.lua | 42 +++ doc/styles.css | 186 +++++++++++++ illuaminate.sexp | 32 ++- .../assets/computercraft/lua/bios.lua | 2 +- .../computercraft/lua/rom/apis/colors.lua | 173 ++++++++++-- .../computercraft/lua/rom/apis/colours.lua | 18 +- .../lua/rom/apis/command/commands.lua | 21 +- .../computercraft/lua/rom/apis/disk.lua | 86 +++++- .../assets/computercraft/lua/rom/apis/gps.lua | 34 +++ .../computercraft/lua/rom/apis/help.lua | 30 +++ .../assets/computercraft/lua/rom/apis/io.lua | 213 +++++++++++---- .../computercraft/lua/rom/apis/keys.lua | 31 ++- .../computercraft/lua/rom/apis/paintutils.lua | 112 ++++++-- .../computercraft/lua/rom/apis/parallel.lua | 27 +- .../computercraft/lua/rom/apis/peripheral.lua | 206 ++++++++++----- .../computercraft/lua/rom/apis/rednet.lua | 165 ++++++++++-- .../computercraft/lua/rom/apis/settings.lua | 75 +++++- .../computercraft/lua/rom/apis/term.lua | 48 +++- .../computercraft/lua/rom/apis/textutils.lua | 142 +++++++++- .../lua/rom/apis/turtle/turtle.lua | 3 + .../computercraft/lua/rom/apis/vector.lua | 247 ++++++++++++------ .../computercraft/lua/rom/apis/window.lua | 51 +++- .../lua/rom/modules/main/cc/expect.lua | 2 +- .../lua/rom/modules/main/cc/pretty.lua | 17 +- src/test/resources/test-rom/mcfly.lua | 56 ++-- .../test-rom/spec/modules/cc/pretty_spec.lua | 2 +- 39 files changed, 2094 insertions(+), 336 deletions(-) create mode 100755 .github/workflows/make-doc.sh create mode 100644 .github/workflows/make-doc.yml create mode 100644 doc/index.md rename logo.png => doc/logo.png (100%) create mode 100644 doc/stub/commands.lua create mode 100644 doc/stub/fs.lua create mode 100644 doc/stub/http.lua create mode 100644 doc/stub/os.lua create mode 100644 doc/stub/redstone.lua create mode 100644 doc/stub/term.lua create mode 100644 doc/stub/turtle.lua create mode 100644 doc/styles.css diff --git a/.editorconfig b/.editorconfig index f11468850..d57b269a4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,5 +14,9 @@ trim_trailing_whitespace = false [*.sexp] indent_size = 2 +[*.yml] +indent_size = 2 + + [*.properties] insert_final_newline = false diff --git a/.github/workflows/make-doc.sh b/.github/workflows/make-doc.sh new file mode 100755 index 000000000..e447c3046 --- /dev/null +++ b/.github/workflows/make-doc.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -eu + +DEST="${GITHUB_REF#refs/*/}" +echo "Uploading docs to https://tweaked.cc/$DEST" + +# Setup ssh key +mkdir -p "$HOME/.ssh/" +echo "$SSH_KEY" > "$HOME/.ssh/key" +chmod 600 "$HOME/.ssh/key" + +# And upload +rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \ + "$GITHUB_WORKSPACE/doc/" \ + "$SSH_USER@$SSH_HOST:/var/www/tweaked.cc/$DEST" diff --git a/.github/workflows/make-doc.yml b/.github/workflows/make-doc.yml new file mode 100644 index 000000000..1d5e4605d --- /dev/null +++ b/.github/workflows/make-doc.yml @@ -0,0 +1,29 @@ +name: Build documentation + +on: + push: + branches: [ master ] + tags: + +jobs: + make_doc: + name: Build + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + + - name: Build documentation + run: | + test -d bin || mkdir bin + test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate + chmod +x bin/illuaminate + bin/illuaminate doc-gen + + - name: Upload documentation + run: .github/workflows/make-doc.sh 2> /dev/null + env: + SSH_KEY: ${{ secrets.SSH_KEY }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_HOST: ${{ secrets.SSH_HOST }} + SSH_PORT: ${{ secrets.SSH_PORT }} diff --git a/.gitignore b/.gitignore index 80c50f9b8..cc7ddcea3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /logs /build /out +/doc/**/*.html # Runtime directories /run diff --git a/README.md b/README.md index 692e7bb48..fd3003786 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![CC: Tweaked](logo.png) +# ![CC: Tweaked](doc/logo.png) [![Current build status](https://github.com/SquidDev-CC/CC-Tweaked/workflows/Build/badge.svg)](https://github.com/SquidDev-CC/CC-Tweaked/actions "Current build status") [![Download CC: Tweaked on CurseForge](http://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge") CC: Tweaked is a fork of [ComputerCraft](https://github.com/dan200/ComputerCraft), adding programmable computers, diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 000000000..efc2afafa --- /dev/null +++ b/doc/index.md @@ -0,0 +1,13 @@ +# CC: Tweaked + +This is a small website to test documentation generation from Lua source code. + +This is still very much in the proof-of-concept stage. We've rolled own own documentation +generation tool, and there's a couple of missing features. Furthermore, Java-based APIs +(such as Lua builtins, or @{os}) are not documented. + +For more information, please check out [the GitHub issue][gh_issue] and [the +documented source][gh_branch]. + +[gh_issue]: https://github.com/SquidDev-CC/CC-Tweaked/issues/133 +[gh_branch]: https://github.com/SquidDev-CC/CC-Tweaked/tree/feature/doc-gen diff --git a/logo.png b/doc/logo.png similarity index 100% rename from logo.png rename to doc/logo.png diff --git a/doc/stub/commands.lua b/doc/stub/commands.lua new file mode 100644 index 000000000..89b0f604b --- /dev/null +++ b/doc/stub/commands.lua @@ -0,0 +1,6 @@ +function exec(command) end +function execAsync(commad) end +function list() end +function getBlockPosition() end +function getBlockInfos(min_x, min_y, min_z, max_x, max_y, max_z) end +function getBlockInfo(x, y, z) end diff --git a/doc/stub/fs.lua b/doc/stub/fs.lua new file mode 100644 index 000000000..73d578e01 --- /dev/null +++ b/doc/stub/fs.lua @@ -0,0 +1,42 @@ +--- The FS API allows you to manipulate files and the filesystem. +-- +-- @module fs + +function list(path) end +function combine(base, child) end +function getName(path) end +function getSize(path) end +function exists(path) end +function isDir(path) end +function isReadOnly(path) end +function makeDir(path) end +function move(from, to) end +function copy(from, to) end +function delete(path) end +function open(path, mode) end +function getDrive(path) end +function getFreeSpace(path) end +function find(pattern) end +function getDir(path) end + +--- A file handle which can be read from. +-- +-- @type ReadHandle +-- @see fs.open +local ReadHandle = {} +function ReadHandle.read(count) end +function ReadHandle.readAll() end +function ReadHandle.readLine(with_trailing) end +function ReadHandle.seek(whence, offset) end +function ReadHandle.close() end + +--- A file handle which can be written to. +-- +-- @type WriteHandle +-- @see fs.open +local WriteHandle = {} +function WriteHandle.write(text) end +function WriteHandle.writeLine(text) end +function WriteHandle.flush(text) end +function WriteHandle.seek(whence, offset) end +function WriteHandle.close() end diff --git a/doc/stub/http.lua b/doc/stub/http.lua new file mode 100644 index 000000000..8e7df608f --- /dev/null +++ b/doc/stub/http.lua @@ -0,0 +1,213 @@ +--- The http library allows communicating with web servers, sending and +-- receiving data from them. +-- +-- #### `http_check` event +-- +-- @module http + +--- Asynchronously make a HTTP request to the given url. +-- +-- This returns immediately, a [`http_success`](#http-success-event) or +-- [`http_failure`](#http-failure-event) will be queued once the request has +-- completed. +-- +-- @tparam string url The url to request +-- @tparam[opt] string body An optional string containing the body of the +-- request. If specified, a `POST` request will be made instead. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of this request. +-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true, +-- the body will not be UTF-8 encoded, and the received response will not be +-- decoded. +-- +-- @tparam[2] { +-- url = string, body? = string, headers? = { [string] = string }, +-- binary? = boolean, method? = string, redirect? = boolean, +-- } request Options for the request. +-- +-- This table form is an expanded version of the previous syntax. All arguments +-- from above are passed in as fields instead (for instance, +-- `http.request("https://example.com")` becomes `http.request { url = +-- "https://example.com" }`). +-- +-- This table also accepts several additional options: +-- +-- - `method`: Which HTTP method to use, for instance `"PATCH"` or `"DELETE"`. +-- - `redirect`: Whether to follow HTTP redirects. Defaults to true. +-- +-- @see http.get For a synchronous way to make GET requests. +-- @see http.post For a synchronous way to make POST requests. +function request(...) end + +--- Make a HTTP GET request to the given url. +-- +-- @tparam string url The url to request +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of this request. +-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true, +-- the body will not be UTF-8 encoded, and the received response will not be +-- decoded. +-- +-- @tparam[2] { +-- url = string, headers? = { [string] = string }, +-- binary? = boolean, method? = string, redirect? = boolean, +-- } request Options for the request. See @{http.request} for details on how +-- these options behave. +-- +-- @treturn Response The resulting http response, which can be read from. +-- @treturn[2] nil When the http request failed, such as in the event of a 404 +-- error or connection timeout. +-- @treturn string A message detailing why the request failed. +-- @treturn Response|nil The failing http response, if available. +-- +-- @usage Make a request to [example.computercraft.cc](https://example.computercraft.cc), +-- and print the returned page. +-- ```lua +-- local request = http.get("https://example.computercraft.cc") +-- print(request.readAll()) +-- -- => HTTP is working! +-- request.close() +-- ``` +function get(...) end + +--- Make a HTTP POST request to the given url. +-- +-- @tparam string url The url to request +-- @tparam string body The body of the POST request. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of this request. +-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true, +-- the body will not be UTF-8 encoded, and the received response will not be +-- decoded. +-- +-- @tparam[2] { +-- url = string, body? = string, headers? = { [string] = string }, +-- binary? = boolean, method? = string, redirect? = boolean, +-- } request Options for the request. See @{http.request} for details on how +-- these options behave. +-- +-- @treturn Response The resulting http response, which can be read from. +-- @treturn[2] nil When the http request failed, such as in the event of a 404 +-- error or connection timeout. +-- @treturn string A message detailing why the request failed. +-- @treturn Response|nil The failing http response, if available. +function post(...) end + +--- A http response. This acts very much like a @{fs.ReadHandle|file}, though +-- provides some http specific methods. +-- +-- #### `http_success` event +-- #### `http_failure` event +-- +-- @type Response +-- @see http.request On how to make a http request. +local Response = {} + +--- Returns the response code and response message returned by the server +-- +-- @treturn number The response code (i.e. 200) +-- @treturn string The response message (i.e. "OK") +function Response.getResponseCode() end + +--- Get a table containing the response's headers, in a format similar to that +-- required by @{http.request}. If multiple headers are sent with the same +-- name, they will be combined with a comma. +-- +-- @treturn { [string]=string } The response's headers. +-- Make a request to [example.computercraft.cc](https://example.computercraft.cc), +-- and print the returned headers. +-- ```lua +-- local request = http.get("https://example.computercraft.cc") +-- print(textutils.serialize(request.getResponseHeaders())) +-- -- => { +-- -- [ "Content-Type" ] = "text/plain; charset=utf8", +-- -- [ "content-length" ] = 17, +-- -- ... +-- -- } +-- request.close() +-- ``` +function Response.getResponseHeaders() end + +function Response.read(count) end +function Response.readAll() end +function Response.readLine(with_trailing) end +function Response.seek(whence, offset) end +function Response.close() end + +--- Asynchronously determine whether a URL can be requested. +-- +-- If this returns `true`, one should also listen for [`http_check` +-- events](#http-check-event) which will container further information about +-- whether the URL is allowed or not. +-- +-- @tparam string url The URL to check. +-- @treturn true When this url is not invalid. This does not imply that it is +-- allowed - see the comment above. +-- @treturn[2] false When this url is invalid. +-- @treturn string A reason why this URL is not valid (for instance, if it is +-- malformed, or blocked). +-- +-- @see http.checkURL For a synchronous version. +function checkURLAsync(url) end + +--- Determine whether a URL can be requested. +-- +-- If this returns `true`, one should also listen for [`http_check` +-- events](#http-check-event) which will container further information about +-- whether the URL is allowed or not. +-- +-- @tparam string url The URL to check. +-- @treturn true When this url is valid and can be requested via @{http.request}. +-- @treturn[2] false When this url is invalid. +-- @treturn string A reason why this URL is not valid (for instance, if it is +-- malformed, or blocked). +-- +-- @see http.checkURLAsync For an asynchronous version. +-- +-- @usage +-- ```lua +-- print(http.checkURL("https://example.computercraft.cc/")) +-- -- => true +-- print(http.checkURL("http://localhost/")) +-- -- => false Domain not permitted +-- print(http.checkURL("not a url")) +-- -- => false URL malformed +-- ``` +function checkURL(url) end + +--- Open a websocket. +-- +-- @tparam string url The websocket url to connect to. This should have the +-- `ws://` or `wss://` protocol. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of the initial websocket connection. +-- +-- @treturn Websocket The websocket connection. +-- @treturn[2] false If the websocket connection failed. +-- @treturn string An error message describing why the connection failed. +function websocket(url, headers) end + +--- Asynchronously open a websocket. +-- +-- This returns immediately, a [`websocket_success`](#websocket-success-event) +-- or [`websocket_failure`](#websocket-failure-event) will be queued once the +-- request has completed. +-- +-- @tparam string url The websocket url to connect to. This should have the +-- `ws://` or `wss://` protocol. +-- @tparam[opt] { [string] = string } headers Additional headers to send as part +-- of the initial websocket connection. +function websocketAsync(url, headers) end + + + +--- A websocket, which can be used to send an receive messages with a web +-- server. +-- +-- @type Websocket +-- @see http.websocket On how to open a websocket. +local Websocket = {} + +function Websocket.send(message, binary) end +function Websocket.receive() end +function Websocket.close() end diff --git a/doc/stub/os.lua b/doc/stub/os.lua new file mode 100644 index 000000000..163d2c2b0 --- /dev/null +++ b/doc/stub/os.lua @@ -0,0 +1,17 @@ +function queueEvent(event, ...) end +function startTimer(delay) end +function setAlarm(time) end +function shutdown() end +function reboot() end +function getComputerID() end +computerID = getComputerID +function setComputerLabel(label) end +function getComputerLabel() end +computerLabel = getComputerLabel +function clock() end +function time(timezone) end +function day(timezone) end +function cancelTimer(id) end +function cancelAlarm(id) end +function epoch(timezone) end +function date(format, time) end diff --git a/doc/stub/redstone.lua b/doc/stub/redstone.lua new file mode 100644 index 000000000..cf10a45ca --- /dev/null +++ b/doc/stub/redstone.lua @@ -0,0 +1,14 @@ +function getSides() end +function setOutput(side, on) end +function getOutput(side) end +function getInput(side) end +function setBundledOutput(side, output) end +function getBundledOutput(side) end +function getBundledInput(side) end +function testBundledInput(side, mask) end +function setAnalogOutput(side, value) end +setAnalogueOutput = setAnalogOutput +function getAnalogOutput(sid) end +getAnalogueOutput = getAnalogOutput +function getAnalogInput(side) end +getAnalogueInput = getAnaloguInput diff --git a/doc/stub/term.lua b/doc/stub/term.lua new file mode 100644 index 000000000..2111949b6 --- /dev/null +++ b/doc/stub/term.lua @@ -0,0 +1,52 @@ +function write(text) end +function scroll(lines) end +function setCursorPos(x, y) end +function setCursorBlink(blink) end +function getCursorPos() end +function getSize() end +function clear() end +function clearLine() end +function setTextColour(colour) end +setTextColor = setTextColour +function setBackgroundColour(colour) end +setBackgroundColor = setBackgroundColour +function isColour() end +isColor = isColour +function getTextColour() end +getTextColor = getTextColor +function getBackgroundColour() end +getBackgroundColour = getBackgroundColour +function blit(text, text_colours, background_colours) end +function setPaletteColour(colour, ...) end +setPaletteColour = setPaletteColour +function getPaletteColour(colour, ...) end +getPaletteColour = getPaletteColour +function nativePaletteColour(colour) end +nativePaletteColour = nativePaletteColour + +--- @type Redirect +local Redirect = {} + +Redirect.write = write +Redirect.scroll = scroll +Redirect.setCursorPos = setCursorPos +Redirect.setCursorBlink = setCursorBlink +Redirect.getCursorPos = getCursorPos +Redirect.getSize = getSize +Redirect.clear = clear +Redirect.clearLine = clearLine +Redirect.setTextColour = setTextColour +Redirect.setTextColor = setTextColor +Redirect.setBackgroundColour = setBackgroundColour +Redirect.setBackgroundColor = setBackgroundColor +Redirect.isColour = isColour +Redirect.isColor = isColor +Redirect.getTextColour = getTextColour +Redirect.getTextColor = getTextColor +Redirect.getBackgroundColour = getBackgroundColour +Redirect.getBackgroundColor = getBackgroundColor +Redirect.blit = blit +Redirect.setPaletteColour = setPaletteColour +Redirect.setPaletteColor = setPaletteColor +Redirect.getPaletteColour = getPaletteColour +Redirect.getPaletteColor = getPaletteColor diff --git a/doc/stub/turtle.lua b/doc/stub/turtle.lua new file mode 100644 index 000000000..0baa4c6fa --- /dev/null +++ b/doc/stub/turtle.lua @@ -0,0 +1,42 @@ +function forward() end +function back() end +function up() end +function down() end +function turnLeft() end +function turnRight() end +function dig(side) end +function digUp(side) end +function digDown(side) end +function place() end +function placeUp() end +function placeDown() end +function drop(count) end +function select(slot) end +function getItemCount(slot) end +function getItemSpace(slot) end +function detect() end +function detectUp() end +function detectDown() end +function compare() end +function compareUp() end +function compareDown() end +function attack(side) end +function attackUp(side) end +function attackDown(side) end +function dropUp(count) end +function dropDown(count) end +function suck(count) end +function suckUp(count) end +function suckDown(count) end +function getFuelLevel() end +function refuel(count) end +function compareTo(slot) end +function transferTo(slot, count) end +function getSelectedSlot() end +function getFuelLimit() end +function equipLeft() end +function equipRight() end +function inspect() end +function inspectUp() end +function inspectDown() end +function getItemDetail(slot) end diff --git a/doc/styles.css b/doc/styles.css new file mode 100644 index 000000000..ef14a7d94 --- /dev/null +++ b/doc/styles.css @@ -0,0 +1,186 @@ +/* Basic reset on elements */ +h1, h2, h3, h4, p, table, div, body { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* Make the page a little more airy */ +body { + margin: 20px auto; + max-width: 1200px; + padding: 0 10px; + line-height: 1.6; + color: #222; + background: #fff; +} + +/* Try to use system default fonts. */ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", + "Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; +} + +code, pre, .parameter, .type, .definition-name, .reference { + font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; +} + +/* Some definitions of basic tags */ +code { + color: #c7254e; + background-color: #f9f2f4; +} + +p { + margin: 0.9em 0; +} + +h1 { + font-size: 1.5em; + font-weight: lighter; + border-bottom: solid 1px #aaa; +} + +h2, h3, h4 { margin: 1.4em 0 0.3em;} +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; font-weight: bold; } +h4 { font-size: 1.06em; } + +a, a:visited, a:active { font-weight: bold; color: #004080; text-decoration: none; } +a:hover { text-decoration: underline; } + +blockquote { margin-left: 3em; } + +/* Stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* Make the target distinct; helps when we're navigating to a function */ +a:target + * { background-color: #FFFF99; } + +/* Allow linking to any subsection */ +a[name]::before { content: "#"; } + +/* Layout */ +#main { + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + min-height: calc(100vh - 100px); +} + +#main > nav { + flex-basis: 30%; + min-width: 150px; + max-width: 250px; + background-color: #f0f0f0; +} + +nav h1, nav ul { padding: 0em 10px; } + +nav h2 { + background-color:#e7e7e7; + font-size: 1.1em; + color:#000000; + padding: 5px 10px; +} + +nav ul { + list-style-type: none; + margin: 0; +} + +#content { + flex-shrink: 1; + flex-basis: 80%; + padding: 0px 10px; +} + +footer { + text-align: right; + font-size: 0.8em; +} + +/* The definition lists at the top of each page */ +table.definition-list { + border-collapse: collapse; + width: 100%; +} + +table.definition-list td, table.definition-list th { + border: 1px solid #cccccc; + padding: 5px; +} + +table.definition-list th { + background-color: #f0f0f0; + min-width: 200px; + white-space: nowrap; + text-align: right; +} + +table.definition-list td { width: 100%; } + +dl.definition dt { + border-top: 1px solid #ccc; + padding-top: 1em; + display: flex; +} + +dl.definition dt .definition-name { + padding: 0 0.1em; + margin: 0 0.1em; + flex-grow: 1; +} + + +dl.definition dd { + padding-bottom: 1em; + margin: 10px 0 0 20px; +} + +dl.definition h3 { + font-size: .95em; +} + +/* Links to source-code */ +.source-link { font-size: 0.8em; } +.source-link::before { content: '[' } +.source-link::after { content: ']' } +a.source-link, a.source-link:visited, a.source-link:active { color: #505050; } + +/* Method definitions */ +span.parameter:after { content:":"; padding-left: 0.3em; } +.optional { text-decoration: underline dotted; } + +/** Fancy colour display. */ +.colour-ref { + display: inline-block; + width: 0.8em; + height: 0.8em; + margin: 0.1em 0.1em 0.3em 0.1em; /* Terrrible hack to force vertical alignment. */ + border: solid 1px black; + box-sizing: border-box; + vertical-align: middle; +} + +/* styles for prettification of source */ +.highlight .comment { color: #558817; } +.highlight .constant { color: #a8660d; } +.highlight .escape { color: #844631; } +.highlight .keyword { color: #aa5050; font-weight: bold; } +.highlight .library { color: #0e7c6b; } +.highlight .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +.highlight .string { color: #8080ff; } +.highlight .literal-kw { color: #8080ff; } +.highlight .number { color: #f8660d; } +.highlight .operator { color: #2239a8; font-weight: bold; } +.highlight .preprocessor, pre .prepro { color: #a33243; } +.highlight .global { color: #800080; } +.highlight .user-keyword { color: #800080; } +.highlight .prompt { color: #558817; } +.highlight .url { color: #272fc2; text-decoration: underline; } diff --git a/illuaminate.sexp b/illuaminate.sexp index cda3d0a85..0a8fe626e 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -1,10 +1,28 @@ ; -*- mode: Lisp;-*- (sources + /doc/stub/ /src/main/resources/assets/computercraft/lua/bios.lua /src/main/resources/assets/computercraft/lua/rom/ /src/test/resources/test-rom) + +(doc + (title "CC: Tweaked") + (index doc/index.md) + (source-link https://github.com/SquidDev-CC/CC-Tweaked/blob/${commit}/${path}#L${line}) + + (library-path + /doc/stub/ + + /src/main/resources/assets/computercraft/lua/rom/apis + /src/main/resources/assets/computercraft/lua/rom/apis/command + /src/main/resources/assets/computercraft/lua/rom/apis/turtle + + /src/main/resources/assets/computercraft/lua/rom/modules/main + /src/main/resources/assets/computercraft/lua/rom/modules/command + /src/main/resources/assets/computercraft/lua/rom/modules/turtle)) + (at / (linters ;; It'd be nice to avoid this, but right now there's a lot of instances of @@ -16,9 +34,9 @@ -var:unused-arg ;; Suppress a couple of documentation comments warnings for now. We'll - ;; hopefully be able to remove them in the coming weeks. - -doc:detached-comment -doc:undocumented -doc:undocumented-arg - -doc:unresolved-reference)) + ;; hopefully be able to remove them in the future. + -doc:undocumented -doc:undocumented-arg -doc:unresolved-reference + -var:unresolved-member)) ;; We disable the unused global linter in bios.lua and the APIs. In the future ;; hopefully we'll get illuaminate to handle this. @@ -26,5 +44,9 @@ (/src/main/resources/assets/computercraft/lua/bios.lua /src/main/resources/assets/computercraft/lua/rom/apis/) (linters -var:unused-global) - (lint - (allow-toplevel-global true))) + (lint (allow-toplevel-global true))) + +;; Silence some variable warnings in documentation stubs. +(at /doc/stub + (linters -var:unused-global) + (lint (allow-toplevel-global true))) diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index a7f7a9d21..377255845 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -21,7 +21,7 @@ if _VERSION == "Lua 5.1" then local nativeloadstring = loadstring local nativesetfenv = setfenv - --- Historically load/loadstring would handle the chunk name as if it has + -- Historically load/loadstring would handle the chunk name as if it has -- been prefixed with "=". We emulate that behaviour here. local function prefix(chunkname) if type(chunkname) ~= "string" then return chunkname end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua b/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua index b2e665268..8754b1929 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua @@ -1,23 +1,91 @@ +--- The Colors API allows you to manipulate sets of colors. +-- +-- This is useful in conjunction with Bundled Cables from the RedPower mod, +-- RedNet Cables from the MineFactory Reloaded mod, and colors on Advanced +-- Computers and Advanced Monitors. +-- +-- For the non-American English version just replace @{colors} with @{colours} +-- and it will use the other API, colours which is exactly the same, except in +-- British English (e.g. @{colors.gray} is spelt @{colours.grey}). +-- +-- @see colours +-- @module colors + local expect = dofile("rom/modules/main/cc/expect.lua").expect --- Colors -white = 1 -orange = 2 -magenta = 4 -lightBlue = 8 -yellow = 16 -lime = 32 -pink = 64 -gray = 128 -lightGray = 256 -cyan = 512 -purple = 1024 -blue = 2048 -brown = 4096 -green = 8192 -red = 16384 -black = 32768 +--- White: Written as `0` in paint files and @{term.blit}, has a default +-- terminal colour of #F0F0F0. +white = 0x1 +--- Orange: Written as `1` in paint files and @{term.blit}, has a +-- default terminal colour of #F2B233. +orange = 0x2 + +--- Magenta: Written as `2` in paint files and @{term.blit}, has a +-- default terminal colour of #E57FD8. +magenta = 0x4 + +--- Light blue: Written as `3` in paint files and @{term.blit}, has a +-- default terminal colour of #99B2F2. +lightBlue = 0x8 + +--- Yellow: Written as `4` in paint files and @{term.blit}, has a +-- default terminal colour of #DEDE6C. +yellow = 0x10 + +--- Lime: Written as `5` in paint files and @{term.blit}, has a default +-- terminal colour of #7FCC19. +lime = 0x20 + +--- Pink. Written as `6` in paint files and @{term.blit}, has a default +-- terminal colour of #F2B2CC. +pink = 0x40 + +--- Gray: Written as `7` in paint files and @{term.blit}, has a default +-- terminal colour of #4C4C4C. +gray = 0x80 + +--- Light gray: Written as `8` in paint files and @{term.blit}, has a +-- default terminal colour of #999999. +lightGray = 0x100 + +--- Cyan: Written as `9` in paint files and @{term.blit}, has a default +-- terminal colour of #4C99B2. +cyan = 0x200 + +--- Purple: Written as `a` in paint files and @{term.blit}, has a +-- default terminal colour of #B266E5. +purple = 0x400 + +--- Blue: Written as `b` in paint files and @{term.blit}, has a default +-- terminal colour of #3366CC. +blue = 0x800 + +--- Brown: Written as `c` in paint files and @{term.blit}, has a default +-- terminal colour of #7F664C. +brown = 0x1000 + +--- Green: Written as `d` in paint files and @{term.blit}, has a default +-- terminal colour of #57A64E. +green = 0x2000 + +--- Red: Written as `e` in paint files and @{term.blit}, has a default +-- terminal colour of #CC4C4C. +red = 0x4000 + +--- Black: Written as `f` in paint files and @{term.blit}, has a default +-- terminal colour of #191919. +black = 0x8000 + +--- Combines a set of colors (or sets of colors) into a larger set. +-- +-- @tparam number ... The colors to combine. +-- @treturn number The union of the color sets given in `...` +-- @usage +-- ```lua +-- colors.combine(colors.white, colors.magenta, colours.lightBlue) +-- -- => 13 +-- ``` function combine( ... ) local r = 0 for i = 1, select('#', ...) do @@ -28,6 +96,20 @@ function combine( ... ) return r end +--- Removes one or more colors (or sets of colors) from an initial set. +-- +-- Each parameter beyond the first may be a single color or may be a set of +-- colors (in the latter case, all colors in the set are removed from the +-- original set). +-- +-- @tparam number colors The color from which to subtract. +-- @tparam number ... The colors to subtract. +-- @treturn number The resulting color. +-- @usage +-- ```lua +-- colours.subtract(colours.lime, colours.orange, colours.white) +-- -- => 32 +-- ``` function subtract( colors, ... ) expect(1, colors, "number") local r = colors @@ -39,12 +121,33 @@ function subtract( colors, ... ) return r end +--- Tests whether `color` is contained within `colors`. +-- +-- @tparam number colors A color, or color set +-- @tparam number color A color or set of colors that `colors` should contain. +-- @treturn boolean If `colors` contains all colors within `color`. +-- @usage +-- ```lua +-- colors.test(colors.combine(colors.white, colors.magenta, colours.lightBlue), colors.lightBlue) +-- -- => true +-- ``` function test( colors, color ) expect(1, colors, "number") expect(2, color, "number") return bit32.band(colors, color) == color end +--- Combine a three-colour RGB value into one hexadecimal representation. +-- +-- @tparam number r The red channel, should be between 0 and 1. +-- @tparam number g The red channel, should be between 0 and 1. +-- @tparam number b The blue channel, should be between 0 and 1. +-- @treturn number The combined hexadecimal colour. +-- @usage +-- ```lua +-- colors.rgb(0.7, 0.2, 0.6) +-- -- => 0xb23399 +-- ``` function packRGB( r, g, b ) expect(1, r, "number") expect(2, g, "number") @@ -55,6 +158,18 @@ function packRGB( r, g, b ) bit32.band( b * 255, 0xFF ) end +--- Separate a hexadecimal RGB colour into its three constituent channels. +-- +-- @tparam number rgb The combined hexadecimal colour. +-- @treturn number The red channel, will be between 0 and 1. +-- @treturn number The red channel, will be between 0 and 1. +-- @treturn number The blue channel, will be between 0 and 1. +-- @usage +-- ```lua +-- colors.rgb(0xb23399) +-- -- => 0.7, 0.2, 0.6 +-- ``` +-- @see colors.packRGB function unpackRGB( rgb ) expect(1, rgb, "number") return @@ -63,6 +178,30 @@ function unpackRGB( rgb ) bit32.band( rgb, 0xFF ) / 255 end +--- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many +-- arguments it receives. +-- +-- **Note:** This function is deprecated, and it is recommended you use the +-- specific pack/unpack function directly. +-- +-- @tparam[1] number r The red channel, as an argument to @{colors.packRGB}. +-- @tparam[1] number g The green channel, as an argument to @{colors.packRGB}. +-- @tparam[1] number b The blue channel, as an argument to @{colors.packRGB}. +-- @tparam[2] number rgb The combined hexadecimal color, as an argument to @{colors.unpackRGB}. +-- @treturn[1] number The combined hexadecimal colour, as returned by @{colors.packRGB}. +-- @treturn[2] number The red channel, as returned by @{colors.unpackRGB} +-- @treturn[2] number The green channel, as returned by @{colors.unpackRGB} +-- @treturn[2] number The blue channel, as returned by @{colors.unpackRGB} +-- @usage +-- ```lua +-- colors.rgb(0xb23399) +-- -- => 0.7, 0.2, 0.6 +-- ``` +-- @usage +-- ```lua +-- colors.rgb(0.7, 0.2, 0.6) +-- -- => 0xb23399 +-- ``` function rgb8( r, g, b ) if g == nil and b == nil then return unpackRGB( r ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua b/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua index fa17c6cb3..74f048df7 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/colours.lua @@ -1,11 +1,23 @@ --- Colours (for lovers of british spelling) +--- Colours for lovers of British spelling. +-- +-- @see colors +-- @module colours + local colours = _ENV for k, v in pairs(colors) do colours[k] = v end +--- Grey. Written as `7` in paint files and @{term.blit}, has a default +-- terminal colour of #4C4C4C. +-- +-- @see colors.gray colours.grey = colors.gray -colours.gray = nil +colours.gray = nil --- @local +--- Light grey. Written as `8` in paint files and @{term.blit}, has a +-- default terminal colour of #999999. +-- +-- @see colors.lightGray colours.lightGrey = colors.lightGray -colours.lightGray = nil +colours.lightGray = nil --- @local diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua b/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua index caf86f5cf..d0c02b304 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua @@ -1,7 +1,26 @@ +--- The commands API allows your system to directly execute [Minecraft +-- commands][mc] and gather data from the results. +-- +-- While one may use @{commands.exec} directly to execute a command, the +-- commands API also provides helper methods to execute every command. For +-- instance, `commands.say("Hi!")` is equivalent to `commands.exec("say Hi!")`. +-- +-- @{commands.async} provides a similar interface to execute asynchronous +-- commands. `commands.async.say("Hi!")` is equivalent to +-- `commands.execAsync("Hi!")`. +-- +-- [mc]: https://minecraft.gamepedia.com/Commands +-- +-- @module commands if not commands then - error( "Cannot load command API on normal computer", 2 ) + error( "Cannot load command API on normal computer", 2 ) end + +--- The builtin commands API, without any generated command helper functions +-- +-- This may be useful if a built-in function (such as @{commands.list}) has been +-- overwritten by a command. native = commands.native or commands local function collapseArgs( bJSONIsNBT, ... ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua b/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua index 7a0dbad39..502989008 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua @@ -1,3 +1,15 @@ +--- The Disk API allows you to interact with disk drives. +-- +-- These functions can operate on locally attached or remote disk drives. To use +-- a locally attached drive, specify “side†as one of the six sides +-- (e.g. `left`); to use a remote disk drive, specify its name as printed when +-- enabling its modem (e.g. `drive_0`). +-- +-- **Note:** All computers (except command computers), turtles and pocket +-- computers can be placed within a disk drive to access it's internal storage +-- like a disk. +-- +-- @module disk local function isDrive( name ) if type( name ) ~= "string" then @@ -6,6 +18,11 @@ local function isDrive( name ) return peripheral.getType( name ) == "drive" end +--- Checks whether any item at all is in the disk drive +-- +-- @tparam string name The name of the disk drive. +-- @treturn boolean If something is in the disk drive. +-- @usage disk.isPresent(false) function isPresent( name ) if isDrive( name ) then return peripheral.call( name, "isDiskPresent" ) @@ -13,6 +30,16 @@ function isPresent( name ) return false end +--- Get the label of the floppy disk, record, or other media within the given +-- disk drive. +-- +-- If there is a computer or turtle within the drive, this will set the label as +-- read by `os.getComputerLabel`. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|nil The name of the current media, or `nil` if the drive is +-- not present or empty. +-- @see disk.setLabel function getLabel( name ) if isDrive( name ) then return peripheral.call( name, "getDiskLabel" ) @@ -20,12 +47,23 @@ function getLabel( name ) return nil end +--- Set the label of the floppy disk or other media +-- +-- @tparam string name The name of the disk drive. +-- @tparam string|nil label The new label of the disk function setLabel( name, label ) if isDrive( name ) then peripheral.call( name, "setDiskLabel", label ) end end +--- Check whether the current disk provides a mount. +-- +-- This will return true for disks and computers, but not records. +-- +-- @tparam string name The name of the disk drive. +-- @treturn boolean If the disk is present and provides a mount. +-- @see disk.getMountPath function hasData( name ) if isDrive( name ) then return peripheral.call( name, "hasData" ) @@ -33,6 +71,13 @@ function hasData( name ) return false end +--- Find the directory name on the local computer where the contents of the +-- current floppy disk (or other mount) can be found. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|nil The mount's directory, or `nil` if the drive does not +-- contain a floppy or computer. +-- @see disk.hasData function getMountPath( name ) if isDrive( name ) then return peripheral.call( name, "getMountPath" ) @@ -40,6 +85,15 @@ function getMountPath( name ) return nil end +--- Whether the current disk is a [music disk][disk] as opposed to a floppy disk +-- or other item. +-- +-- If this returns true, you will can @{disk.playAudio|play} the record. +-- +-- [disk]: https://minecraft.gamepedia.com/Music_Disc +-- +-- @tparam string name The name of the disk drive. +-- @treturn boolean If the disk is present and has audio saved on it. function hasAudio( name ) if isDrive( name ) then return peripheral.call( name, "hasAudio" ) @@ -47,6 +101,13 @@ function hasAudio( name ) return false end +--- Get the title of the audio track from the music record in the drive. +-- +-- This generally returns the same as @{disk.getLabel} for records. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|false|nil The track title, `false` if there is not a music +-- record in the drive or `nil` if no drive is present. function getAudioTitle( name ) if isDrive( name ) then return peripheral.call( name, "getAudioTitle" ) @@ -54,12 +115,25 @@ function getAudioTitle( name ) return nil end +--- Starts playing the music record in the drive. +-- +-- If any record is already playing on any disk drive, it stops before the +-- target drive starts playing. The record stops when it reaches the end of the +-- track, when it is removed from the drive, when @{disk.stopAudio} is called, or +-- when another record is started. +-- +-- @tparam string name The name of the disk drive. +-- @usage disk.playAudio("bottom") function playAudio( name ) if isDrive( name ) then peripheral.call( name, "playAudio" ) end end +--- Stops the music record in the drive from playing, if it was started with +-- @{disk.playAudio}. +-- +-- @tparam string name The name o the disk drive. function stopAudio( name ) if not name then for _, sName in ipairs( peripheral.getNames() ) do @@ -72,16 +146,26 @@ function stopAudio( name ) end end +--- Ejects any item currently in the drive, spilling it into the world as a loose item. +-- +-- @tparam string name The name of the disk drive. +-- @usage disk.eject("bottom") function eject( name ) if isDrive( name ) then peripheral.call( name, "ejectDisk" ) end end +--- Returns a number which uniquely identifies the disk in the drive. +-- +-- Note, unlike @{disk.getLabel}, this does not return anything for other media, +-- such as computers or turtles. +-- +-- @tparam string name The name of the disk drive. +-- @treturn string|nil The disk ID, or `nil` if the drive does not contain a floppy disk. function getID( name ) if isDrive( name ) then return peripheral.call( name, "getDiskID" ) end return nil end - diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua b/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua index 79b192494..7a4d03af0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua @@ -1,5 +1,30 @@ +--- The GPS API provides a method for turtles and computers to retrieve their +-- own locations. +-- +-- It broadcasts a PING message over @{rednet} and wait for responses. In order +-- for this system to work, there must be at least 4 computers used as gps hosts +-- which will respond and allow trilateration. Three of these hosts should be in +-- a plane, and the fourth should be either above or below the other three. The +-- three in a plane should not be in a line with each other. You can set up +-- hosts using the gps program. +-- +-- **Note**: When entering in the coordinates for the host you need to put in +-- the `x`, `y`, and `z` coordinates of the computer, not the modem, as all +-- rednet distances are measured from the block the computer is in. +-- +-- Also note that you may choose which axes x, y, or z refers to - so long as +-- your systems have the same definition as any GPS servers that're in range, it +-- works just the same. For example, you might build a GPS cluster according to +-- [this tutorial][1], using z to account for height, or you might use y to +-- account for height in the way that Minecraft's debug screen displays. +-- +-- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/ +-- +-- @module gps + local expect = dofile("rom/modules/main/cc/expect.lua").expect +--- The channel which GPS requests and responses are broadcast on. CHANNEL_GPS = 65534 local function trilaterate( A, B, C ) @@ -56,6 +81,15 @@ local function narrow( p1, p2, fix ) end end +--- Tries to retrieve the computer or turtles own location. +-- +-- @tparam[opt] number timeout The maximum time taken to establish our +-- position. Defaults to 2 seconds if not specified. +-- @tparam[opt] boolean debug Print debugging messages +-- @treturn[1] number This computer's `x` position. +-- @treturn[1] number This computer's `y` position. +-- @treturn[1] number This computer's `z` position. +-- @treturn[2] nil If the position could not be established. function locate( _nTimeout, _bDebug ) expect(1, _nTimeout, "number", "nil") expect(2, _bDebug, "boolean", "nil") diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/help.lua b/src/main/resources/assets/computercraft/lua/rom/apis/help.lua index ded20f2f3..1b3088c4b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/help.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/help.lua @@ -1,16 +1,38 @@ +--- Provides an API to read help files. +-- +-- @module help + local expect = dofile("rom/modules/main/cc/expect.lua").expect local sPath = "/rom/help" +--- Returns a colon-separated list of directories where help files are searched +-- for. All directories are absolute. +-- +-- @treturn string The current help search path, separated by colons. +-- @see help.setPath function path() return sPath end +--- Sets the colon-seperated list of directories where help files are searched +-- for to `newPath` +-- +-- @tparam string newPath The new path to use. +-- @usage help.setPath( "/disk/help/" ) +-- @usage help.setPath( help.path() .. ":/myfolder/help/" ) +-- @see help.path function setPath( _sPath ) expect(1, _sPath, "string") sPath = _sPath end +--- Returns the location of the help file for the given topic. +-- +-- @tparam string topic The topic to find +-- @treturn string|nil The path to the given topic's help file, or `nil` if it +-- cannot be found. +-- @usage print(help.lookup("disk")) function lookup( _sTopic ) expect(1, _sTopic, "string") -- Look on the path variable @@ -27,6 +49,9 @@ function lookup( _sTopic ) return nil end +--- Returns a list of topics that can be looked up and/or displayed. +-- +-- @treturn table A list of topics in alphabetical order. function topics() -- Add index local tItems = { @@ -59,6 +84,11 @@ function topics() return tItemList end +--- Returns a list of topic endings that match the prefix. Can be used with +-- `read` to allow input of a help topic. +-- +-- @tparam string prefix The prefix to match +-- @treturn table A list of matching topics. function completeTopic( sText ) expect(1, sText, "string") local tTopics = topics() diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/io.lua b/src/main/resources/assets/computercraft/lua/rom/apis/io.lua index 3cf28c80b..8079e022f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/io.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/io.lua @@ -1,6 +1,10 @@ --- Definition for the IO API +--- Emulates Lua's standard [io library][io]. +-- +-- [io]: https://www.lua.org/manual/5.1/manual.html#5.7 +-- +-- @module io -local expect, typeOf = dofile("rom/modules/main/cc/expect.lua").expect, _G.type +local expect, type_of = dofile("rom/modules/main/cc/expect.lua").expect, _G.type --- If we return nil then close the file, as we've reached the end. -- We use this weird wrapper function as we wish to preserve the varargs @@ -9,6 +13,9 @@ local function checkResult(handle, ...) return ... end +--- A file handle which can be read or written to. +-- +-- @type Handle local handleMetatable handleMetatable = { __name = "FILE*", @@ -20,10 +27,17 @@ handleMetatable = { return "file (" .. hash .. ")" end end, + __index = { + --- Close this file handle, freeing any resources it uses. + -- + -- @treturn[1] true If this handle was successfully closed. + -- @treturn[2] nil If this file handle could not be closed. + -- @treturn[2] string The reason it could not be closed. + -- @throws If this handle was already closed. close = function(self) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -36,18 +50,24 @@ handleMetatable = { return nil, "attempt to close standard stream" end end, + + --- Flush any buffered output, forcing it to be written to the file + -- + -- @throws If the handle has been closed flush = function(self) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end local handle = self._handle if handle.flush then handle.flush() end + return true end, + lines = function(self, ...) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -57,9 +77,10 @@ handleMetatable = { local args = table.pack(...) return function() return checkResult(self, self:read(table.unpack(args, 1, args.n))) end end, + read = function(self, ...) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -71,9 +92,9 @@ handleMetatable = { for i = 1, n do local arg = select(i, ...) local res - if typeOf(arg) == "number" then + if type_of(arg) == "number" then if handle.read then res = handle.read(arg) end - elseif typeOf(arg) == "string" then + elseif type_of(arg) == "string" then local format = arg:gsub("^%*", ""):sub(1, 1) if format == "l" then @@ -88,7 +109,7 @@ handleMetatable = { error("bad argument #" .. i .. " (invalid format)", 2) end else - error("bad argument #" .. i .. " (expected string, got " .. typeOf(arg) .. ")", 2) + error("bad argument #" .. i .. " (expected string, got " .. type_of(arg) .. ")", 2) end output[i] = res @@ -99,9 +120,10 @@ handleMetatable = { if n == 0 and handle.readLine then return handle.readLine() end return table.unpack(output, 1, n) end, + seek = function(self, whence, offset) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -111,10 +133,18 @@ handleMetatable = { -- It's a tail call, so error positions are preserved return handle.seek(whence, offset) end, + setvbuf = function(self, mode, size) end, + + --- Write one or more values to the file + -- + -- @tparam string|number ... The values to write. + -- @treturn[1] Handle The current file, allowing chained calls. + -- @treturn[2] nil If the file could not be written to. + -- @treturn[2] string The error message which occurred while writing. write = function(self, ...) - if typeOf(self) ~= "table" or getmetatable(self) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(self) .. ")", 2) + if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) end if self._closed then error("attempt to use a closed file", 2) end @@ -156,41 +186,88 @@ local defaultError = setmetatable({ local currentInput = defaultInput local currentOutput = defaultOutput +--- A file handle representing the "standard input". Reading from this +-- file will prompt the user for input. stdin = defaultInput + +--- A file handle representing the "standard output". Writing to this +-- file will display the written text to the screen. stdout = defaultOutput + +--- A file handle representing the "standard error" stream. +-- +-- One may use this to display error messages, writing to it will display +-- them on the terminal. stderr = defaultError -function close(_file) - if _file == nil then return currentOutput:close() end +--- Closes the provided file handle. +-- +-- @tparam[opt] Handle file The file handle to close, defaults to the +-- current output file. +-- +-- @see Handle:close +-- @see io.output +function close(file) + if file == nil then return currentOutput:close() end - if typeOf(_file) ~= "table" or getmetatable(_file) ~= handleMetatable then - error("bad argument #1 (FILE expected, got " .. typeOf(_file) .. ")", 2) + if type_of(file) ~= "table" or getmetatable(file) ~= handleMetatable then + error("bad argument #1 (FILE expected, got " .. type_of(file) .. ")", 2) end - return _file:close() + return file:close() end +--- Flushes the current output file. +-- +-- @see Handle:flush +-- @see io.output function flush() return currentOutput:flush() end -function input(_arg) - if typeOf(_arg) == "string" then - local res, err = open(_arg, "rb") +--- Get or set the current input file. +-- +-- @tparam[opt] Handle|string file The new input file, either as a file path or pre-existing handle. +-- @treturn Handle The current input file. +-- @throws If the provided filename cannot be opened for reading. +function input(file) + if type_of(file) == "string" then + local res, err = open(file, "rb") if not res then error(err, 2) end currentInput = res - elseif typeOf(_arg) == "table" and getmetatable(_arg) == handleMetatable then - currentInput = _arg - elseif _arg ~= nil then - error("bad argument #1 (FILE expected, got " .. typeOf(_arg) .. ")", 2) + elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then + currentInput = file + elseif file ~= nil then + error("bad fileument #1 (FILE expected, got " .. type_of(file) .. ")", 2) end return currentInput end -function lines(_sFileName) - expect(1, _sFileName, "string", "nil") - if _sFileName then - local ok, err = open(_sFileName, "rb") +--- Opens the given file name in read mode and returns an iterator that, +-- each time it is called, returns a new line from the file. +-- +-- This can be used in a for loop to iterate over all lines of a file: +-- +-- ```lua +-- for line in io.lines(filename) do print(line) end +-- ``` +-- +-- Once the end of the file has been reached, @{nil} will be +-- returned. The file is automatically closed. +-- +-- If no file name is given, the @{io.input|current input} will be used +-- instead. In this case, the handle is not used. +-- +-- @tparam[opt] string filename The name of the file to extract lines from +-- @treturn function():string|nil The line iterator. +-- @throws If the file cannot be opened for reading +-- +-- @see Handle:lines +-- @see io.input +function lines(filename) + expect(1, filename, "string", "nil") + if filename then + local ok, err = open(filename, "rb") if not ok then error(err, 2) end -- We set this magic flag to mark this file as being opened by io.lines and so should be @@ -202,38 +279,72 @@ function lines(_sFileName) end end -function open(_sPath, _sMode) - expect(1, _sPath, "string") - expect(2, _sMode, "string", "nil") +--- Open a file with the given mode, either returning a new file handle +-- or @{nil}, plus an error message. +-- +-- The `mode` string can be any of the following: +-- - **"r"**: Read mode +-- - **"w"**: Write mode +-- - **"w"**: Append mode +-- +-- The mode may also have a `b` at the end, which opens the file in "binary +-- mode". This allows you to read binary files, as well as seek within a file. +-- +-- @tparam string filename The name of the file to open. +-- @tparam[opt] string mode The mode to open the file with. This defaults to `rb`. +-- @treturn[1] Handle The opened file. +-- @treturn[2] nil In case of an error. +-- @treturn[2] string The reason the file could not be opened. +function open(filename, mode) + expect(1, filename, "string") + expect(2, mode, "string", "nil") - local sMode = _sMode and _sMode:gsub("%+", "") or "rb" - local file, err = fs.open(_sPath, sMode) + local sMode = mode and mode:gsub("%+", "") or "rb" + local file, err = fs.open(filename, sMode) if not file then return nil, err end return setmetatable({ _handle = file }, handleMetatable) end -function output(_arg) - if typeOf(_arg) == "string" then - local res, err = open(_arg, "w") +--- Get or set the current output file. +-- +-- @tparam[opt] Handle|string file The new output file, either as a file path or pre-existing handle. +-- @treturn Handle The current output file. +-- @throws If the provided filename cannot be opened for writing. +function output(file) + if type_of(file) == "string" then + local res, err = open(file, "w") if not res then error(err, 2) end currentOutput = res - elseif typeOf(_arg) == "table" and getmetatable(_arg) == handleMetatable then - currentOutput = _arg - elseif _arg ~= nil then - error("bad argument #1 (FILE expected, got " .. typeOf(_arg) .. ")", 2) + elseif type_of(file) == "table" and getmetatable(file) == handleMetatable then + currentOutput = file + elseif file ~= nil then + error("bad argument #1 (FILE expected, got " .. type_of(file) .. ")", 2) end return currentOutput end +--- Read from the currently opened input file. +-- +-- This is equivalent to `io.input():read(...)`. See @{Handle:read|the +-- documentation} there for full details. +-- +-- @tparam string ... The formats to read, defaulting to a whole line. +-- @treturn (string|nil)... The data read, or @{nil} if nothing can be read. function read(...) return currentInput:read(...) end -function type(handle) - if typeOf(handle) == "table" and getmetatable(handle) == handleMetatable then - if handle._closed then +--- Checks whether `handle` is a given file handle, and determine if it is open +-- or not. +-- +-- @param obj The value to check +-- @treturn string|nil `"file"` if this is an open file, `"closed file"` if it +-- is a closed file handle, or `nil` if not a file handle. +function type(obj) + if type_of(obj) == "table" and getmetatable(obj) == handleMetatable then + if obj._closed then return "closed file" else return "file" @@ -242,6 +353,12 @@ function type(handle) return nil end +--- Write to the currently opened output file. +-- +-- This is equivalent to `io.output():write(...)`. See @{Handle:write|the +-- documentation} there for full details. +-- +-- @tparam string ... The strings to write function write(...) return currentOutput:write(...) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua b/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua index c3ab42605..d303a8087 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua @@ -1,5 +1,12 @@ --- Minecraft key code bindings --- See http://www.minecraftwiki.net/wiki/Key_codes for more info +--- The Keys API provides a table of numerical codes corresponding to keyboard +-- keys, suitable for decoding key events. +-- +-- The Minecraft wiki [has a list of key +-- codes](http://www.minecraftwiki.net/wiki/Key_codes). It is recommended that +-- you use the constants provided by this file, rather than the underlying +-- numerical values. +-- +-- @module keys local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -53,12 +60,18 @@ local keys = _ENV for nKey, sKey in pairs( tKeys ) do keys[sKey] = nKey end -keys["return"] = keys.enter ---backwards compatibility to earlier, typo prone, versions -keys.scollLock = keys.scrollLock -keys.cimcumflex = keys.circumflex -function getName( _nKey ) - expect(1, _nKey, "number") - return tKeys[ _nKey ] +keys["return"] = keys.enter --- @local +--backwards compatibility to earlier, typo prone, versions +keys.scollLock = keys.scrollLock --- @local +keys.cimcumflex = keys.circumflex --- @local + +--- Translates a numerical key code to a human-readable name. The human-readable +-- name is one of the constants in the keys API. +-- +-- @tparam number code The key code to look up. +-- @treturn string|nil The name of the key, or `nil` if not a valid key code. +function getName( code ) + expect(1, code, "number") + return tKeys[ code ] end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua index d725367d0..4a5630034 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua @@ -1,3 +1,8 @@ +--- An API for advanced systems which can draw pixels and lines, load and draw +-- image files. You can use the `colors` API for easier color manipulation. +-- +-- @module paintutils + local expect = dofile("rom/modules/main/cc/expect.lua").expect local function drawPixelInternal( xPos, yPos ) @@ -18,51 +23,88 @@ local function parseLine( tImageArg, sLine ) table.insert( tImageArg, tLine ) end -function parseImage( sRawData ) - expect(1, sRawData, "string") +--- Parses an image from a multi-line string +-- +-- @tparam string image The string containing the raw-image data. +-- @treturn table The parsed image data, suitable for use with +-- @{paintutils.drawImage}. +function parseImage( image ) + expect(1, image, "string") local tImage = {} - for sLine in ( sRawData .. "\n" ):gmatch( "(.-)\n" ) do -- read each line like original file handling did + for sLine in ( image .. "\n" ):gmatch( "(.-)\n" ) do parseLine( tImage, sLine ) end return tImage end -function loadImage( sPath ) - expect(1, sPath, "string") +--- Loads an image from a file. +-- +-- You can create a file suitable for being loaded using the `paint` program. +-- +-- @tparam string path The file to load. +-- +-- @treturn table|nil The parsed image data, suitable for use with +-- @{paintutils.drawImage}, or `nil` if the file does not exist. +function loadImage( path ) + expect(1, path, "string") - if fs.exists( sPath ) then - local file = io.open( sPath, "r" ) + if fs.exists( path ) then + local file = io.open( path, "r" ) local sContent = file:read("*a") file:close() - return parseImage( sContent ) -- delegate image parse to parseImage + return parseImage( sContent ) end return nil end -function drawPixel( xPos, yPos, nColour ) +--- Draws a single pixel to the current term at the specified position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number xPos The x position to draw at, where 1 is the far left. +-- @tparam number yPos The y position to draw at, where 1 is the very top. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. +function drawPixel( xPos, yPos, colour ) expect(1, xPos, "number") expect(2, yPos, "number") - expect(3, nColour, "number", "nil") - if nColour then - term.setBackgroundColor( nColour ) + expect(3, colour, "number", "nil") + + if type( xPos ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( xPos ) .. ")", 2 ) end + if type( yPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( yPos ) .. ")", 2 ) end + if colour ~= nil and type( colour ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( colour ) .. ")", 2 ) end + if colour then + term.setBackgroundColor( colour ) end return drawPixelInternal( xPos, yPos ) end -function drawLine( startX, startY, endX, endY, nColour ) +--- Draws a straight line from the start to end position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number startX The starting x position of the line. +-- @tparam number startY The starting y position of the line. +-- @tparam number endX The end x position of the line. +-- @tparam number endY The end y position of the line. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. +function drawLine( startX, startY, endX, endY, colour ) expect(1, startX, "number") expect(2, startY, "number") expect(3, endX, "number") expect(4, endY, "number") - expect(5, nColour, "number", "nil") + expect(5, colour, "number", "nil") startX = math.floor(startX) startY = math.floor(startY) endX = math.floor(endX) endY = math.floor(endY) - if nColour then - term.setBackgroundColor( nColour ) + if colour then + term.setBackgroundColor( colour ) end if startX == endX and startY == endY then drawPixelInternal( startX, startY ) @@ -110,6 +152,18 @@ function drawLine( startX, startY, endX, endY, nColour ) end end +--- Draws the outline of a box on the current term from the specified start +-- position to the specified end position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number startX The starting x position of the line. +-- @tparam number startY The starting y position of the line. +-- @tparam number endX The end x position of the line. +-- @tparam number endY The end y position of the line. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. function drawBox( startX, startY, endX, endY, nColour ) expect(1, startX, "number") expect(2, startY, "number") @@ -154,7 +208,18 @@ function drawBox( startX, startY, endX, endY, nColour ) end end end - +--- Draws a filled box on the current term from the specified start position to +-- the specified end position. +-- +-- Be warned, this may change the position of the cursor and the current +-- background colour. You should not expect either to be preserved. +-- +-- @tparam number startX The starting x position of the line. +-- @tparam number startY The starting y position of the line. +-- @tparam number endX The end x position of the line. +-- @tparam number endY The end y position of the line. +-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be +-- the current background colour if not specified. function drawFilledBox( startX, startY, endX, endY, nColour ) expect(1, startX, "number") expect(2, startY, "number") @@ -194,12 +259,17 @@ function drawFilledBox( startX, startY, endX, endY, nColour ) end end -function drawImage( tImage, xPos, yPos ) - expect(1, tImage, "table") +--- Draw an image loaded by @{paintutils.parseImage} or @{paintutils.loadImage}. +-- +-- @tparam table image The parsed image data. +-- @tparam number xPos The x position to start drawing at. +-- @tparam number xPos The y position to start drawing at. +function drawImage( image, xPos, yPos ) + expect(1, image, "table") expect(2, xPos, "number") expect(3, yPos, "number") - for y = 1, #tImage do - local tLine = tImage[y] + for y = 1, #image do + local tLine = image[y] for x = 1, #tLine do if tLine[x] > 0 then term.setBackgroundColor( tLine[x] ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua b/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua index 43b832c8b..ea4f8d43a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua @@ -1,3 +1,18 @@ +--- Provides a simple implementation of multitasking. +-- +-- Functions are not actually executed simultaniously, but rather this API will +-- automatically switch between them whenever they yield (eg whenever they call +-- @{coroutine.yield}, or functions that call that - eg `os.pullEvent` - or +-- functions that call that, etc - basically, anything that causes the function +-- to "pause"). +-- +-- Each function executed in "parallel" gets its own copy of the event queue, +-- and so "event consuming" functions (again, mostly anything that causes the +-- script to pause - eg `sleep`, `rednet.receive`, most of the `turtle` API, +-- etc) can safely be used in one without affecting the event queue accessed by +-- the other. +-- +-- @module parallel local function create( ... ) local tFns = table.pack(...) @@ -55,12 +70,22 @@ local function runUntilLimit( _routines, _limit ) end end +--- Switches between execution of the functions, until any of them +-- finishes. If any of the functions errors, the message is propagated upwards +-- from the @{parallel.waitForAny} call. +-- +-- @tparam function ... The functions this task will run function waitForAny( ... ) local routines = create( ... ) return runUntilLimit( routines, #routines - 1 ) end +--- Switches between execution of the functions, until all of them are +-- finished. If any of the functions errors, the message is propagated upwards +-- from the @{parallel.waitForAll} call. +-- +-- @tparam function ... The functions this task will run function waitForAll( ... ) local routines = create( ... ) - runUntilLimit( routines, 0 ) + return runUntilLimit( routines, 0 ) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua index a398d696f..c3c71ff65 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua @@ -1,110 +1,182 @@ +--- The Peripheral API is for interacting with peripherals connected to the +-- computer, such as the Disk Drive, the Advanced Monitor and Monitor. +-- +-- Each peripheral block has a name, either referring to the side the peripheral +-- can be found on, or a name on an adjacent wired network. +-- +-- If the peripheral is next to the computer, its side is either `front`, +-- `back`, `left`, `right`, `top` or `bottom`. If the peripheral is attached by +-- a cable, its side will follow the format `type_id`, for example `printer_0`. +-- +-- Peripheral functions are called *methods*, a term borrowed from Java. +-- +-- @module peripheral + local expect = dofile("rom/modules/main/cc/expect.lua").expect local native = peripheral +local sides = rs.getSides() +--- Provides a list of all peripherals available. +-- +-- If a device is located directly next to the system, then its name will be +-- listed as the side it is attached to. If a device is attached via a Wired +-- Modem, then it'll be reported according to its name on the wired network. +-- +-- @treturn table A list of the names of all attached peripherals. function getNames() - local tResults = {} - for _, sSide in ipairs( rs.getSides() ) do - if native.isPresent( sSide ) then - table.insert( tResults, sSide ) - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - local tRemote = native.call( sSide, "getNamesRemote" ) - for _, sName in ipairs( tRemote ) do - table.insert( tResults, sName ) + local results = {} + for n = 1, #sides do + local side = sides[n] + if native.isPresent(side) then + table.insert(results, side) + if native.getType(side) == "modem" and not native.call(side, "isWireless") then + local remote = native.call(side, "getNamesRemote") + for _, name in ipairs(remote) do + table.insert(results, name) end end end end - return tResults + return results end -function isPresent( _sSide ) - expect(1, _sSide, "string") - if native.isPresent( _sSide ) then +--- Determines if a peripheral is present with the given name. +-- +-- @tparam string name The side or network name that you want to check. +-- @treturn boolean If a peripheral is present with the given name. +-- @usage peripheral.isPresent("top") +-- @usage peripheral.isPresent("monitor_0") +function isPresent(name) + expect(1, name, "string") + if native.isPresent(name) then return true end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return true - end + + for n = 1, #sides do + local name = sides[n] + if native.getType(name) == "modem" and not native.call(name, "isWireless") and + native.call(name, "isPresentRemote", name) + then + return true end end return false end -function getType( _sSide ) - expect(1, _sSide, "string") - if native.isPresent( _sSide ) then - return native.getType( _sSide ) +--- Get the type of the peripheral with the given name. +-- +-- @tparam string name The name of the peripheral to find. +-- @treturn string|nil The peripheral's type, or `nil` if it is not present. +function getType(name) + expect(1, name, "string") + if native.isPresent(name) then + return native.getType(name) end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return native.call( sSide, "getTypeRemote", _sSide ) - end + for n = 1, #sides do + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) + then + return native.call(side, "getTypeRemote", name) end end return nil end -function getMethods( _sSide ) - expect(1, _sSide, "string") - if native.isPresent( _sSide ) then - return native.getMethods( _sSide ) +--- Get all available methods for the peripheral with the given name. +-- +-- @tparam string name The name of the peripheral to find. +-- @treturn table|nil A list of methods provided by this peripheral, or `nil` if +-- it is not present. +function getMethods(name) + expect(1, name, "string") + if native.isPresent(name) then + return native.getMethods(name) end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return native.call( sSide, "getMethodsRemote", _sSide ) - end + for n = 1, #sides do + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) + then + return native.call(side, "getMethodsRemote", name) end end return nil end -function call( _sSide, _sMethod, ... ) - expect(1, _sSide, "string") - expect(2, _sMethod, "string") - if native.isPresent( _sSide ) then - return native.call( _sSide, _sMethod, ... ) +--- Call a method on a peripheral with a given name +-- +-- @tparam string name The name of the peripheral to invoke the method on. +-- @tparam string method The name of the method +-- @param ... Additional arguments to pass to the method +-- @return The return values of the peripheral method. +-- +-- @usage peripheral.call("top", "open", 1) +function call(name, method, ...) + expect(1, name, "string") + expect(2, method, "string") + if native.isPresent(name) then + return native.call(name, method, ...) end - for _, sSide in ipairs( rs.getSides() ) do - if native.getType( sSide ) == "modem" and not native.call( sSide, "isWireless" ) then - if native.call( sSide, "isPresentRemote", _sSide ) then - return native.call( sSide, "callRemote", _sSide, _sMethod, ... ) - end + + for n = 1, #sides do + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) + then + return native.call(side, "callRemote", name, method, ...) end end return nil end -function wrap( _sSide ) - expect(1, _sSide, "string") - if peripheral.isPresent( _sSide ) then - local tMethods = peripheral.getMethods( _sSide ) - local tResult = {} - for _, sMethod in ipairs( tMethods ) do - tResult[sMethod] = function( ... ) - return peripheral.call( _sSide, sMethod, ... ) - end - end - return tResult +--- Get a table containing functions pointing to the peripheral's methods, which +-- can then be called as if using @{peripheral.call}. +-- +-- @tparam string name The name of the peripheral to wrap. +-- @treturn table|nil The table containing the peripheral's methods, or `nil` if +-- there is no peripheral present with the given name. +-- @usage peripheral.wrap("top").open(1) +function wrap(name) + expect(1, name, "string") + + local methods = peripheral.getMethods(name) + if not methods then + return nil end - return nil + + local result = {} + for _, method in ipairs(methods) do + result[method] = function(...) + return peripheral.call(name, method, ...) + end + end + return result end -function find( sType, fnFilter ) - expect(1, sType, "string") - expect(2, fnFilter, "function", "nil") - local tResults = {} - for _, sName in ipairs( peripheral.getNames() ) do - if peripheral.getType( sName ) == sType then - local wrapped = peripheral.wrap( sName ) - if fnFilter == nil or fnFilter( sName, wrapped ) then - table.insert( tResults, wrapped ) +--- Find all peripherals of a specific type, and return the +-- @{peripheral.wrap|wrapped} peripherals. +-- +-- @tparam string ty The type of peripheral to look for. +-- @tparam[opt] function(name:string, wrapped:table):boolean filter A +-- filter function, which takes the peripheral's name and wrapped table +-- and returns if it should be included in the result. +-- @treturn table... 0 or more wrapped peripherals matching the given filters. +-- @usage local monitors = { peripheral.find("monitor") } +-- @usage peripheral.find("modem", rednet.open) +function find(ty, filter) + expect(1, ty, "string") + expect(2, filter, "function", "nil") + + local results = {} + for _, name in ipairs(peripheral.getNames()) do + if peripheral.getType(name) == ty then + local wrapped = peripheral.wrap(name) + if filter == nil or filter(name, wrapped) then + table.insert(results, wrapped) end end end - return table.unpack( tResults ) + return table.unpack(results) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua b/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua index d6bf5d668..9152aa647 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua @@ -1,51 +1,92 @@ +--- The Rednet API allows systems to communicate between each other without +-- using redstone. It serves as a wrapper for the modem API, offering ease of +-- functionality (particularly in regards to repeating signals) with some +-- expense of fine control. +-- +-- In order to send and receive data, a modem (either wired, wireless, or ender) +-- is required. The data reaches any possible destinations immediately after +-- sending it, but is range limited. +-- +-- Rednet also allows you to use a "protocol" - simple string names indicating +-- what messages are about. Receiving systems may filter messages according to +-- their protocols, thereby automatically ignoring incoming messages which don't +-- specify an identical string. It's also possible to @{rednet.lookup|lookup} +-- which systems in the area use certain protocols, hence making it easier to +-- determine where given messages should be sent in the first place. +-- +-- @module rednet + local expect = dofile("rom/modules/main/cc/expect.lua").expect +--- The channel used by the Rednet API to @{broadcast} messages. CHANNEL_BROADCAST = 65535 + +--- The channel used by the Rednet API to repeat messages. CHANNEL_REPEAT = 65533 local tReceivedMessages = {} local tReceivedMessageTimeouts = {} local tHostnames = {} -function open( sModem ) - expect(1, sModem, "string") - if peripheral.getType( sModem ) ~= "modem" then - error( "No such modem: " .. sModem, 2 ) +--- Opens a modem with the given @{peripheral} name, allowing it to send and +--- receive messages over rednet. +-- +-- This will open the modem on two channels: one which has the same +-- @{os.getComputerID|ID} as the computer, and another on +-- @{CHANNEL_BROADCAST|the broadcast channel}. +-- +-- @tparam string modem The name of the modem to open. +-- @throws If there is no such modem with the given name +function open( modem ) + expect(1, modem, "string") + if peripheral.getType( modem ) ~= "modem" then + error( "No such modem: " .. modem, 2 ) end - peripheral.call( sModem, "open", os.getComputerID() ) - peripheral.call( sModem, "open", CHANNEL_BROADCAST ) + peripheral.call( modem, "open", os.getComputerID() ) + peripheral.call( modem, "open", CHANNEL_BROADCAST ) end -function close( sModem ) - expect(1, sModem, "string", "nil") - if sModem then +--- Close a modem with the given @{peripheral} name, meaning it can no longer +-- send and receive rednet messages. +-- +-- @tparam[opt] string modem The side the modem exists on. If not given, all +-- open modems will be closed. +-- @throws If there is no such modem with the given name +function close( modem ) + expect(1, modem, "string", "nil") + if modem then -- Close a specific modem - if peripheral.getType( sModem ) ~= "modem" then - error( "No such modem: " .. sModem, 2 ) + if peripheral.getType( modem ) ~= "modem" then + error( "No such modem: " .. modem, 2 ) end - peripheral.call( sModem, "close", os.getComputerID() ) - peripheral.call( sModem, "close", CHANNEL_BROADCAST ) + peripheral.call( modem, "close", os.getComputerID() ) + peripheral.call( modem, "close", CHANNEL_BROADCAST ) else -- Close all modems - for _, sModem in ipairs( peripheral.getNames() ) do - if isOpen( sModem ) then - close( sModem ) + for _, modem in ipairs( peripheral.getNames() ) do + if isOpen( modem ) then + close( modem ) end end end end -function isOpen( sModem ) - expect(1, sModem, "string", "nil") - if sModem then +--- Determine if rednet is currently open. +-- +-- @tparam[opt] string modem Which modem to check. If not given, all connected +-- modems will be checked. +-- @treturn boolean If the given modem is open. +function isOpen( modem ) + expect(1, modem, "string", "nil") + if modem then -- Check if a specific modem is open - if peripheral.getType( sModem ) == "modem" then - return peripheral.call( sModem, "isOpen", os.getComputerID() ) and peripheral.call( sModem, "isOpen", CHANNEL_BROADCAST ) + if peripheral.getType( modem ) == "modem" then + return peripheral.call( modem, "isOpen", os.getComputerID() ) and peripheral.call( modem, "isOpen", CHANNEL_BROADCAST ) end else -- Check if any modem is open - for _, sModem in ipairs( peripheral.getNames() ) do - if isOpen( sModem ) then + for _, modem in ipairs( peripheral.getNames() ) do + if isOpen( modem ) then return true end end @@ -53,6 +94,23 @@ function isOpen( sModem ) return false end +--- Allows a computer or turtle with an attached modem to send a message +-- intended for a system with a specific ID. At least one such modem must first +-- be @{rednet.open|opened} before sending is possible. +-- +-- Assuming the target was in range and also had a correctly opened modem, it +-- may then use @{rednet.receive} to collect the message. +-- +-- @tparam number nRecipient The ID of the receiving computer. +-- @param message The message to send. This should not contain coroutines or +-- functions, as they will be converted to @{nil}. +-- @tparam[opt] string sProtocol The "protocol" to send this message under. When +-- using @{rednet.receive} one can filter to only receive messages sent under a +-- particular protocol. +-- @treturn boolean If this message was successfully sent (i.e. if rednet is +-- currently @{rednet.open|open}). Note, this does not guarantee the message was +-- actually _received_. +-- @see rednet.receive function send( nRecipient, message, sProtocol ) expect(1, nRecipient, "number") expect(3, sProtocol, "string", "nil") @@ -91,11 +149,34 @@ function send( nRecipient, message, sProtocol ) return sent end +--- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST} +-- channel. The message will be received by every device listening to rednet. +-- +-- @param message The message to send. This should not contain coroutines or +-- functions, as they will be converted to @{nil}. +-- @tparam[opt] string sProtocol The "protocol" to send this message under. When +-- using @{rednet.receive} one can filter to only receive messages sent under a +-- particular protocol. +-- @see rednet.receive function broadcast( message, sProtocol ) expect(2, sProtocol, "string", "nil") send( CHANNEL_BROADCAST, message, sProtocol ) end +--- Wait for a rednet message to be received, or until `nTimeout` seconds have +-- elapsed. +-- +-- @tparam[opt] string sProtocolFilter The protocol the received message must be +-- sent with. If specified, any messages not sent under this protocol will be +-- discarded. +-- @tparam[opt] number nTimeout The number of seconds to wait if no message is +-- received. +-- @treturn[1] number The computer which sent this message +-- @return[1] The received message +-- @treturn[1] string|nil The protocol this message was sent under. +-- @treturn[2] nil If the timeout elapsed and no message was received. +-- @see rednet.broadcast +-- @see rednet.send function receive( sProtocolFilter, nTimeout ) -- The parameters used to be ( nTimeout ), detect this case for backwards compatibility if type(sProtocolFilter) == "number" and nTimeout == nil then @@ -132,6 +213,24 @@ function receive( sProtocolFilter, nTimeout ) end end +--- Register the system as "hosting" the desired protocol under the specified +-- name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and +-- maybe name) on the same network, the registered system will automatically +-- respond via a background process, hence providing the system performing the +-- lookup with its ID number. +-- +-- Multiple computers may not register themselves on the same network as having +-- the same names against the same protocols, and the title `localhost` is +-- specifically reserved. They may, however, share names as long as their hosted +-- protocols are different, or if they only join a given network after +-- "registering" themselves before doing so (eg while offline or part of a +-- different network). +-- +-- @tparam string sProtocol The protocol this computer provides. +-- @tparam string sHostname The name this protocol exposes for the given protocol. +-- @throws If trying to register a hostname which is reserved, or currently in use. +-- @see rednet.unhost +-- @see rednet.lookup function host( sProtocol, sHostname ) expect(1, sProtocol, "string") expect(2, sHostname, "string") @@ -146,11 +245,29 @@ function host( sProtocol, sHostname ) end end +--- Stop @{rednet.host|hosting} a specific protocol, meaning it will no longer +--- respond to @{rednet.lookup} requests. +-- +-- @tparam string sProtocol The protocol to unregister your self from. function unhost( sProtocol ) expect(1, sProtocol, "string") tHostnames[ sProtocol ] = nil end +--- Search the local rednet network for systems @{rednet.host|hosting} the +-- desired protocol and returns any computer IDs that respond as "registered" +-- against it. +-- +-- If a hostname is specified, only one ID will be returned (assuming an exact +-- match is found). +-- +-- @tparam string sProtocol The protocol to search for. +-- @tparam[opt] string sHostname The hostname to search for. +-- +-- @treturn[1] { number }|nil A list of computer IDs hosting the given +-- protocol, or @{nil} if none exist. +-- @treturn[2] number|nil The computer ID with the provided hostname and protocol, +-- or @{nil} if none exists. function lookup( sProtocol, sHostname ) expect(1, sProtocol, "string") expect(2, sHostname, "string", "nil") @@ -216,6 +333,8 @@ function lookup( sProtocol, sHostname ) end local bRunning = false + +--- @local function run() if bRunning then error( "rednet is already running", 2 ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua index 54da34a32..50501bbaf 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua @@ -1,16 +1,31 @@ +--- The settings API allows to store values and save them to a file for +-- persistent configurations for CraftOS and your programs. +-- +-- By default, the settings API will load its configuration from the +-- `/.settings` file. One can then use @{settings.save} to update the file. +-- +-- @module settings + local expect = dofile("rom/modules/main/cc/expect.lua").expect local tSettings = {} -function set( sName, value ) - expect(1, sName, "string") +--- Set the value of a setting. +-- +-- @tparam string name The name of the setting to set +-- @param value The setting's value. This cannot be `nil`, and must be +-- serialisable by @{textutils.serialize}. +-- @throws If this value cannot be serialised +-- @see settings.unset +function set( name, value ) + expect(1, name, "string") expect(2, value, "number", "string", "boolean", "table") if type(value) == "table" then -- Ensure value is serializeable value = textutils.unserialize( textutils.serialize(value) ) end - tSettings[ sName ] = value + tSettings[ name ] = value end local copy @@ -26,9 +41,15 @@ function copy( value ) end end -function get( sName, default ) - expect(1, sName, "string") - local result = tSettings[ sName ] +--- Get the value of a setting. +-- +-- @tparam string name The name of the setting to get. +-- @param[opt] default The value to use should there be pre-existing value for +-- this setting. Defaults to `nil`. +-- @return The setting's, or `default` if the setting has not been set. +function get( name, default ) + expect(1, name, "string") + local result = tSettings[ name ] if result ~= nil then return copy(result) else @@ -36,15 +57,31 @@ function get( sName, default ) end end -function unset( sName ) - expect(1, sName, "string") - tSettings[ sName ] = nil +--- Remove the value of a setting, clearing it back to `nil`. +-- +-- @{settings.get} will return the default value until the setting's value is +-- @{settings.set|set}, or the computer is rebooted. +-- +-- @tparam string name The name of the setting to unset. +-- @see settings.set +-- @see settings.clear +function unset( name ) + expect(1, name, "string") + tSettings[ name ] = nil end +--- Removes the value of all settings. Equivalent to calling @{settings.unset} +--- on every setting. +-- +-- @see settings.unset function clear() tSettings = {} end +--- Get the names of all currently defined settings. +-- +-- @treturn { string } An alphabetically sorted list of all currently-defined +-- settings. function getNames() local result = {} for k in pairs( tSettings ) do @@ -54,6 +91,17 @@ function getNames() return result end +--- Load settings from the given file. +-- +-- Existing settings will be merged with any pre-existing ones. Conflicting +-- entries will be overwritten, but any others will be preserved. +-- +-- @tparam string sPath The file to load from. +-- @treturn boolean Whether settings were successfully read from this +-- file. Reasons for failure may include the file not existing or being +-- corrupted. +-- +-- @see settings.save function load( sPath ) expect(1, sPath, "string") local file = fs.open( sPath, "r" ) @@ -79,6 +127,15 @@ function load( sPath ) return true end +--- Save settings to the given file. +-- +-- This will entirely overwrite the pre-existing file. Settings defined in the +-- file, but not currently loaded will be removed. +-- +-- @tparam string sPath The path to save settings to. +-- @treturn boolean If the settings were successfully saved. +-- +-- @see settings.load function save( sPath ) expect(1, sPath, "string") local file = fs.open( sPath, "w" ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua index c6f9c2a9b..095fe1e9f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua @@ -1,3 +1,8 @@ +--- The Terminal API provides functions for writing text to the terminal and +-- monitors, and drawing ASCII graphics. +-- +-- @module term + local expect = dofile("rom/modules/main/cc/expect.lua").expect local native = term.native and term.native() or term @@ -9,8 +14,26 @@ local function wrap( _sFunction ) end end -local term = {} +local term = _ENV +--- Redirects terminal output to a monitor, a @{window}, or any other custom +-- terminal object. Once the redirect is performed, any calls to a "term" +-- function - or to a function that makes use of a term function, as @{print} - +-- will instead operate with the new terminal object. +-- +-- A "terminal object" is simply a table that contains functions with the same +-- names - and general features - as those found in the term table. For example, +-- a wrapped monitor is suitable. +-- +-- The redirect can be undone by pointing back to the previous terminal object +-- (which this function returns whenever you switch). +-- +-- @tparam Redirect target The terminal redirect the @{term} API will draw to. +-- @treturn Redirect The previous redirect object, as returned by +-- @{term.current}. +-- @usage +-- Redirect to a monitor on the right of the computer. +-- term.redirect(peripheral.wrap("right")) term.redirect = function( target ) expect(1, target, "table") if target == term or target == _G.term then @@ -30,14 +53,24 @@ term.redirect = function( target ) return oldRedirectTarget end +--- Returns the current terminal object of the computer. +-- +-- @treturn Redirect The current terminal redirect +-- @usage +-- Create a new @{window} which draws to the current redirect target +-- window.create(term.current(), 1, 1, 10, 10) term.current = function() return redirectTarget end +--- Get the native terminal object of the current computer. +-- +-- It is recommended you do not use this function unless you absolutely have +-- to. In a multitasked environment, @{term.native} will _not_ be the current +-- terminal object, and so drawing may interfere with other programs. +-- +-- @treturn Redirect The native terminal redirect. term.native = function() - -- NOTE: please don't use this function unless you have to. - -- If you're running in a redirected or multitasked enviorment, term.native() will NOT be - -- the current terminal when your program starts up. It is far better to use term.current() return native end @@ -49,12 +82,7 @@ for _, method in ipairs { "nativePaletteColor", "nativePaletteColour" } do end for k, v in pairs( native ) do - if type( k ) == "string" and type( v ) == "function" and term[k] == nil then + if type( k ) == "string" and type( v ) == "function" and rawget(term, k) == nil then term[k] = wrap( k ) end end - -local env = _ENV -for k, v in pairs( term ) do - env[k] = v -end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua index de7cd4e95..4be4388f7 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua @@ -1,5 +1,20 @@ +--- The `textutils` API provides helpful utilities for formatting and +-- manipulating strings. +-- +-- @module textutils + local expect = dofile("rom/modules/main/cc/expect.lua").expect +--- Slowly writes string text at current cursor position, +-- character-by-character. +-- +-- Like @{write}, this does not insert a newline at the end. +-- +-- @tparam string sText The the text to write to the screen +-- @tparam[opt] number nRate The number of characters to write each second, +-- Defaults to 20. +-- @usage textutils.slowWrite("Hello, world!") +-- @usage textutils.slowWrite("Hello, world!", 5) function slowWrite( sText, nRate ) expect(2, nRate, "number", "nil") nRate = nRate or 20 @@ -21,11 +36,28 @@ function slowWrite( sText, nRate ) end end +--- Slowly prints string text at current cursor position, +-- character-by-character. +-- +-- Like @{print}, this inserts a newline after printing. +-- +-- @tparam string sText The the text to write to the screen +-- @tparam[opt] number nRate The number of characters to write each second, +-- Defaults to 20. +-- @usage textutils.slowPrint("Hello, world!") +-- @usage textutils.slowPrint("Hello, world!", 5) function slowPrint( sText, nRate ) slowWrite( sText, nRate ) print() end +--- Takes input time and formats it in a more readable format such as `6:30 PM`. +-- +-- @tparam number nTime The time to format, as provided by @{os.time}. +-- @tparam[opt] boolean bTwentyFourHour Whether to format this as a 24-hour +-- clock (`18:30`) rather than a 12-hour one (`6:30 AM`) +-- @treturn string The formatted time +-- @usage textutils.formatTime(os.time()) function formatTime( nTime, bTwentyFourHour ) expect(1, nTime, "number") expect(2, bTwentyFourHour, "boolean", "nil") @@ -71,6 +103,23 @@ local function makePagedScroll( _term, _nFreeLines ) end end +--- Prints a given string to the display. +-- +-- If the action can be completed without scrolling, it acts much the same as +-- @{print}; otherwise, it will throw up a "Press any key to continue" prompt at +-- the bottom of the display. Each press will cause it to scroll down and write +-- a single line more before prompting again, if need be. +-- +-- @tparam string _sText The text to print to the screen. +-- @tparam[opt] number _nFreeLines The number of lines which will be +-- automatically scrolled before the first prompt appears (meaning _nFreeLines + +-- 1 lines will be printed). This can be set to the terminal's height - 2 to +-- always try to fill the screen. Defaults to 0, meaning only one line is +-- displayed before prompting. +-- @treturn number The number of lines printed. +-- @usage +-- local width, height = term.getSize() +-- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2) function pagedPrint( _sText, _nFreeLines ) expect(2, _nFreeLines, "number", "nil") -- Setup a redirector @@ -159,10 +208,30 @@ local function tabulateCommon( bPaged, ... ) end end +--- Prints tables in a structured form. +-- +-- This accepts multiple arguments, either a table or a number. When +-- encountering a table, this will be treated as a table row, with each column +-- width being auto-adjusted. +-- +-- When encountering a number, this sets the text color of the subsequent rows to it. +-- +-- @tparam {string...}|number ... The rows and text colors to display. +-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) function tabulate( ... ) return tabulateCommon( false, ... ) end +--- Prints tables in a structured form, stopping and prompting for input should +-- the result not fit on the terminal. +-- +-- This functions identically to @{textutils.tabulate}, but will prompt for user +-- input should the whole output not fit on the display. +-- +-- @tparam {string...}|number ... The rows and text colors to display. +-- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) +-- @see textutils.tabulate +-- @see textutils.pagedPrint function pagedTabulate( ... ) return tabulateCommon( true, ... ) end @@ -238,6 +307,13 @@ local function serializeImpl( t, tTracking, sIndent ) end end +--- A table representing an empty JSON array, in order to distinguish it from an +-- empty JSON object. +-- +-- The contents of this table should not be modified. +-- +-- @usage textutils.serialiseJSON(textutils.empty_json_array) +-- @see textutils.serialiseJSON empty_json_array = setmetatable({}, { __newindex = function() error("attempt to mutate textutils.empty_json_array", 2) @@ -310,11 +386,28 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle ) end end +--- Convert a Lua object into a textual representation, suitable for +-- saving in a file or pretty-printing. +-- +-- @param t The object to serialise +-- @treturn string The serialised representation +-- @throws If the object contains a value which cannot be +-- serialised. This includes functions and tables which appear multiple +-- times. function serialize( t ) local tTracking = {} return serializeImpl( t, tTracking, "" ) end +serialise = serialize -- GB version + +--- Converts a serialised string back into a reassembled Lua object. +-- +-- This is mainly used together with @{textutils.serialize}. +-- +-- @tparam string s The serialised string to deserialise. +-- @return[1] The deserialised object +-- @treturn[2] nil If the object could not be deserialised. function unserialize( s ) expect(1, s, "string") local func = load( "return " .. s, "unserialize", "t", {} ) @@ -327,6 +420,26 @@ function unserialize( s ) return nil end +unserialise = unserialize -- GB version + +--- Returns a JSON representation of the given data. +-- +-- This function attempts to guess whether a table is a JSON array or +-- object. However, empty tables are assumed to be empty objects - use +-- @{textutils.empty_json_array} to mark an empty array. +-- +-- This is largely intended for interacting with various functions from the +-- @{commands} API, though may also be used in making @{http} requests. +-- +-- @param t The value to serialise. Like @{textutils.serialise}, this should not +-- contain recursive tables or functions. +-- @tparam[opt] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys) +-- instead of standard JSON. +-- @treturn string The JSON representation of the input. +-- @throws If the object contains a value which cannot be +-- serialised. This includes functions and tables which appear multiple +-- times. +-- @usage textutils.serializeJSON({ values = { 1, "2", true } }) function serializeJSON( t, bNBTStyle ) expect(1, t, "table", "string", "number", "boolean") expect(2, bNBTStyle, "boolean", "nil") @@ -334,6 +447,13 @@ function serializeJSON( t, bNBTStyle ) return serializeJSONImpl( t, tTracking, bNBTStyle or false ) end +serialiseJSON = serializeJSON -- GB version + +--- Replaces certain characters in a string to make it safe for use in URLs or POST data. +-- +-- @tparam string str The string to encode +-- @treturn string The encoded string. +-- @usage print("https://example.com/?view=" .. textutils.urlEncode(read())) function urlEncode( str ) expect(1, str, "string") if str then @@ -356,6 +476,23 @@ function urlEncode( str ) end local tEmpty = {} + +--- Provides a list of possible completions for a partial Lua expression. +-- +-- If the completed element is a table, suggestions will have `.` appended to +-- them. Similarly, functions have `(` appended to them. +-- +-- @tparam string sSearchText The partial expression to complete, such as a +-- variable name or table index. +-- +-- @tparam[opt] table tSearchTable The table to find variables in, defaulting to +-- the global environment (@{_G}). The function also searches the "parent" +-- environment via the `__index` metatable field. +-- +-- @treturn { string... } The (possibly empty) list of completions. +-- @see shell.setCompletionFunction +-- @see read +-- @usage textutils.complete( "pa", getfenv() ) function complete( sSearchText, tSearchTable ) expect(1, sSearchText, "string") expect(2, tSearchTable, "table", "nil") @@ -431,8 +568,3 @@ function complete( sSearchText, tSearchTable ) table.sort( tResults ) return tResults end - --- GB versions -serialise = serialize -unserialise = unserialize -serialiseJSON = serializeJSON diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua index c471212a4..9b5fab369 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua @@ -1,3 +1,6 @@ +--- The turtle API allows you to control your turtle. +-- +-- @module turtle if not turtle then error( "Cannot load turtle API on computer", 2 ) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua b/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua index 4d6c159f9..e19937bba 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua @@ -1,85 +1,178 @@ +--- The vector API provides methods to create and manipulate vectors. +-- +-- An introduction to vectors can be found on [Wikipedia][wiki]. +-- +-- [wiki]: http://en.wikipedia.org/wiki/Euclidean_vector +-- +-- @module vector +--- A 3-dimensional vector, with `x`, `y`, and `z` values. +-- +-- This is suitable for representing both position and directional vectors. +-- +-- @type Vector local vector = { - add = function( self, o ) - return vector.new( - self.x + o.x, - self.y + o.y, - self.z + o.z - ) - end, - sub = function( self, o ) - return vector.new( - self.x - o.x, - self.y - o.y, - self.z - o.z - ) - end, - mul = function( self, m ) - return vector.new( - self.x * m, - self.y * m, - self.z * m - ) - end, - div = function( self, m ) - return vector.new( - self.x / m, - self.y / m, - self.z / m - ) - end, - unm = function( self ) - return vector.new( - -self.x, - -self.y, - -self.z - ) - end, - dot = function( self, o ) - return self.x * o.x + self.y * o.y + self.z * o.z - end, - cross = function( self, o ) - return vector.new( - self.y * o.z - self.z * o.y, - self.z * o.x - self.x * o.z, - self.x * o.y - self.y * o.x - ) - end, - length = function( self ) - return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) - end, - normalize = function( self ) - return self:mul( 1 / self:length() ) - end, - round = function( self, nTolerance ) - nTolerance = nTolerance or 1.0 - return vector.new( - math.floor( (self.x + nTolerance * 0.5) / nTolerance ) * nTolerance, - math.floor( (self.y + nTolerance * 0.5) / nTolerance ) * nTolerance, - math.floor( (self.z + nTolerance * 0.5) / nTolerance ) * nTolerance - ) - end, - tostring = function( self ) - return self.x .. "," .. self.y .. "," .. self.z - end, + --- Adds two vectors together. + -- + -- @tparam Vector self The first vector to add. + -- @tparam Vector o The second vector to add. + -- @treturn Vector The resulting vector + -- @usage v1:add(v2) + -- @usage v1 + v2 + add = function(self, o) + return vector.new( + self.x + o.x, + self.y + o.y, + self.z + o.z + ) + end, + + --- Subtracts one vector from another. + -- + -- @tparam Vector self The vector to subtract from. + -- @tparam Vector o The vector to subtract. + -- @treturn Vector The resulting vector + -- @usage v1:sub(v2) + -- @usage v1 - v2 + sub = function(self, o) + return vector.new( + self.x - o.x, + self.y - o.y, + self.z - o.z + ) + end, + + --- Multiplies a vector by a scalar value. + -- + -- @tparam Vector self The vector to multiply. + -- @tparam number m The scalar value to multiply with. + -- @treturn Vector A vector with value `(x * m, y * m, z * m)`. + -- @usage v:mul(3) + -- @usage v * 3 + mul = function(self, m) + return vector.new( + self.x * m, + self.y * m, + self.z * m + ) + end, + + --- Divides a vector by a scalar value. + -- + -- @tparam Vector self The vector to divide. + -- @tparam number m The scalar value to divide by. + -- @treturn Vector A vector with value `(x / m, y / m, z / m)`. + -- @usage v:div(3) + -- @usage v / 3 + div = function(self, m) + return vector.new( + self.x / m, + self.y / m, + self.z / m + ) + end, + + --- Negate a vector + -- + -- @tparam Vector self The vector to negate. + -- @treturn Vector The negated vector. + -- @usage -v + unm = function(self) + return vector.new( + -self.x, + -self.y, + -self.z + ) + end, + + --- Compute the dot product of two vectors + -- + -- @tparam Vector self The first vector to compute the dot product of. + -- @tparam Vector o The second vector to compute the dot product of. + -- @treturn Vector The dot product of `self` and `o`. + -- @usage v1:dot(v2) + dot = function(self, o) + return self.x * o.x + self.y * o.y + self.z * o.z + end, + + --- Compute the cross product of two vectors + -- + -- @tparam Vector self The first vector to compute the cross product of. + -- @tparam Vector o The second vector to compute the cross product of. + -- @treturn Vector The cross product of `self` and `o`. + -- @usage v1:cross(v2) + cross = function(self, o) + return vector.new( + self.y * o.z - self.z * o.y, + self.z * o.x - self.x * o.z, + self.x * o.y - self.y * o.x + ) + end, + + --- Get the length (also referred to as magnitude) of this vector. + -- @tparam Vector self This vector. + -- @treturn number The length of this vector. + length = function(self) + return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) + end, + + --- Divide this vector by its length, producing with the same direction, but + -- of length 1. + -- + -- @tparam Vector self The vector to normalise + -- @treturn Vector The normalised vector + -- @usage v:normalize() + normalize = function(self) + return self:mul( 1 / self:length() ) + end, + + --- Construct a vector with each dimension rounded to the nearest value. + -- + -- @tparam Vector self The vector to round + -- @tparam[opt] number tolerance The tolerance that we should round to, + -- defaulting to 1. For instance, a tolerance of 0.5 will round to the + -- nearest 0.5. + -- @treturn Vector The rounded vector. + round = function(self, tolerance) + tolerance = tolerance or 1.0 + return vector.new( + math.floor((self.x + tolerance * 0.5) / tolerance) * tolerance, + math.floor((self.y + tolerance * 0.5) / tolerance) * tolerance, + math.floor((self.z + tolerance * 0.5) / tolerance) * tolerance + ) + end, + + --- Convert this vector into a string, for pretty printing. + -- + -- @tparam Vector self This vector. + -- @treturn string This vector's string representation. + -- @usage v:tostring() + -- @usage tostring(v) + tostring = function(self) + return self.x .. "," .. self.y .. "," .. self.z + end, } local vmetatable = { - __index = vector, - __add = vector.add, - __sub = vector.sub, - __mul = vector.mul, - __div = vector.div, - __unm = vector.unm, - __tostring = vector.tostring, + __index = vector, + __add = vector.add, + __sub = vector.sub, + __mul = vector.mul, + __div = vector.div, + __unm = vector.unm, + __tostring = vector.tostring, } -function new( x, y, z ) - local v = { - x = tonumber(x) or 0, - y = tonumber(y) or 0, - z = tonumber(z) or 0, - } - setmetatable( v, vmetatable ) - return v +--- Construct a new @{Vector} with the given coordinates. +-- +-- @tparam number x The X coordinate or direction of the vector. +-- @tparam number y The Y coordinate or direction of the vector. +-- @tparam number z The Z coordinate or direction of the vector. +-- @treturn Vector The constructed vector. +function new(x, y, z) + return setmetatable({ + x = tonumber(x) or 0, + y = tonumber(y) or 0, + z = tonumber(z) or 0, + }, vmetatable) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua index 0e24b7696..5617c2175 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua @@ -1,3 +1,32 @@ +--- The Window API allows easy definition of spaces within the display that can +-- be written/drawn to, then later redrawn/repositioned/etc as need be. The API +-- itself contains only one function, @{window.create}, which returns the +-- windows themselves. +-- +-- Windows are considered terminal objects - as such, they have access to nearly +-- all the commands in the term API (plus a few extras of their own, listed +-- within said API) and are valid targets to redirect to. +-- +-- Each window has a "parent" terminal object, which can be the computer's own +-- display, a monitor, another window or even other, user-defined terminal +-- objects. Whenever a window is rendered to, the actual screen-writing is +-- performed via that parent (or, if that has one too, then that parent, and so +-- forth). Bear in mind that the cursor of a window's parent will hence be moved +-- around etc when writing a given child window. +-- +-- Windows retain a memory of everything rendered "through" them (hence acting +-- as display buffers), and if the parent's display is wiped, the window's +-- content can be easily redrawn later. A window may also be flagged as +-- invisible, preventing any changes to it from being rendered until it's +-- flagged as visible once more. +-- +-- A parent terminal object may have multiple children assigned to it, and +-- windows may overlap. For example, the Multishell system functions by +-- assigning each tab a window covering the screen, each using the starting +-- terminal display as its parent, and only one of which is visible at a time. +-- +-- @module window + local expect = dofile("rom/modules/main/cc/expect.lua").expect local tHex = { @@ -23,6 +52,24 @@ local type = type local string_rep = string.rep local string_sub = string.sub +--- Returns a terminal object that is a space within the specified parent +-- terminal object. This can then be used (or even redirected to) in the same +-- manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of +-- functions available to it. +-- +-- @{term} itself may not be passed as the parent, though @{term.native} is +-- acceptable. Generally, @{term.current} or a wrapped monitor will be most +-- suitable, though windows may even have other windows assigned as their +-- parents. +-- +-- @tparam term.Redirect parent The parent terminal redirect to draw to. +-- @tparam number nX The x coordinate this window is drawn at in the parent terminal +-- @tparam number nY The y coordinate this window is drawn at in the parent terminal +-- @tparam number nWidth The width of this window +-- @tparam number nHeight The height of this window +-- @tparam[opt] boolean bStartVisible Whether this window is visible by +-- default. Defaults to `true`. +-- @treturn Window The constructed window function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) expect(1, parent, "table") expect(2, nX, "number") @@ -182,7 +229,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) end end - -- Terminal implementation + --- Terminal implementation + -- + -- @type Window local window = {} function window.write( sText ) diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua index 7b1a53e8c..1791a29a0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua @@ -7,7 +7,7 @@ local native_select, native_type = select, type --- Expect an argument to have a specific type. -- --- @tparam int index The 1-based argument index. +-- @tparam number index The 1-based argument index. -- @param value The argument's value. -- @tparam string ... The allowed types of the argument. -- @throws If the value is not one of the allowed types. diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua index 2fdaee187..5a34ad389 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -2,7 +2,7 @@ -- aesthetically pleasing manner. -- -- In order to display something using @{cc.pretty}, you build up a series of --- @{documents|Doc}. These behave a little bit like strings; you can concatenate +-- @{Doc|documents}. These behave a little bit like strings; you can concatenate -- them together and then print them to the screen. -- -- However, documents also allow you to control how they should be printed. There @@ -28,7 +28,7 @@ local function append(out, value) out[n], out.n = value, n end ---- A document, which +--- A document containing formatted text, with multiple possible layouts. -- -- Documents effectively represent a sequence of strings in alternative layouts, -- which we will try to print in the most compact form necessary. @@ -61,7 +61,7 @@ end -- -- @tparam string text The string to construct a new document with. -- @tparam[opt] number colour The colour this text should be printed with. If not given, we default to the current --- colour. +-- colour. -- @treturn Doc The document with the provided text. local function text(text, colour) expect(1, text, "string") @@ -94,7 +94,7 @@ local function text(text, colour) end --- Concatenate several documents together. This behaves very similar to string concatenation. - +-- -- @tparam Doc|string ... The documents to concatenate. -- @treturn Doc The concatenated documents. -- @usage pretty.concat(doc1, " - ", doc2) @@ -113,7 +113,7 @@ local function concat(...) return setmetatable(args, Doc) end -Doc.__concat = concat +Doc.__concat = concat --- @local --- Indent later lines of the given document with the given number of spaces. -- @@ -121,7 +121,7 @@ Doc.__concat = concat -- ```txt -- foo -- bar --- `` +-- ``` -- by two spaces will produce -- ```txt -- foo @@ -271,8 +271,9 @@ end -- -- @tparam Doc doc The document to render. -- @tparam[opt] number width The maximum width of this document. Note that long strings will not be wrapped to --- fit this width - it is only used for finding the best layout. +-- fit this width - it is only used for finding the best layout. -- @tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in. +-- @treturn string The rendered document as a string. local function render(doc, width, ribbon_frac) if getmetatable(doc) ~= Doc then expect(1, doc, "document") end expect(2, width, "number", "nil") @@ -316,6 +317,8 @@ local function render(doc, width, ribbon_frac) return table.concat(out, "", 1, out.n) end +Doc.__tostring = render --- @local + local keywords = { [ "and" ] = true, [ "break" ] = true, [ "do" ] = true, [ "else" ] = true, [ "elseif" ] = true, [ "end" ] = true, [ "false" ] = true, [ "for" ] = true, diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua index 20dcd4cd2..2fe853b95 100644 --- a/src/test/resources/test-rom/mcfly.lua +++ b/src/test/resources/test-rom/mcfly.lua @@ -356,32 +356,33 @@ function expect_mt:called_with_matching(...) return called_with_check(matches, self, ...) end -local expect = setmetatable({ - --- Construct an expectation on the error message calling this function - -- produces - -- - -- @tparam fun The function to call - -- @param ... The function arguments - -- @return The new expectation - error = function(fun, ...) - local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2) - if ok then fail("expected function to error") end - if res:sub(1, #line) == line then - res = res:sub(#line + 1) - elseif res:sub(1, 7) == "pcall: " then - res = res:sub(8) - end - return setmetatable({ value = res }, expect_mt) - end, -}, { - --- Construct a new expectation from the provided value - -- - -- @param value The value to apply assertions to - -- @return The new expectation - __call = function(_, value) - return setmetatable({ value = value }, expect_mt) - end, -}) +local expect = {} +setmetatable(expect, expect) + +--- Construct an expectation on the error message calling this function +-- produces +-- +-- @tparam fun The function to call +-- @param ... The function arguments +-- @return The new expectation +function expect.error(fun, ...) + local ok, res = pcall(fun, ...) local _, line = pcall(error, "", 2) + if ok then fail("expected function to error") end + if res:sub(1, #line) == line then + res = res:sub(#line + 1) + elseif res:sub(1, 7) == "pcall: " then + res = res:sub(8) + end + return setmetatable({ value = res }, expect_mt) +end + +--- Construct a new expectation from the provided value +-- +-- @param value The value to apply assertions to +-- @return The new expectation +function expect:__call(value) + return setmetatable({ value = value }, expect_mt) +end --- The stack of "describe"s. local test_stack = { n = 0 } @@ -390,7 +391,8 @@ local test_stack = { n = 0 } local tests_locked = false --- The list of tests that we'll run -local test_list, test_map, test_count = { }, { }, 0 +local test_list = {} +local test_map, test_count = {}, 0 --- Add a new test to our queue. -- diff --git a/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua index 38b2ecc3c..2949c1a75 100644 --- a/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua +++ b/src/test/resources/test-rom/spec/modules/cc/pretty_spec.lua @@ -163,7 +163,7 @@ describe("cc.pretty", function() end) describe("pretty", function() - --- We make use of "render" here, as it's considerably easier than checking against the actual structure. + -- We make use of "render" here, as it's considerably easier than checking against the actual structure. -- However, it does also mean our tests are less unit-like. local function pretty(x, width) return pp.render(pp.pretty(x), width) end From e52d98ad8bcce5b85647921a7aa12168451a1ac6 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 10 Apr 2020 14:28:46 +0100 Subject: [PATCH 18/35] Make IDAssigner.getNextID synchronized This should prevent race conditions when allocating IDs. Fixes #386. --- src/main/java/dan200/computercraft/shared/util/IDAssigner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/dan200/computercraft/shared/util/IDAssigner.java b/src/main/java/dan200/computercraft/shared/util/IDAssigner.java index fd0f222ab..fbbf461bf 100644 --- a/src/main/java/dan200/computercraft/shared/util/IDAssigner.java +++ b/src/main/java/dan200/computercraft/shared/util/IDAssigner.java @@ -36,7 +36,7 @@ public final class IDAssigner return getNextID( file, false ); } - private static int getNextID( File location, boolean directory ) + private static synchronized int getNextID( File location, boolean directory ) { // Determine where to locate ID file File lastIdFile; From 062977336a45f90612e075f90d5eee7360c5e880 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Fri, 10 Apr 2020 14:43:42 +0100 Subject: [PATCH 19/35] Handle missing file metadata on zip files - Return EPOCH if a zip entry's creation/modification/access time is missing. - If a BasicFileAttributes.*Time method returns null, use 0 as our time. This shouldn't happen, but is a good sanity check. Fixes #371 --- .../dan200/computercraft/core/apis/FSAPI.java | 10 ++++-- .../core/filesystem/JarMount.java | 15 +++++++-- .../core/filesystem/JarMountTest.java | 31 ++++++++++++++++--- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/main/java/dan200/computercraft/core/apis/FSAPI.java b/src/main/java/dan200/computercraft/core/apis/FSAPI.java index 74f786f23..90a866a53 100644 --- a/src/main/java/dan200/computercraft/core/apis/FSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/FSAPI.java @@ -23,6 +23,7 @@ import java.io.BufferedWriter; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; import java.util.HashMap; import java.util.Map; import java.util.OptionalLong; @@ -359,8 +360,8 @@ public class FSAPI implements ILuaAPI { BasicFileAttributes attributes = m_fileSystem.getAttributes( path ); Map result = new HashMap<>(); - result.put( "modification", attributes.lastModifiedTime().toMillis() ); - result.put( "created", attributes.creationTime().toMillis() ); + result.put( "modification", getFileTime( attributes.lastModifiedTime() ) ); + result.put( "created", getFileTime( attributes.creationTime() ) ); result.put( "size", attributes.isDirectory() ? 0 : attributes.size() ); result.put( "isDir", attributes.isDirectory() ); return new Object[] { result }; @@ -375,4 +376,9 @@ public class FSAPI implements ILuaAPI return null; } } + + private static long getFileTime( FileTime time ) + { + return time == null ? 0 : time.toMillis(); + } } diff --git a/src/main/java/dan200/computercraft/core/filesystem/JarMount.java b/src/main/java/dan200/computercraft/core/filesystem/JarMount.java index 2cf55a747..e2a1694e0 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/JarMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/JarMount.java @@ -25,6 +25,7 @@ import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.time.Instant; import java.util.Enumeration; import java.util.HashMap; import java.util.List; @@ -290,19 +291,20 @@ public class JarMount implements IMount @Override public FileTime lastModifiedTime() { - return entry.getLastModifiedTime(); + return orEpoch( entry.getLastModifiedTime() ); } @Override public FileTime lastAccessTime() { - return entry.getLastAccessTime(); + return orEpoch( entry.getLastAccessTime() ); } @Override public FileTime creationTime() { - return entry.getCreationTime(); + FileTime time = entry.getCreationTime(); + return time == null ? lastModifiedTime() : time; } @Override @@ -340,5 +342,12 @@ public class JarMount implements IMount { return null; } + + private static final FileTime EPOCH = FileTime.from( Instant.EPOCH ); + + private static FileTime orEpoch( FileTime time ) + { + return time == null ? EPOCH : time; + } } } diff --git a/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java b/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java index 31031c96c..4fedb95b5 100644 --- a/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java +++ b/src/test/java/dan200/computercraft/core/filesystem/JarMountTest.java @@ -15,20 +15,24 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import static org.junit.jupiter.api.Assertions.*; -@SuppressWarnings( "deprecation" ) public class JarMountTest { private static final File ZIP_FILE = new File( "test-files/jar-mount.zip" ); + private static final FileTime MODIFY_TIME = FileTime.from( Instant.EPOCH.plus( 2, ChronoUnit.DAYS ) ); + @BeforeAll public static void before() throws IOException { - if( ZIP_FILE.exists() ) return; ZIP_FILE.getParentFile().mkdirs(); try( ZipOutputStream stream = new ZipOutputStream( new FileOutputStream( ZIP_FILE ) ) ) @@ -36,7 +40,7 @@ public class JarMountTest stream.putNextEntry( new ZipEntry( "dir/" ) ); stream.closeEntry(); - stream.putNextEntry( new ZipEntry( "dir/file.lua" ) ); + stream.putNextEntry( new ZipEntry( "dir/file.lua" ).setLastModifiedTime( MODIFY_TIME ) ); stream.write( "print('testing')".getBytes( StandardCharsets.UTF_8 ) ); stream.closeEntry(); } @@ -63,7 +67,7 @@ public class JarMountTest { IMount mount = new JarMount( ZIP_FILE, "dir/file.lua" ); byte[] contents; - try( InputStream stream = mount.openForRead( "" ) ) + try( @SuppressWarnings( "deprecation" ) InputStream stream = mount.openForRead( "" ) ) { contents = ByteStreams.toByteArray( stream ); } @@ -76,11 +80,28 @@ public class JarMountTest { IMount mount = new JarMount( ZIP_FILE, "dir" ); byte[] contents; - try( InputStream stream = mount.openForRead( "file.lua" ) ) + try( @SuppressWarnings( "deprecation" ) InputStream stream = mount.openForRead( "file.lua" ) ) { contents = ByteStreams.toByteArray( stream ); } assertEquals( new String( contents, StandardCharsets.UTF_8 ), "print('testing')" ); } + + @Test + public void fileAttributes() throws IOException + { + BasicFileAttributes attributes = new JarMount( ZIP_FILE, "dir" ).getAttributes( "file.lua" ); + assertFalse( attributes.isDirectory() ); + assertEquals( "print('testing')".length(), attributes.size() ); + assertEquals( MODIFY_TIME, attributes.lastModifiedTime() ); + } + + @Test + public void directoryAttributes() throws IOException + { + BasicFileAttributes attributes = new JarMount( ZIP_FILE, "dir" ).getAttributes( "" ); + assertTrue( attributes.isDirectory() ); + assertEquals( 0, attributes.size() ); + } } From 6a6a87489c4cb530772e3dc64bf1d8723fcb8f53 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Mon, 13 Apr 2020 10:54:57 +0100 Subject: [PATCH 20/35] Add a separate CONTRIBUTING.md file Hopefully provides a /little/ more information about the build process. Hey, also means I only need to add a CoC to complete GH's community tab! In all seriousness, there's probably a lot more I need to flesh out here, such as some kind of vision and guides for issues/PRs. But this at least documents the local development process. Somewhat. --- CONTRIBUTING.md | 36 ++++++++++++++++++++++++++++++++++++ README.md | 16 +++++----------- 2 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..6d3b46feb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing to CC: Tweaked +As with many open source projects, CC: Tweaked thrives on contributions from other people! This document (hopefully) +provides an introduction as to how to get started in helping out. + +If you've any other questions, [just ask the community][community] or [open an issue][new-issue]. + +## Reporting issues +If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, +do use the issue templates - they provide a useful hint on what information to provide. + +## Developing +In order to develop CC: Tweaked, you'll need to download the source code and then run it. This is a pretty simple +process. + + - **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked` + - **Setup Forge:** `./gradlew setupDecompWorkspace` + - **Run Minecraft:** `./gradlew runClient` (or run the `GradleStart` class from your IDE). + +If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`. +These commands may take a few minutes to run the first time, as the environment is set up, but should be much faster +afterwards. + +### Code linters +CC: Tweaked uses a couple of "linters" on its source code, to enforce a consistent style across the project. While these +are run whenever you submit a PR, it's often useful to + + - **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or + `./gradle check`. + - **[illuaminate]:** Checks Lua code for semantic and styleistic issues. See [the usage section][illuaminate-usage] for + how to download and run it. + +[new-issue]: https://github.com/SquidDev-CC/CC-Tweaked/issues/new/choose "Create a new issue" +[community]: README.md#Community "Get in touch with the community." +[checkstyle]: https://checkstyle.org/ +[illuaminate]: https://github.com/SquidDev/illuaminate/ +[illuaminate-usage]: https://github.com/SquidDev/illuaminate/blob/master/README.md#usage diff --git a/README.md b/README.md index fd3003786..78a9cb32e 100644 --- a/README.md +++ b/README.md @@ -37,20 +37,14 @@ several features have been included, such as full block modems, the Cobalt runti computers. ## Contributing -Any contribution is welcome, be that using the mod, reporting bugs or contributing code. In order to start helping -develop CC:T, you'll need to follow these steps: - - - **Clone the repository:** `git clone https://github.com/SquidDev-CC/CC-Tweaked.git && cd CC-Tweaked` - - **Setup Forge:** `./gradlew setupDecompWorkspace` - - **Test your changes:** `./gradlew runClient` (or run the `GradleStart` class from your IDE). - -If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`. +Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to developing the +mod, [check out the instructions here](CONTRIBUTING.md#developing). ## Community If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about -ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.gg/H2UyJXe)! -There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=#computercraft), if -that's more your cup of tea. +ComputerCraft we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.computercraft.cc)! +There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=computercraft), if that's +more your cup of tea. I'd generally recommend you don't contact me directly (email, DM, etc...) unless absolutely necessary (i.e. in order to report exploits). You'll get a far quicker response if you ask the whole community! From ef4b0a563295fc555ef5af73d651109a1daa396a Mon Sep 17 00:00:00 2001 From: SquidDev Date: Mon, 13 Apr 2020 11:00:31 +0100 Subject: [PATCH 21/35] Fix config name for enabling http API It hasn't been http_enable for yonks - slightly worried I didn't notice this earlier. Also don't refer to ComputerCraft.cfg - the name has changed several times across versions, so let's leave it ambiguous. --- src/main/java/dan200/computercraft/shared/Config.java | 2 +- .../assets/computercraft/lua/rom/programs/http/pastebin.lua | 4 ++-- .../assets/computercraft/lua/rom/programs/http/wget.lua | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/dan200/computercraft/shared/Config.java b/src/main/java/dan200/computercraft/shared/Config.java index a58cd1962..65819e69e 100644 --- a/src/main/java/dan200/computercraft/shared/Config.java +++ b/src/main/java/dan200/computercraft/shared/Config.java @@ -175,7 +175,7 @@ public final class Config "for more fine grained control than this)" ); httpWebsocketEnable = config.get( CATEGORY_HTTP, "websocket_enabled", ComputerCraft.http_websocket_enable ); - httpWebsocketEnable.setComment( "Enable use of http websockets. This requires the \"http_enable\" option to also be true." ); + httpWebsocketEnable.setComment( "Enable use of http websockets. This requires the \"http.enabled\" option to also be true." ); httpAllowedDomains = config.get( CATEGORY_HTTP, "allowed_domains", DEFAULT_HTTP_WHITELIST ); httpAllowedDomains.setComment( "A list of wildcards for domains or IP ranges that can be accessed through the " + diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua b/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua index 3e28a2f7a..49266c20e 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua @@ -13,8 +13,8 @@ if #tArgs < 2 then end if not http then - printError( "Pastebin requires http API" ) - printError( "Set http_enable to true in ComputerCraft.cfg" ) + printError( "Pastebin requires the http API" ) + printError( "Set http.enabled to true in CC: Tweaked's config" ) return end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua b/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua index e05def30e..6c5894e77 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua @@ -21,8 +21,8 @@ end local url = table.remove( tArgs, 1 ) if not http then - printError( "wget requires http API" ) - printError( "Set http_enable to true in ComputerCraft.cfg" ) + printError( "wget requires the http API" ) + printError( "Set http.enabled to true in CC: Tweaked's config" ) return end From cb8135a0d1f3228a6df943254384c51ca0e551fc Mon Sep 17 00:00:00 2001 From: SquidDev Date: Thu, 16 Apr 2020 10:48:26 +0100 Subject: [PATCH 22/35] Bump Cobalt version - Remove stub for table.pack/table.unpack. - Remove Lua 5.3 bitlib stub. We're not on 5.3, there's no point emulating it. - Change peripheral.call to correctly adjust the error level. This is a terrible hack, but I believe the only good option. It'd be good to remove load as well, but it's a little more complex due to our injecting of _ENV. Closes #363 --- CONTRIBUTING.md | 2 +- build.gradle | 2 +- .../core/apis/FastLuaException.java | 35 ++++++++++ .../core/apis/PeripheralAPI.java | 30 +++++---- .../core/lua/CobaltLuaMachine.java | 1 + .../assets/computercraft/lua/bios.lua | 66 ------------------- .../core/ComputerTestDelegate.java | 36 ++++++++++ src/test/resources/test-rom/mcfly.lua | 20 +++++- .../test-rom/spec/apis/peripheral_spec.lua | 7 ++ .../spec/programs/peripherals_spec.lua | 1 + 10 files changed, 119 insertions(+), 81 deletions(-) create mode 100644 src/main/java/dan200/computercraft/core/apis/FastLuaException.java diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d3b46feb..a5273632b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,7 +22,7 @@ afterwards. ### Code linters CC: Tweaked uses a couple of "linters" on its source code, to enforce a consistent style across the project. While these -are run whenever you submit a PR, it's often useful to +are run whenever you submit a PR, it's often useful to run this before committing. - **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or `./gradle check`. diff --git a/build.gradle b/build.gradle index f2fa11e96..df2375cc5 100644 --- a/build.gradle +++ b/build.gradle @@ -77,7 +77,7 @@ dependencies { runtime "mezz.jei:jei_1.12.2:4.15.0.269" - shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT' + shade 'org.squiddev:Cobalt:0.5.1-SNAPSHOT' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.2' diff --git a/src/main/java/dan200/computercraft/core/apis/FastLuaException.java b/src/main/java/dan200/computercraft/core/apis/FastLuaException.java new file mode 100644 index 000000000..6ef64f275 --- /dev/null +++ b/src/main/java/dan200/computercraft/core/apis/FastLuaException.java @@ -0,0 +1,35 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ + +package dan200.computercraft.core.apis; + +import dan200.computercraft.api.lua.LuaException; + +import javax.annotation.Nullable; + +/** + * A Lua exception which does not contain its stack trace. + */ +public class FastLuaException extends LuaException +{ + private static final long serialVersionUID = 5957864899303561143L; + + public FastLuaException( @Nullable String message ) + { + super( message ); + } + + public FastLuaException( @Nullable String message, int level ) + { + super( message, level ); + } + + @Override + public synchronized Throwable fillInStackTrace() + { + return this; + } +} diff --git a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java index c289c85a2..43dc63077 100644 --- a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java @@ -383,22 +383,30 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange String methodName = getString( args, 1 ); Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length ); - if( side != null ) + if( side == null ) throw new LuaException( "No peripheral attached" ); + + PeripheralWrapper p; + synchronized( m_peripherals ) { - PeripheralWrapper p; - synchronized( m_peripherals ) - { - p = m_peripherals[side.ordinal()]; - } - if( p != null ) - { - return p.call( context, methodName, methodArgs ); - } + p = m_peripherals[side.ordinal()]; + } + if( p == null ) throw new LuaException( "No peripheral attached" ); + + try + { + return p.call( context, methodName, methodArgs ); + } + catch( LuaException e ) + { + // We increase the error level by one in order to shift the error level to where peripheral.call was + // invoked. It would be possible to do it in Lua code, but would add significantly more overhead. + if( e.getLevel() > 0 ) throw new FastLuaException( e.getMessage(), e.getLevel() + 1 ); + throw e; } - throw new LuaException( "No peripheral attached" ); } default: return null; } } + } diff --git a/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java b/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java index 08c3231f0..8e5e88bb0 100644 --- a/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java +++ b/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java @@ -92,6 +92,7 @@ public class CobaltLuaMachine implements ILuaMachine m_globals.load( state, new MathLib() ); m_globals.load( state, new CoroutineLib() ); m_globals.load( state, new Bit32Lib() ); + m_globals.load( state, new Utf8Lib() ); if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() ); // Remove globals we don't want to expose diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index 377255845..faffa2cea 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -70,8 +70,6 @@ if _VERSION == "Lua 5.1" then error( p1, 2 ) end end - table.unpack = unpack - table.pack = function( ... ) return { n = select( "#", ... ), ... } end if _CC_DISABLE_LUA51_FEATURES then -- Remove the Lua 5.1 features that will be removed when we update to Lua 5.2, for compatibility testing. @@ -98,70 +96,6 @@ if _VERSION == "Lua 5.1" then end end -if _VERSION == "Lua 5.3" and not bit32 then - -- If we're on Lua 5.3, install the bit32 api from Lua 5.2 - -- (Loaded from a string so this file will still parse on <5.3 lua) - load( [[ - bit32 = {} - - function bit32.arshift( n, bits ) - if type(n) ~= "number" or type(bits) ~= "number" then - error( "Expected number, number", 2 ) - end - return n >> bits - end - - function bit32.band( m, n ) - if type(m) ~= "number" or type(n) ~= "number" then - error( "Expected number, number", 2 ) - end - return m & n - end - - function bit32.bnot( n ) - if type(n) ~= "number" then - error( "Expected number", 2 ) - end - return ~n - end - - function bit32.bor( m, n ) - if type(m) ~= "number" or type(n) ~= "number" then - error( "Expected number, number", 2 ) - end - return m | n - end - - function bit32.btest( m, n ) - if type(m) ~= "number" or type(n) ~= "number" then - error( "Expected number, number", 2 ) - end - return (m & n) ~= 0 - end - - function bit32.bxor( m, n ) - if type(m) ~= "number" or type(n) ~= "number" then - error( "Expected number, number", 2 ) - end - return m ~ n - end - - function bit32.lshift( n, bits ) - if type(n) ~= "number" or type(bits) ~= "number" then - error( "Expected number, number", 2 ) - end - return n << bits - end - - function bit32.rshift( n, bits ) - if type(n) ~= "number" or type(bits) ~= "number" then - error( "Expected number, number", 2 ) - end - return n >> bits - end - ]] )() -end - -- Install lua parts of the os api function os.version() return "CraftOS 1.8" diff --git a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java index 6413d5ef0..fec45a318 100644 --- a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java +++ b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java @@ -10,12 +10,18 @@ import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.core.computer.BasicEnvironment; import dan200.computercraft.core.computer.Computer; +import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.computer.MainThread; import dan200.computercraft.core.filesystem.FileMount; import dan200.computercraft.core.filesystem.FileSystemException; import dan200.computercraft.core.terminal.Terminal; +import dan200.computercraft.shared.peripheral.modem.ModemState; +import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.*; @@ -94,6 +100,7 @@ public class ComputerTestDelegate } computer = new Computer( new BasicEnvironment( mount ), term, 0 ); + computer.getEnvironment().setPeripheral( ComputerSide.TOP, new FakeModem() ); computer.addApi( new ILuaAPI() { @Override @@ -417,4 +424,33 @@ public class ComputerTestDelegate { return name.replace( "\0", " -> " ); } + + private static class FakeModem extends WirelessModemPeripheral + { + FakeModem() + { + super( new ModemState(), true ); + } + + @Nonnull + @Override + @SuppressWarnings( "ConstantConditions" ) + public World getWorld() + { + return null; + } + + @Nonnull + @Override + public Vec3d getPosition() + { + return Vec3d.ZERO; + } + + @Override + public boolean equals( @Nullable IPeripheral other ) + { + return this == other; + } + } } diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua index 2fe853b95..ecb01eaac 100644 --- a/src/test/resources/test-rom/mcfly.lua +++ b/src/test/resources/test-rom/mcfly.lua @@ -224,7 +224,7 @@ expect_mt.ne = expect_mt.not_equals function expect_mt:type(exp_type) local actual_type = type(self.value) if exp_type ~= actual_type then - fail(("Expected value of type %s\n got %s"):format(exp_type, actual_type)) + fail(("Expected value of type %s\nbut got %s"):format(exp_type, actual_type)) end return self @@ -273,7 +273,7 @@ end -- @throws If they are not equivalent function expect_mt:same(value) if not matches({}, true, self.value, value) then - fail(("Expected %s\n but got %s"):format(format(value), format(self.value))) + fail(("Expected %s\nbut got %s"):format(format(value), format(self.value))) end return self @@ -356,6 +356,22 @@ function expect_mt:called_with_matching(...) return called_with_check(matches, self, ...) end +--- Assert that this expectation matches a Lua pattern +-- +-- @tparam string pattern The pattern to match against +-- @throws If it does not match this pattern. +function expect_mt:str_match(pattern) + local actual_type = type(self.value) + if actual_type ~= "string" then + fail(("Expected value of type string\nbut got %s"):format(actual_type)) + end + if not self.value:find(pattern) then + fail(("Expected %q\n to match pattern %q"):format(self.value, pattern)) + end + + return self +end + local expect = {} setmetatable(expect, expect) diff --git a/src/test/resources/test-rom/spec/apis/peripheral_spec.lua b/src/test/resources/test-rom/spec/apis/peripheral_spec.lua index 8fca583e1..c3fb5675f 100644 --- a/src/test/resources/test-rom/spec/apis/peripheral_spec.lua +++ b/src/test/resources/test-rom/spec/apis/peripheral_spec.lua @@ -1,4 +1,6 @@ describe("The peripheral library", function() + local it_modem = peripheral.getType("top") == "modem" and it or pending + describe("peripheral.isPresent", function() it("validates arguments", function() peripheral.isPresent("") @@ -26,6 +28,11 @@ describe("The peripheral library", function() expect.error(peripheral.call, nil):eq("bad argument #1 (expected string, got nil)") expect.error(peripheral.call, "", nil):eq("bad argument #2 (expected string, got nil)") end) + + it_modem("has the correct error location", function() + expect.error(function() peripheral.call("top", "isOpen", false) end) + :str_match("^peripheral_spec.lua:%d+: bad argument #1 %(number expected, got boolean%)$") + end) end) describe("peripheral.wrap", function() diff --git a/src/test/resources/test-rom/spec/programs/peripherals_spec.lua b/src/test/resources/test-rom/spec/programs/peripherals_spec.lua index 6c9d433bd..68762e79f 100644 --- a/src/test/resources/test-rom/spec/programs/peripherals_spec.lua +++ b/src/test/resources/test-rom/spec/programs/peripherals_spec.lua @@ -2,6 +2,7 @@ local capture = require "test_helpers".capture_program describe("The peripherals program", function() it("says when there are no peripherals", function() + stub(peripheral, 'getNames', function() return {} end) expect(capture(stub, "peripherals")) :matches { ok = true, output = "Attached Peripherals:\nNone\n", error = "" } end) From f9f94b83046f531e6d87dc695cb021a95c462b3e Mon Sep 17 00:00:00 2001 From: SquidDev Date: Thu, 16 Apr 2020 18:18:36 +0100 Subject: [PATCH 23/35] Rewrite our documentation index We're going to flesh this out a lot in the future, but this'll have to do for now. --- .gitignore | 1 + doc/index.md | 18 ++++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index cc7ddcea3..51cff3b7a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /build /out /doc/**/*.html +/doc/index.json # Runtime directories /run diff --git a/doc/index.md b/doc/index.md index efc2afafa..c22acd230 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,13 +1,11 @@ -# CC: Tweaked +# ![CC: Tweaked](logo.png) [![Download CC: Tweaked on CurseForge](http://cf.way2muchnoise.eu/title/cc-tweaked.svg)](https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked on CurseForge") -This is a small website to test documentation generation from Lua source code. +CC: Tweaked is a fork of [ComputerCraft], adding programmable computers, turtles and more to Minecraft. -This is still very much in the proof-of-concept stage. We've rolled own own documentation -generation tool, and there's a couple of missing features. Furthermore, Java-based APIs -(such as Lua builtins, or @{os}) are not documented. +This website contains documentation for all Lua libraries and APIs from the latest version of CC: Tweaked. This +documentation is still in development, so will most likely be incomplete. If you've found something you think is wrong, +or would like to help out [please get in touch on GitHub][gh]. -For more information, please check out [the GitHub issue][gh_issue] and [the -documented source][gh_branch]. - -[gh_issue]: https://github.com/SquidDev-CC/CC-Tweaked/issues/133 -[gh_branch]: https://github.com/SquidDev-CC/CC-Tweaked/tree/feature/doc-gen +[bug]: https://github.com/SquidDev-CC/CC-Tweaked/issues/new/choose +[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub" +[gh]: https://github.com/SquidDev-CC/CC-Tweaked "CC:Tweaked on GitHub" From 865fc239a0af201a99cff92df995a25cc6b727ef Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sat, 18 Apr 2020 10:09:40 +0100 Subject: [PATCH 24/35] Reformat bracketed expressions in Lua - Parenthesised expressions (function calls, arguments, etc...) should never have spaces in them. - Tables always will have spaces inside. --- illuaminate.sexp | 11 +- .../assets/computercraft/lua/bios.lua | 384 +++++++------- .../computercraft/lua/rom/apis/colors.lua | 28 +- .../lua/rom/apis/command/commands.lua | 30 +- .../computercraft/lua/rom/apis/disk.lua | 78 +-- .../assets/computercraft/lua/rom/apis/gps.lua | 66 +-- .../computercraft/lua/rom/apis/help.lua | 36 +- .../computercraft/lua/rom/apis/keys.lua | 6 +- .../computercraft/lua/rom/apis/paintutils.lua | 84 ++-- .../computercraft/lua/rom/apis/parallel.lua | 30 +- .../computercraft/lua/rom/apis/rednet.lua | 126 ++--- .../computercraft/lua/rom/apis/settings.lua | 34 +- .../computercraft/lua/rom/apis/term.lua | 24 +- .../computercraft/lua/rom/apis/textutils.lua | 226 ++++----- .../lua/rom/apis/turtle/turtle.lua | 26 +- .../computercraft/lua/rom/apis/vector.lua | 4 +- .../computercraft/lua/rom/apis/window.lua | 188 +++---- .../lua/rom/modules/main/cc/expect.lua | 4 +- .../lua/rom/modules/main/cc/pretty.lua | 10 +- .../lua/rom/programs/advanced/bg.lua | 6 +- .../lua/rom/programs/advanced/fg.lua | 10 +- .../lua/rom/programs/advanced/multishell.lua | 162 +++--- .../computercraft/lua/rom/programs/alias.lua | 14 +- .../computercraft/lua/rom/programs/apis.lua | 14 +- .../computercraft/lua/rom/programs/cd.lua | 10 +- .../computercraft/lua/rom/programs/clear.lua | 2 +- .../lua/rom/programs/command/commands.lua | 12 +- .../lua/rom/programs/command/exec.lua | 24 +- .../computercraft/lua/rom/programs/copy.lua | 32 +- .../computercraft/lua/rom/programs/drive.lua | 16 +- .../computercraft/lua/rom/programs/edit.lua | 296 +++++------ .../computercraft/lua/rom/programs/eject.lua | 8 +- .../lua/rom/programs/fun/advanced/paint.lua | 68 +-- .../rom/programs/fun/advanced/redirection.lua | 96 ++-- .../lua/rom/programs/fun/adventure.lua | 474 +++++++++--------- .../computercraft/lua/rom/programs/fun/dj.lua | 26 +- .../lua/rom/programs/fun/hello.lua | 6 +- .../lua/rom/programs/fun/worm.lua | 86 ++-- .../computercraft/lua/rom/programs/gps.lua | 36 +- .../computercraft/lua/rom/programs/help.lua | 12 +- .../lua/rom/programs/http/pastebin.lua | 72 +-- .../lua/rom/programs/http/wget.lua | 50 +- .../computercraft/lua/rom/programs/id.lua | 16 +- .../computercraft/lua/rom/programs/label.lua | 68 +-- .../computercraft/lua/rom/programs/list.lua | 30 +- .../computercraft/lua/rom/programs/lua.lua | 44 +- .../computercraft/lua/rom/programs/mkdir.lua | 16 +- .../lua/rom/programs/monitor.lua | 56 +-- .../computercraft/lua/rom/programs/motd.lua | 2 +- .../computercraft/lua/rom/programs/move.lua | 20 +- .../lua/rom/programs/peripherals.lua | 6 +- .../lua/rom/programs/pocket/equip.lua | 6 +- .../lua/rom/programs/pocket/falling.lua | 10 +- .../lua/rom/programs/pocket/unequip.lua | 6 +- .../lua/rom/programs/programs.lua | 4 +- .../computercraft/lua/rom/programs/reboot.lua | 8 +- .../lua/rom/programs/rednet/chat.lua | 254 +++++----- .../lua/rom/programs/rednet/repeat.lua | 56 +-- .../lua/rom/programs/redstone.lua | 76 +-- .../computercraft/lua/rom/programs/rename.lua | 20 +- .../computercraft/lua/rom/programs/set.lua | 16 +- .../computercraft/lua/rom/programs/shell.lua | 254 +++++----- .../lua/rom/programs/shutdown.lua | 8 +- .../computercraft/lua/rom/programs/time.lua | 2 +- .../lua/rom/programs/turtle/craft.lua | 20 +- .../lua/rom/programs/turtle/dance.lua | 18 +- .../lua/rom/programs/turtle/equip.lua | 26 +- .../lua/rom/programs/turtle/excavate.lua | 78 +-- .../lua/rom/programs/turtle/go.lua | 12 +- .../lua/rom/programs/turtle/refuel.lua | 16 +- .../lua/rom/programs/turtle/tunnel.lua | 32 +- .../lua/rom/programs/turtle/turn.lua | 12 +- .../lua/rom/programs/turtle/unequip.lua | 22 +- .../computercraft/lua/rom/programs/type.lua | 14 +- .../assets/computercraft/lua/rom/startup.lua | 162 +++--- src/test/resources/test-rom/mcfly.lua | 4 +- .../spec/programs/command/exec_spec.lua | 4 +- .../spec/programs/http/pastebin_spec.lua | 2 +- 78 files changed, 2168 insertions(+), 2159 deletions(-) diff --git a/illuaminate.sexp b/illuaminate.sexp index 0a8fe626e..38ed350c5 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -25,6 +25,8 @@ (at / (linters + syntax:string-index + ;; It'd be nice to avoid this, but right now there's a lot of instances of ;; it. -var:set-loop @@ -36,7 +38,14 @@ ;; Suppress a couple of documentation comments warnings for now. We'll ;; hopefully be able to remove them in the future. -doc:undocumented -doc:undocumented-arg -doc:unresolved-reference - -var:unresolved-member)) + -var:unresolved-member) + (lint + (bracket-spaces + (call no-space) + (function-args no-space) + (parens no-space) + (table space) + (index no-space)))) ;; We disable the unused global linter in bios.lua and the APIs. In the future ;; hopefully we'll get illuaminate to handle this. diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index faffa2cea..39dd252c3 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -33,41 +33,41 @@ if _VERSION == "Lua 5.1" then end end - function load( x, name, mode, env ) + function load(x, name, mode, env) expect(1, x, "function", "string") expect(2, name, "string", "nil") expect(3, mode, "string", "nil") expect(4, env, "table", "nil") - local ok, p1, p2 = pcall( function() + local ok, p1, p2 = pcall(function() if type(x) == "string" then - local result, err = nativeloadstring( x, name ) + local result, err = nativeloadstring(x, name) if result then if env then env._ENV = env - nativesetfenv( result, env ) + nativesetfenv(result, env) end return result else return nil, err end else - local result, err = nativeload( x, name ) + local result, err = nativeload(x, name) if result then if env then env._ENV = env - nativesetfenv( result, env ) + nativesetfenv(result, env) end return result else return nil, err end end - end ) + end) if ok then return p1, p2 else - error( p1, 2 ) + error(p1, 2) end end @@ -81,7 +81,7 @@ if _VERSION == "Lua 5.1" then math.log10 = nil table.maxn = nil else - loadstring = function(string, chunkname) return nativeloadstring(string, prefix( chunkname )) end + loadstring = function(string, chunkname) return nativeloadstring(string, prefix(chunkname)) end -- Inject a stub for the old bit library _G.bit = { @@ -101,28 +101,28 @@ function os.version() return "CraftOS 1.8" end -function os.pullEventRaw( sFilter ) - return coroutine.yield( sFilter ) +function os.pullEventRaw(sFilter) + return coroutine.yield(sFilter) end -function os.pullEvent( sFilter ) - local eventData = table.pack( os.pullEventRaw( sFilter ) ) +function os.pullEvent(sFilter) + local eventData = table.pack(os.pullEventRaw(sFilter)) if eventData[1] == "terminate" then - error( "Terminated", 0 ) + error("Terminated", 0) end - return table.unpack( eventData, 1, eventData.n ) + return table.unpack(eventData, 1, eventData.n) end -- Install globals -function sleep( nTime ) +function sleep(nTime) expect(1, nTime, "number", "nil") - local timer = os.startTimer( nTime or 0 ) + local timer = os.startTimer(nTime or 0) repeat - local _, param = os.pullEvent( "timer" ) + local _, param = os.pullEvent("timer") until param == timer end -function write( sText ) +function write(sText) expect(1, sText, "string", "number") local w, h = term.getSize() @@ -143,32 +143,32 @@ function write( sText ) -- Print the line with proper word wrapping sText = tostring(sText) while #sText > 0 do - local whitespace = string.match( sText, "^[ \t]+" ) + local whitespace = string.match(sText, "^[ \t]+") if whitespace then -- Print whitespace - term.write( whitespace ) + term.write(whitespace) x, y = term.getCursorPos() - sText = string.sub( sText, #whitespace + 1 ) + sText = string.sub(sText, #whitespace + 1) end - local newline = string.match( sText, "^\n" ) + local newline = string.match(sText, "^\n") if newline then -- Print newlines newLine() - sText = string.sub( sText, 2 ) + sText = string.sub(sText, 2) end - local text = string.match( sText, "^[^ \t\n]+" ) + local text = string.match(sText, "^[^ \t\n]+") if text then - sText = string.sub( sText, #text + 1 ) + sText = string.sub(sText, #text + 1) if #text > w then -- Print a multiline word while #text > 0 do if x > w then newLine() end - term.write( text ) - text = string.sub( text, w - x + 2 ) + term.write(text) + text = string.sub(text, w - x + 2) x, y = term.getCursorPos() end else @@ -176,7 +176,7 @@ function write( sText ) if x + #text - 1 > w then newLine() end - term.write( text ) + term.write(text) x, y = term.getCursorPos() end end @@ -185,42 +185,42 @@ function write( sText ) return nLinesPrinted end -function print( ... ) +function print(...) local nLinesPrinted = 0 local nLimit = select("#", ...) for n = 1, nLimit do - local s = tostring( select( n, ... ) ) + local s = tostring(select(n, ...)) if n < nLimit then s = s .. "\t" end - nLinesPrinted = nLinesPrinted + write( s ) + nLinesPrinted = nLinesPrinted + write(s) end - nLinesPrinted = nLinesPrinted + write( "\n" ) + nLinesPrinted = nLinesPrinted + write("\n") return nLinesPrinted end -function printError( ... ) +function printError(...) local oldColour if term.isColour() then oldColour = term.getTextColour() - term.setTextColour( colors.red ) + term.setTextColour(colors.red) end - print( ... ) + print(...) if term.isColour() then - term.setTextColour( oldColour ) + term.setTextColour(oldColour) end end -function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) +function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault) expect(1, _sReplaceChar, "string", "nil") expect(2, _tHistory, "table", "nil") expect(3, _fnComplete, "function", "nil") expect(4, _sDefault, "string", "nil") - term.setCursorBlink( true ) + term.setCursorBlink(true) local sLine - if type( _sDefault ) == "string" then + if type(_sDefault) == "string" then sLine = _sDefault else sLine = "" @@ -228,14 +228,14 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) local nHistoryPos local nPos, nScroll = #sLine, 0 if _sReplaceChar then - _sReplaceChar = string.sub( _sReplaceChar, 1, 1 ) + _sReplaceChar = string.sub(_sReplaceChar, 1, 1) end local tCompletions local nCompletion local function recomplete() if _fnComplete and nPos == #sLine then - tCompletions = _fnComplete( sLine ) + tCompletions = _fnComplete(sLine) if tCompletions and #tCompletions > 0 then nCompletion = 1 else @@ -255,7 +255,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) local w = term.getSize() local sx = term.getCursorPos() - local function redraw( _bClear ) + local function redraw(_bClear) local cursor_pos = nPos - nScroll if sx + cursor_pos >= w then -- We've moved beyond the RHS, ensure we're on the edge. @@ -266,39 +266,39 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) end local _, cy = term.getCursorPos() - term.setCursorPos( sx, cy ) + term.setCursorPos(sx, cy) local sReplace = _bClear and " " or _sReplaceChar if sReplace then - term.write( string.rep( sReplace, math.max( #sLine - nScroll, 0 ) ) ) + term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0))) else - term.write( string.sub( sLine, nScroll + 1 ) ) + term.write(string.sub(sLine, nScroll + 1)) end if nCompletion then - local sCompletion = tCompletions[ nCompletion ] + local sCompletion = tCompletions[nCompletion] local oldText, oldBg if not _bClear then oldText = term.getTextColor() oldBg = term.getBackgroundColor() - term.setTextColor( colors.white ) - term.setBackgroundColor( colors.gray ) + term.setTextColor(colors.white) + term.setBackgroundColor(colors.gray) end if sReplace then - term.write( string.rep( sReplace, #sCompletion ) ) + term.write(string.rep(sReplace, #sCompletion)) else - term.write( sCompletion ) + term.write(sCompletion) end if not _bClear then - term.setTextColor( oldText ) - term.setBackgroundColor( oldBg ) + term.setTextColor(oldText) + term.setBackgroundColor(oldBg) end end - term.setCursorPos( sx + nPos - nScroll, cy ) + term.setCursorPos(sx + nPos - nScroll, cy) end local function clear() - redraw( true ) + redraw(true) end recomplete() @@ -310,7 +310,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) clear() -- Find the common prefix of all the other suggestions which start with the same letter as the current one - local sCompletion = tCompletions[ nCompletion ] + local sCompletion = tCompletions[nCompletion] sLine = sLine .. sCompletion nPos = #sLine @@ -324,7 +324,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) if sEvent == "char" then -- Typed key clear() - sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 ) + sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1) nPos = nPos + 1 recomplete() redraw() @@ -332,7 +332,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) elseif sEvent == "paste" then -- Pasted text clear() - sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 ) + sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1) nPos = nPos + #param recomplete() redraw() @@ -423,7 +423,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) -- Backspace if nPos > 0 then clear() - sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 ) + sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1) nPos = nPos - 1 if nScroll > 0 then nScroll = nScroll - 1 end recomplete() @@ -443,7 +443,7 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) -- Delete if nPos < #sLine then clear() - sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 ) + sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2) recomplete() redraw() end @@ -480,14 +480,14 @@ function read( _sReplaceChar, _tHistory, _fnComplete, _sDefault ) end local _, cy = term.getCursorPos() - term.setCursorBlink( false ) - term.setCursorPos( w + 1, cy ) + term.setCursorBlink(false) + term.setCursorPos(w + 1, cy) print() return sLine end -function loadfile( filename, mode, env ) +function loadfile(filename, mode, env) -- Support the previous `loadfile(filename, env)` form instead. if type(mode) == "table" and env == nil then mode, env = nil, mode @@ -497,81 +497,81 @@ function loadfile( filename, mode, env ) expect(2, mode, "string", "nil") expect(3, env, "table", "nil") - local file = fs.open( filename, "r" ) + local file = fs.open(filename, "r") if not file then return nil, "File not found" end - local func, err = load( file.readAll(), "@" .. fs.getName( filename ), mode, env ) + local func, err = load(file.readAll(), "@" .. fs.getName(filename), mode, env) file.close() return func, err end -function dofile( _sFile ) +function dofile(_sFile) expect(1, _sFile, "string") - local fnFile, e = loadfile( _sFile, nil, _G ) + local fnFile, e = loadfile(_sFile, nil, _G) if fnFile then return fnFile() else - error( e, 2 ) + error(e, 2) end end -- Install the rest of the OS api -function os.run( _tEnv, _sPath, ... ) +function os.run(_tEnv, _sPath, ...) expect(1, _tEnv, "table") expect(2, _sPath, "string") - local tArgs = table.pack( ... ) + local tArgs = table.pack(...) local tEnv = _tEnv - setmetatable( tEnv, { __index = _G } ) - local fnFile, err = loadfile( _sPath, nil, tEnv ) + setmetatable(tEnv, { __index = _G }) + local fnFile, err = loadfile(_sPath, nil, tEnv) if fnFile then - local ok, err = pcall( function() - fnFile( table.unpack( tArgs, 1, tArgs.n ) ) - end ) + local ok, err = pcall(function() + fnFile(table.unpack(tArgs, 1, tArgs.n)) + end) if not ok then if err and err ~= "" then - printError( err ) + printError(err) end return false end return true end if err and err ~= "" then - printError( err ) + printError(err) end return false end local tAPIsLoading = {} -function os.loadAPI( _sPath ) +function os.loadAPI(_sPath) expect(1, _sPath, "string") - local sName = fs.getName( _sPath ) + local sName = fs.getName(_sPath) if sName:sub(-4) == ".lua" then sName = sName:sub(1, -5) end if tAPIsLoading[sName] == true then - printError( "API " .. sName .. " is already being loaded" ) + printError("API " .. sName .. " is already being loaded") return false end tAPIsLoading[sName] = true local tEnv = {} - setmetatable( tEnv, { __index = _G } ) - local fnAPI, err = loadfile( _sPath, nil, tEnv ) + setmetatable(tEnv, { __index = _G }) + local fnAPI, err = loadfile(_sPath, nil, tEnv) if fnAPI then - local ok, err = pcall( fnAPI ) + local ok, err = pcall(fnAPI) if not ok then tAPIsLoading[sName] = nil - return error( "Failed to load API " .. sName .. " due to " .. err, 1 ) + return error("Failed to load API " .. sName .. " due to " .. err, 1) end else tAPIsLoading[sName] = nil - return error( "Failed to load API " .. sName .. " due to " .. err, 1 ) + return error("Failed to load API " .. sName .. " due to " .. err, 1) end local tAPI = {} - for k, v in pairs( tEnv ) do + for k, v in pairs(tEnv) do if k ~= "_ENV" then tAPI[k] = v end @@ -582,15 +582,15 @@ function os.loadAPI( _sPath ) return true end -function os.unloadAPI( _sName ) +function os.unloadAPI(_sName) expect(1, _sName, "string") if _sName ~= "_G" and type(_G[_sName]) == "table" then _G[_sName] = nil end end -function os.sleep( nTime ) - sleep( nTime ) +function os.sleep(nTime) + sleep(nTime) end local nativeShutdown = os.shutdown @@ -619,7 +619,7 @@ if http then PATCH = true, TRACE = true, } - local function checkKey( options, key, ty, opt ) + local function checkKey(options, key, ty, opt) local value = options[key] local valueTy = type(value) @@ -628,24 +628,24 @@ if http then end end - local function checkOptions( options, body ) - checkKey( options, "url", "string" ) + local function checkOptions(options, body) + checkKey(options, "url", "string") if body == false then - checkKey( options, "body", "nil" ) + checkKey(options, "body", "nil") else - checkKey( options, "body", "string", not body ) + checkKey(options, "body", "string", not body) end - checkKey( options, "headers", "table", true ) - checkKey( options, "method", "string", true ) - checkKey( options, "redirect", "boolean", true ) + checkKey(options, "headers", "table", true) + checkKey(options, "method", "string", true) + checkKey(options, "redirect", "boolean", true) if options.method and not methods[options.method] then - error( "Unsupported HTTP method", 3 ) + error("Unsupported HTTP method", 3) end end - local function wrapRequest( _url, ... ) - local ok, err = nativeHTTPRequest( ... ) + local function wrapRequest(_url, ...) + local ok, err = nativeHTTPRequest(...) if ok then while true do local event, param1, param2, param3 = os.pullEvent() @@ -660,34 +660,34 @@ if http then end http.get = function(_url, _headers, _binary) - if type( _url ) == "table" then - checkOptions( _url, false ) - return wrapRequest( _url.url, _url ) + if type(_url) == "table" then + checkOptions(_url, false) + return wrapRequest(_url.url, _url) end expect(1, _url, "string") expect(2, _headers, "table", "nil") expect(3, _binary, "boolean", "nil") - return wrapRequest( _url, _url, nil, _headers, _binary ) + return wrapRequest(_url, _url, nil, _headers, _binary) end http.post = function(_url, _post, _headers, _binary) - if type( _url ) == "table" then - checkOptions( _url, true ) - return wrapRequest( _url.url, _url ) + if type(_url) == "table" then + checkOptions(_url, true) + return wrapRequest(_url.url, _url) end expect(1, _url, "string") expect(2, _post, "string") expect(3, _headers, "table", "nil") expect(4, _binary, "boolean", "nil") - return wrapRequest( _url, _url, _post, _headers, _binary ) + return wrapRequest(_url, _url, _post, _headers, _binary) end - http.request = function( _url, _post, _headers, _binary ) + http.request = function(_url, _post, _headers, _binary) local url - if type( _url ) == "table" then - checkOptions( _url ) + if type(_url) == "table" then + checkOptions(_url) url = _url.url else expect(1, _url, "string") @@ -697,32 +697,32 @@ if http then url = _url.url end - local ok, err = nativeHTTPRequest( _url, _post, _headers, _binary ) + local ok, err = nativeHTTPRequest(_url, _post, _headers, _binary) if not ok then - os.queueEvent( "http_failure", url, err ) + os.queueEvent("http_failure", url, err) end return ok, err end local nativeCheckURL = http.checkURL http.checkURLAsync = nativeCheckURL - http.checkURL = function( _url ) - local ok, err = nativeCheckURL( _url ) + http.checkURL = function(_url) + local ok, err = nativeCheckURL(_url) if not ok then return ok, err end while true do - local _, url, ok, err = os.pullEvent( "http_check" ) + local _, url, ok, err = os.pullEvent("http_check") if url == _url then return ok, err end end end local nativeWebsocket = http.websocket http.websocketAsync = nativeWebsocket - http.websocket = function( _url, _headers ) + http.websocket = function(_url, _headers) expect(1, _url, "string") expect(2, _headers, "table", "nil") - local ok, err = nativeWebsocket( _url, _headers ) + local ok, err = nativeWebsocket(_url, _headers) if not ok then return ok, err end while true do @@ -738,7 +738,7 @@ end -- Install the lua part of the FS api local tEmpty = {} -function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs ) +function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs) expect(1, sPath, "string") expect(2, sLocation, "string") expect(3, bIncludeFiles, "boolean", "nil") @@ -748,49 +748,49 @@ function fs.complete( sPath, sLocation, bIncludeFiles, bIncludeDirs ) bIncludeDirs = bIncludeDirs ~= false local sDir = sLocation local nStart = 1 - local nSlash = string.find( sPath, "[/\\]", nStart ) + local nSlash = string.find(sPath, "[/\\]", nStart) if nSlash == 1 then sDir = "" nStart = 2 end local sName while not sName do - local nSlash = string.find( sPath, "[/\\]", nStart ) + local nSlash = string.find(sPath, "[/\\]", nStart) if nSlash then - local sPart = string.sub( sPath, nStart, nSlash - 1 ) - sDir = fs.combine( sDir, sPart ) + local sPart = string.sub(sPath, nStart, nSlash - 1) + sDir = fs.combine(sDir, sPart) nStart = nSlash + 1 else - sName = string.sub( sPath, nStart ) + sName = string.sub(sPath, nStart) end end - if fs.isDir( sDir ) then + if fs.isDir(sDir) then local tResults = {} if bIncludeDirs and sPath == "" then - table.insert( tResults, "." ) + table.insert(tResults, ".") end if sDir ~= "" then if sPath == "" then - table.insert( tResults, bIncludeDirs and ".." or "../" ) + table.insert(tResults, bIncludeDirs and ".." or "../") elseif sPath == "." then - table.insert( tResults, bIncludeDirs and "." or "./" ) + table.insert(tResults, bIncludeDirs and "." or "./") end end - local tFiles = fs.list( sDir ) + local tFiles = fs.list(sDir) for n = 1, #tFiles do local sFile = tFiles[n] - if #sFile >= #sName and string.sub( sFile, 1, #sName ) == sName then - local bIsDir = fs.isDir( fs.combine( sDir, sFile ) ) - local sResult = string.sub( sFile, #sName + 1 ) + if #sFile >= #sName and string.sub(sFile, 1, #sName) == sName then + local bIsDir = fs.isDir(fs.combine(sDir, sFile)) + local sResult = string.sub(sFile, #sName + 1) if bIsDir then - table.insert( tResults, sResult .. "/" ) + table.insert(tResults, sResult .. "/") if bIncludeDirs and #sResult > 0 then - table.insert( tResults, sResult ) + table.insert(tResults, sResult) end else if bIncludeFiles and #sResult > 0 then - table.insert( tResults, sResult ) + table.insert(tResults, sResult) end end end @@ -802,26 +802,26 @@ end -- Load APIs local bAPIError = false -local tApis = fs.list( "rom/apis" ) -for _, sFile in ipairs( tApis ) do - if string.sub( sFile, 1, 1 ) ~= "." then - local sPath = fs.combine( "rom/apis", sFile ) - if not fs.isDir( sPath ) then - if not os.loadAPI( sPath ) then +local tApis = fs.list("rom/apis") +for _, sFile in ipairs(tApis) do + if string.sub(sFile, 1, 1) ~= "." then + local sPath = fs.combine("rom/apis", sFile) + if not fs.isDir(sPath) then + if not os.loadAPI(sPath) then bAPIError = true end end end end -if turtle and fs.isDir( "rom/apis/turtle" ) then +if turtle and fs.isDir("rom/apis/turtle") then -- Load turtle APIs - local tApis = fs.list( "rom/apis/turtle" ) - for _, sFile in ipairs( tApis ) do - if string.sub( sFile, 1, 1 ) ~= "." then - local sPath = fs.combine( "rom/apis/turtle", sFile ) - if not fs.isDir( sPath ) then - if not os.loadAPI( sPath ) then + local tApis = fs.list("rom/apis/turtle") + for _, sFile in ipairs(tApis) do + if string.sub(sFile, 1, 1) ~= "." then + local sPath = fs.combine("rom/apis/turtle", sFile) + if not fs.isDir(sPath) then + if not os.loadAPI(sPath) then bAPIError = true end end @@ -829,14 +829,14 @@ if turtle and fs.isDir( "rom/apis/turtle" ) then end end -if pocket and fs.isDir( "rom/apis/pocket" ) then +if pocket and fs.isDir("rom/apis/pocket") then -- Load pocket APIs - local tApis = fs.list( "rom/apis/pocket" ) - for _, sFile in ipairs( tApis ) do - if string.sub( sFile, 1, 1 ) ~= "." then - local sPath = fs.combine( "rom/apis/pocket", sFile ) - if not fs.isDir( sPath ) then - if not os.loadAPI( sPath ) then + local tApis = fs.list("rom/apis/pocket") + for _, sFile in ipairs(tApis) do + if string.sub(sFile, 1, 1) ~= "." then + local sPath = fs.combine("rom/apis/pocket", sFile) + if not fs.isDir(sPath) then + if not os.loadAPI(sPath) then bAPIError = true end end @@ -844,18 +844,18 @@ if pocket and fs.isDir( "rom/apis/pocket" ) then end end -if commands and fs.isDir( "rom/apis/command" ) then +if commands and fs.isDir("rom/apis/command") then -- Load command APIs - if os.loadAPI( "rom/apis/command/commands.lua" ) then + if os.loadAPI("rom/apis/command/commands.lua") then -- Add a special case-insensitive metatable to the commands api local tCaseInsensitiveMetatable = { - __index = function( table, key ) - local value = rawget( table, key ) + __index = function(table, key) + local value = rawget(table, key) if value ~= nil then return value end if type(key) == "string" then - local value = rawget( table, string.lower(key) ) + local value = rawget(table, string.lower(key)) if value ~= nil then return value end @@ -863,8 +863,8 @@ if commands and fs.isDir( "rom/apis/command" ) then return nil end, } - setmetatable( commands, tCaseInsensitiveMetatable ) - setmetatable( commands.async, tCaseInsensitiveMetatable ) + setmetatable(commands, tCaseInsensitiveMetatable) + setmetatable(commands.async, tCaseInsensitiveMetatable) -- Add global "exec" function exec = commands.exec @@ -874,29 +874,29 @@ if commands and fs.isDir( "rom/apis/command" ) then end if bAPIError then - print( "Press any key to continue" ) - os.pullEvent( "key" ) + print("Press any key to continue") + os.pullEvent("key") term.clear() - term.setCursorPos( 1, 1 ) + term.setCursorPos(1, 1) end -- Set default settings -settings.set( "shell.allow_startup", true ) -settings.set( "shell.allow_disk_startup", commands == nil ) -settings.set( "shell.autocomplete", true ) -settings.set( "edit.autocomplete", true ) -settings.set( "edit.default_extension", "lua" ) -settings.set( "paint.default_extension", "nfp" ) -settings.set( "lua.autocomplete", true ) -settings.set( "list.show_hidden", false ) -settings.set( "motd.enable", false ) -settings.set( "motd.path", "/rom/motd.txt:/motd.txt" ) +settings.set("shell.allow_startup", true) +settings.set("shell.allow_disk_startup", commands == nil) +settings.set("shell.autocomplete", true) +settings.set("edit.autocomplete", true) +settings.set("edit.default_extension", "lua") +settings.set("paint.default_extension", "nfp") +settings.set("lua.autocomplete", true) +settings.set("list.show_hidden", false) +settings.set("motd.enable", false) +settings.set("motd.path", "/rom/motd.txt:/motd.txt") if term.isColour() then - settings.set( "bios.use_multishell", true ) + settings.set("bios.use_multishell", true) end if _CC_DEFAULT_SETTINGS then - for sPair in string.gmatch( _CC_DEFAULT_SETTINGS, "[^,]+" ) do - local sName, sValue = string.match( sPair, "([^=]*)=(.*)" ) + for sPair in string.gmatch(_CC_DEFAULT_SETTINGS, "[^,]+") do + local sName, sValue = string.match(sPair, "([^=]*)=(.*)") if sName and sValue then local value if sValue == "true" then @@ -911,43 +911,43 @@ if _CC_DEFAULT_SETTINGS then value = sValue end if value ~= nil then - settings.set( sName, value ) + settings.set(sName, value) else - settings.unset( sName ) + settings.unset(sName) end end end end -- Load user settings -if fs.exists( ".settings" ) then - settings.load( ".settings" ) +if fs.exists(".settings") then + settings.load(".settings") end -- Run the shell local ok, err = pcall(parallel.waitForAny, function() local sShell - if term.isColour() and settings.get( "bios.use_multishell" ) then + if term.isColour() and settings.get("bios.use_multishell") then sShell = "rom/programs/advanced/multishell.lua" else sShell = "rom/programs/shell.lua" end - os.run( {}, sShell ) - os.run( {}, "rom/programs/shutdown.lua" ) + os.run({}, sShell) + os.run({}, "rom/programs/shutdown.lua") end, rednet.run ) -- If the shell errored, let the user read it. -term.redirect( term.native() ) +term.redirect(term.native()) if not ok then - printError( err ) - pcall( function() - term.setCursorBlink( false ) - print( "Press any key to continue" ) - os.pullEvent( "key" ) - end ) + printError(err) + pcall(function() + term.setCursorBlink(false) + print("Press any key to continue") + os.pullEvent("key") + end) end -- End diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua b/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua index 8754b1929..b821c38d3 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua @@ -86,7 +86,7 @@ black = 0x8000 -- colors.combine(colors.white, colors.magenta, colours.lightBlue) -- -- => 13 -- ``` -function combine( ... ) +function combine(...) local r = 0 for i = 1, select('#', ...) do local c = select(i, ...) @@ -110,7 +110,7 @@ end -- colours.subtract(colours.lime, colours.orange, colours.white) -- -- => 32 -- ``` -function subtract( colors, ... ) +function subtract(colors, ...) expect(1, colors, "number") local r = colors for i = 1, select('#', ...) do @@ -131,7 +131,7 @@ end -- colors.test(colors.combine(colors.white, colors.magenta, colours.lightBlue), colors.lightBlue) -- -- => true -- ``` -function test( colors, color ) +function test(colors, color) expect(1, colors, "number") expect(2, color, "number") return bit32.band(colors, color) == color @@ -148,14 +148,14 @@ end -- colors.rgb(0.7, 0.2, 0.6) -- -- => 0xb23399 -- ``` -function packRGB( r, g, b ) +function packRGB(r, g, b) expect(1, r, "number") expect(2, g, "number") expect(3, b, "number") return - bit32.band( r * 255, 0xFF ) * 2 ^ 16 + - bit32.band( g * 255, 0xFF ) * 2 ^ 8 + - bit32.band( b * 255, 0xFF ) + bit32.band(r * 255, 0xFF) * 2 ^ 16 + + bit32.band(g * 255, 0xFF) * 2 ^ 8 + + bit32.band(b * 255, 0xFF) end --- Separate a hexadecimal RGB colour into its three constituent channels. @@ -170,12 +170,12 @@ end -- -- => 0.7, 0.2, 0.6 -- ``` -- @see colors.packRGB -function unpackRGB( rgb ) +function unpackRGB(rgb) expect(1, rgb, "number") return - bit32.band( bit32.rshift( rgb, 16 ), 0xFF ) / 255, - bit32.band( bit32.rshift( rgb, 8 ), 0xFF ) / 255, - bit32.band( rgb, 0xFF ) / 255 + bit32.band(bit32.rshift(rgb, 16), 0xFF) / 255, + bit32.band(bit32.rshift(rgb, 8), 0xFF) / 255, + bit32.band(rgb, 0xFF) / 255 end --- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many @@ -202,10 +202,10 @@ end -- colors.rgb(0.7, 0.2, 0.6) -- -- => 0xb23399 -- ``` -function rgb8( r, g, b ) +function rgb8(r, g, b) if g == nil and b == nil then - return unpackRGB( r ) + return unpackRGB(r) else - return packRGB( r, g, b ) + return packRGB(r, g, b) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua b/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua index d0c02b304..7f5c4b824 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/command/commands.lua @@ -14,7 +14,7 @@ -- @module commands if not commands then - error( "Cannot load command API on normal computer", 2 ) + error("Cannot load command API on normal computer", 2) end --- The builtin commands API, without any generated command helper functions @@ -23,16 +23,16 @@ end -- overwritten by a command. native = commands.native or commands -local function collapseArgs( bJSONIsNBT, ... ) +local function collapseArgs(bJSONIsNBT, ...) local args = table.pack(...) for i = 1, #args do local arg = args[i] if type(arg) == "boolean" or type(arg) == "number" or type(arg) == "string" then args[i] = tostring(arg) elseif type(arg) == "table" then - args[i] = textutils.serialiseJSON( arg, bJSONIsNBT ) + args[i] = textutils.serialiseJSON(arg, bJSONIsNBT) else - error( "Expected string, number, boolean or table", 3 ) + error("Expected string, number, boolean or table", 3) end end @@ -41,27 +41,27 @@ end -- Put native functions into the environment local env = _ENV -for k, v in pairs( native ) do +for k, v in pairs(native) do env[k] = v end -- Create wrapper functions for all the commands local tAsync = {} local tNonNBTJSONCommands = { - [ "tellraw" ] = true, - [ "title" ] = true, + ["tellraw"] = true, + ["title"] = true, } local tCommands = native.list() for _, sCommandName in ipairs(tCommands) do - if env[ sCommandName ] == nil then - local bJSONIsNBT = tNonNBTJSONCommands[ sCommandName ] == nil - env[ sCommandName ] = function( ... ) - local sCommand = collapseArgs( bJSONIsNBT, sCommandName, ... ) - return native.exec( sCommand ) + if env[sCommandName] == nil then + local bJSONIsNBT = tNonNBTJSONCommands[sCommandName] == nil + env[sCommandName] = function(...) + local sCommand = collapseArgs(bJSONIsNBT, sCommandName, ...) + return native.exec(sCommand) end - tAsync[ sCommandName ] = function( ... ) - local sCommand = collapseArgs( bJSONIsNBT, sCommandName, ... ) - return native.execAsync( sCommand ) + tAsync[sCommandName] = function(...) + local sCommand = collapseArgs(bJSONIsNBT, sCommandName, ...) + return native.execAsync(sCommand) end end end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua b/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua index 502989008..fcc907121 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/disk.lua @@ -11,11 +11,11 @@ -- -- @module disk -local function isDrive( name ) - if type( name ) ~= "string" then - error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 3 ) +local function isDrive(name) + if type(name) ~= "string" then + error("bad argument #1 (expected string, got " .. type(name) .. ")", 3) end - return peripheral.getType( name ) == "drive" + return peripheral.getType(name) == "drive" end --- Checks whether any item at all is in the disk drive @@ -23,9 +23,9 @@ end -- @tparam string name The name of the disk drive. -- @treturn boolean If something is in the disk drive. -- @usage disk.isPresent(false) -function isPresent( name ) - if isDrive( name ) then - return peripheral.call( name, "isDiskPresent" ) +function isPresent(name) + if isDrive(name) then + return peripheral.call(name, "isDiskPresent") end return false end @@ -40,9 +40,9 @@ end -- @treturn string|nil The name of the current media, or `nil` if the drive is -- not present or empty. -- @see disk.setLabel -function getLabel( name ) - if isDrive( name ) then - return peripheral.call( name, "getDiskLabel" ) +function getLabel(name) + if isDrive(name) then + return peripheral.call(name, "getDiskLabel") end return nil end @@ -51,9 +51,9 @@ end -- -- @tparam string name The name of the disk drive. -- @tparam string|nil label The new label of the disk -function setLabel( name, label ) - if isDrive( name ) then - peripheral.call( name, "setDiskLabel", label ) +function setLabel(name, label) + if isDrive(name) then + peripheral.call(name, "setDiskLabel", label) end end @@ -64,9 +64,9 @@ end -- @tparam string name The name of the disk drive. -- @treturn boolean If the disk is present and provides a mount. -- @see disk.getMountPath -function hasData( name ) - if isDrive( name ) then - return peripheral.call( name, "hasData" ) +function hasData(name) + if isDrive(name) then + return peripheral.call(name, "hasData") end return false end @@ -78,9 +78,9 @@ end -- @treturn string|nil The mount's directory, or `nil` if the drive does not -- contain a floppy or computer. -- @see disk.hasData -function getMountPath( name ) - if isDrive( name ) then - return peripheral.call( name, "getMountPath" ) +function getMountPath(name) + if isDrive(name) then + return peripheral.call(name, "getMountPath") end return nil end @@ -94,9 +94,9 @@ end -- -- @tparam string name The name of the disk drive. -- @treturn boolean If the disk is present and has audio saved on it. -function hasAudio( name ) - if isDrive( name ) then - return peripheral.call( name, "hasAudio" ) +function hasAudio(name) + if isDrive(name) then + return peripheral.call(name, "hasAudio") end return false end @@ -108,9 +108,9 @@ end -- @tparam string name The name of the disk drive. -- @treturn string|false|nil The track title, `false` if there is not a music -- record in the drive or `nil` if no drive is present. -function getAudioTitle( name ) - if isDrive( name ) then - return peripheral.call( name, "getAudioTitle" ) +function getAudioTitle(name) + if isDrive(name) then + return peripheral.call(name, "getAudioTitle") end return nil end @@ -124,9 +124,9 @@ end -- -- @tparam string name The name of the disk drive. -- @usage disk.playAudio("bottom") -function playAudio( name ) - if isDrive( name ) then - peripheral.call( name, "playAudio" ) +function playAudio(name) + if isDrive(name) then + peripheral.call(name, "playAudio") end end @@ -134,14 +134,14 @@ end -- @{disk.playAudio}. -- -- @tparam string name The name o the disk drive. -function stopAudio( name ) +function stopAudio(name) if not name then - for _, sName in ipairs( peripheral.getNames() ) do - stopAudio( sName ) + for _, sName in ipairs(peripheral.getNames()) do + stopAudio(sName) end else - if isDrive( name ) then - peripheral.call( name, "stopAudio" ) + if isDrive(name) then + peripheral.call(name, "stopAudio") end end end @@ -150,9 +150,9 @@ end -- -- @tparam string name The name of the disk drive. -- @usage disk.eject("bottom") -function eject( name ) - if isDrive( name ) then - peripheral.call( name, "ejectDisk" ) +function eject(name) + if isDrive(name) then + peripheral.call(name, "ejectDisk") end end @@ -163,9 +163,9 @@ end -- -- @tparam string name The name of the disk drive. -- @treturn string|nil The disk ID, or `nil` if the drive does not contain a floppy disk. -function getID( name ) - if isDrive( name ) then - return peripheral.call( name, "getDiskID" ) +function getID(name) + if isDrive(name) then + return peripheral.call(name, "getDiskID") end return nil end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua b/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua index 7a4d03af0..3be8906a5 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/gps.lua @@ -27,20 +27,20 @@ local expect = dofile("rom/modules/main/cc/expect.lua").expect --- The channel which GPS requests and responses are broadcast on. CHANNEL_GPS = 65534 -local function trilaterate( A, B, C ) +local function trilaterate(A, B, C) local a2b = B.vPosition - A.vPosition local a2c = C.vPosition - A.vPosition - if math.abs( a2b:normalize():dot( a2c:normalize() ) ) > 0.999 then + if math.abs(a2b:normalize():dot(a2c:normalize())) > 0.999 then return nil end local d = a2b:length() local ex = a2b:normalize( ) - local i = ex:dot( a2c ) + local i = ex:dot(a2c) local ey = (a2c - ex * i):normalize() - local j = ey:dot( a2c ) - local ez = ex:cross( ey ) + local j = ey:dot(a2c) + local ez = ex:cross(ey) local r1 = A.nDistance local r2 = B.nDistance @@ -53,31 +53,31 @@ local function trilaterate( A, B, C ) local zSquared = r1 * r1 - x * x - y * y if zSquared > 0 then - local z = math.sqrt( zSquared ) + local z = math.sqrt(zSquared) local result1 = result + ez * z local result2 = result - ez * z - local rounded1, rounded2 = result1:round( 0.01 ), result2:round( 0.01 ) + local rounded1, rounded2 = result1:round(0.01), result2:round(0.01) if rounded1.x ~= rounded2.x or rounded1.y ~= rounded2.y or rounded1.z ~= rounded2.z then return rounded1, rounded2 else return rounded1 end end - return result:round( 0.01 ) + return result:round(0.01) end -local function narrow( p1, p2, fix ) - local dist1 = math.abs( (p1 - fix.vPosition):length() - fix.nDistance ) - local dist2 = math.abs( (p2 - fix.vPosition):length() - fix.nDistance ) +local function narrow(p1, p2, fix) + local dist1 = math.abs((p1 - fix.vPosition):length() - fix.nDistance) + local dist2 = math.abs((p2 - fix.vPosition):length() - fix.nDistance) if math.abs(dist1 - dist2) < 0.01 then return p1, p2 elseif dist1 < dist2 then - return p1:round( 0.01 ) + return p1:round(0.01) else - return p2:round( 0.01 ) + return p2:round(0.01) end end @@ -90,7 +90,7 @@ end -- @treturn[1] number This computer's `y` position. -- @treturn[1] number This computer's `z` position. -- @treturn[2] nil If the position could not be established. -function locate( _nTimeout, _bDebug ) +function locate(_nTimeout, _bDebug) expect(1, _nTimeout, "number", "nil") expect(2, _bDebug, "boolean", "nil") -- Let command computers use their magic fourth-wall-breaking special abilities @@ -100,8 +100,8 @@ function locate( _nTimeout, _bDebug ) -- Find a modem local sModemSide = nil - for _, sSide in ipairs( rs.getSides() ) do - if peripheral.getType( sSide ) == "modem" and peripheral.call( sSide, "isWireless" ) then + for _, sSide in ipairs(rs.getSides()) do + if peripheral.getType(sSide) == "modem" and peripheral.call(sSide, "isWireless") then sModemSide = sSide break end @@ -109,30 +109,30 @@ function locate( _nTimeout, _bDebug ) if sModemSide == nil then if _bDebug then - print( "No wireless modem attached" ) + print("No wireless modem attached") end return nil end if _bDebug then - print( "Finding position..." ) + print("Finding position...") end -- Open GPS channel to listen for ping responses - local modem = peripheral.wrap( sModemSide ) + local modem = peripheral.wrap(sModemSide) local bCloseChannel = false - if not modem.isOpen( CHANNEL_GPS ) then - modem.open( CHANNEL_GPS ) + if not modem.isOpen(CHANNEL_GPS) then + modem.open(CHANNEL_GPS) bCloseChannel = true end -- Send a ping to listening GPS hosts - modem.transmit( CHANNEL_GPS, CHANNEL_GPS, "PING" ) + modem.transmit(CHANNEL_GPS, CHANNEL_GPS, "PING") -- Wait for the responses local tFixes = {} local pos1, pos2 = nil, nil - local timeout = os.startTimer( _nTimeout or 2 ) + local timeout = os.startTimer(_nTimeout or 2) while true do local e, p1, p2, p3, p4, p5 = os.pullEvent() if e == "modem_message" then @@ -141,19 +141,19 @@ function locate( _nTimeout, _bDebug ) if sSide == sModemSide and sChannel == CHANNEL_GPS and sReplyChannel == CHANNEL_GPS and nDistance then -- Received the correct message from the correct modem: use it to determine position if type(tMessage) == "table" and #tMessage == 3 and tonumber(tMessage[1]) and tonumber(tMessage[2]) and tonumber(tMessage[3]) then - local tFix = { vPosition = vector.new( tMessage[1], tMessage[2], tMessage[3] ), nDistance = nDistance } + local tFix = { vPosition = vector.new(tMessage[1], tMessage[2], tMessage[3]), nDistance = nDistance } if _bDebug then - print( tFix.nDistance .. " metres from " .. tostring( tFix.vPosition ) ) + print(tFix.nDistance .. " metres from " .. tostring(tFix.vPosition)) end if tFix.nDistance == 0 then pos1, pos2 = tFix.vPosition, nil else - table.insert( tFixes, tFix ) + table.insert(tFixes, tFix) if #tFixes >= 3 then if not pos1 then - pos1, pos2 = trilaterate( tFixes[1], tFixes[2], tFixes[#tFixes] ) + pos1, pos2 = trilaterate(tFixes[1], tFixes[2], tFixes[#tFixes]) else - pos1, pos2 = narrow( pos1, pos2, tFixes[#tFixes] ) + pos1, pos2 = narrow(pos1, pos2, tFixes[#tFixes]) end end end @@ -175,24 +175,24 @@ function locate( _nTimeout, _bDebug ) -- Close the channel, if we opened one if bCloseChannel then - modem.close( CHANNEL_GPS ) + modem.close(CHANNEL_GPS) end -- Return the response if pos1 and pos2 then if _bDebug then - print( "Ambiguous position" ) - print( "Could be " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z .. " or " .. pos2.x .. "," .. pos2.y .. "," .. pos2.z ) + print("Ambiguous position") + print("Could be " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z .. " or " .. pos2.x .. "," .. pos2.y .. "," .. pos2.z) end return nil elseif pos1 then if _bDebug then - print( "Position is " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z ) + print("Position is " .. pos1.x .. "," .. pos1.y .. "," .. pos1.z) end return pos1.x, pos1.y, pos1.z else if _bDebug then - print( "Could not determine position" ) + print("Could not determine position") end return nil end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/help.lua b/src/main/resources/assets/computercraft/lua/rom/apis/help.lua index 1b3088c4b..438af474f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/help.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/help.lua @@ -22,7 +22,7 @@ end -- @usage help.setPath( "/disk/help/" ) -- @usage help.setPath( help.path() .. ":/myfolder/help/" ) -- @see help.path -function setPath( _sPath ) +function setPath(_sPath) expect(1, _sPath, "string") sPath = _sPath end @@ -33,14 +33,14 @@ end -- @treturn string|nil The path to the given topic's help file, or `nil` if it -- cannot be found. -- @usage print(help.lookup("disk")) -function lookup( _sTopic ) +function lookup(_sTopic) expect(1, _sTopic, "string") -- Look on the path variable for sPath in string.gmatch(sPath, "[^:]+") do - sPath = fs.combine( sPath, _sTopic ) - if fs.exists( sPath ) and not fs.isDir( sPath ) then + sPath = fs.combine(sPath, _sTopic) + if fs.exists(sPath) and not fs.isDir(sPath) then return sPath - elseif fs.exists( sPath .. ".txt" ) and not fs.isDir( sPath .. ".txt" ) then + elseif fs.exists(sPath .. ".txt") and not fs.isDir(sPath .. ".txt") then return sPath .. ".txt" end end @@ -55,20 +55,20 @@ end function topics() -- Add index local tItems = { - [ "index" ] = true, + ["index"] = true, } -- Add topics from the path for sPath in string.gmatch(sPath, "[^:]+") do - if fs.isDir( sPath ) then - local tList = fs.list( sPath ) - for _, sFile in pairs( tList ) do - if string.sub( sFile, 1, 1 ) ~= "." then - if not fs.isDir( fs.combine( sPath, sFile ) ) then + if fs.isDir(sPath) then + local tList = fs.list(sPath) + for _, sFile in pairs(tList) do + if string.sub(sFile, 1, 1) ~= "." then + if not fs.isDir(fs.combine(sPath, sFile)) then if #sFile > 4 and sFile:sub(-4) == ".txt" then sFile = sFile:sub(1, -5) end - tItems[ sFile ] = true + tItems[sFile] = true end end end @@ -77,10 +77,10 @@ function topics() -- Sort and return local tItemList = {} - for sItem in pairs( tItems ) do - table.insert( tItemList, sItem ) + for sItem in pairs(tItems) do + table.insert(tItemList, sItem) end - table.sort( tItemList ) + table.sort(tItemList) return tItemList end @@ -89,14 +89,14 @@ end -- -- @tparam string prefix The prefix to match -- @treturn table A list of matching topics. -function completeTopic( sText ) +function completeTopic(sText) expect(1, sText, "string") local tTopics = topics() local tResults = {} for n = 1, #tTopics do local sTopic = tTopics[n] - if #sTopic > #sText and string.sub( sTopic, 1, #sText ) == sText then - table.insert( tResults, string.sub( sTopic, #sText + 1 ) ) + if #sTopic > #sText and string.sub(sTopic, 1, #sText) == sText then + table.insert(tResults, string.sub(sTopic, #sText + 1)) end end return tResults diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua b/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua index d303a8087..14bd98a8a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/keys.lua @@ -57,7 +57,7 @@ local tKeys = { } local keys = _ENV -for nKey, sKey in pairs( tKeys ) do +for nKey, sKey in pairs(tKeys) do keys[sKey] = nKey end @@ -71,7 +71,7 @@ keys.cimcumflex = keys.circumflex --- @local -- -- @tparam number code The key code to look up. -- @treturn string|nil The name of the key, or `nil` if not a valid key code. -function getName( code ) +function getName(code) expect(1, code, "number") - return tKeys[ code ] + return tKeys[code] end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua index 4a5630034..1af17ee6c 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua @@ -5,22 +5,22 @@ local expect = dofile("rom/modules/main/cc/expect.lua").expect -local function drawPixelInternal( xPos, yPos ) - term.setCursorPos( xPos, yPos ) +local function drawPixelInternal(xPos, yPos) + term.setCursorPos(xPos, yPos) term.write(" ") end local tColourLookup = {} for n = 1, 16 do - tColourLookup[ string.byte( "0123456789abcdef", n, n ) ] = 2 ^ (n - 1) + tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1) end -local function parseLine( tImageArg, sLine ) +local function parseLine(tImageArg, sLine) local tLine = {} for x = 1, sLine:len() do - tLine[x] = tColourLookup[ string.byte(sLine, x, x) ] or 0 + tLine[x] = tColourLookup[string.byte(sLine, x, x)] or 0 end - table.insert( tImageArg, tLine ) + table.insert(tImageArg, tLine) end --- Parses an image from a multi-line string @@ -28,11 +28,11 @@ end -- @tparam string image The string containing the raw-image data. -- @treturn table The parsed image data, suitable for use with -- @{paintutils.drawImage}. -function parseImage( image ) +function parseImage(image) expect(1, image, "string") local tImage = {} - for sLine in ( image .. "\n" ):gmatch( "(.-)\n" ) do - parseLine( tImage, sLine ) + for sLine in (image .. "\n"):gmatch("(.-)\n") do + parseLine(tImage, sLine) end return tImage end @@ -45,14 +45,14 @@ end -- -- @treturn table|nil The parsed image data, suitable for use with -- @{paintutils.drawImage}, or `nil` if the file does not exist. -function loadImage( path ) +function loadImage(path) expect(1, path, "string") - if fs.exists( path ) then - local file = io.open( path, "r" ) + if fs.exists(path) then + local file = io.open(path, "r") local sContent = file:read("*a") file:close() - return parseImage( sContent ) + return parseImage(sContent) end return nil end @@ -66,18 +66,18 @@ end -- @tparam number yPos The y position to draw at, where 1 is the very top. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be -- the current background colour if not specified. -function drawPixel( xPos, yPos, colour ) +function drawPixel(xPos, yPos, colour) expect(1, xPos, "number") expect(2, yPos, "number") expect(3, colour, "number", "nil") - if type( xPos ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( xPos ) .. ")", 2 ) end - if type( yPos ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( yPos ) .. ")", 2 ) end - if colour ~= nil and type( colour ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( colour ) .. ")", 2 ) end + if type(xPos) ~= "number" then error("bad argument #1 (expected number, got " .. type(xPos) .. ")", 2) end + if type(yPos) ~= "number" then error("bad argument #2 (expected number, got " .. type(yPos) .. ")", 2) end + if colour ~= nil and type(colour) ~= "number" then error("bad argument #3 (expected number, got " .. type(colour) .. ")", 2) end if colour then - term.setBackgroundColor( colour ) + term.setBackgroundColor(colour) end - return drawPixelInternal( xPos, yPos ) + return drawPixelInternal(xPos, yPos) end --- Draws a straight line from the start to end position. @@ -91,7 +91,7 @@ end -- @tparam number endY The end y position of the line. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be -- the current background colour if not specified. -function drawLine( startX, startY, endX, endY, colour ) +function drawLine(startX, startY, endX, endY, colour) expect(1, startX, "number") expect(2, startY, "number") expect(3, endX, "number") @@ -104,14 +104,14 @@ function drawLine( startX, startY, endX, endY, colour ) endY = math.floor(endY) if colour then - term.setBackgroundColor( colour ) + term.setBackgroundColor(colour) end if startX == endX and startY == endY then - drawPixelInternal( startX, startY ) + drawPixelInternal(startX, startY) return end - local minX = math.min( startX, endX ) + local minX = math.min(startX, endX) local maxX, minY, maxY if minX == startX then minY = startY @@ -132,7 +132,7 @@ function drawLine( startX, startY, endX, endY, colour ) local y = minY local dy = yDiff / xDiff for x = minX, maxX do - drawPixelInternal( x, math.floor( y + 0.5 ) ) + drawPixelInternal(x, math.floor(y + 0.5)) y = y + dy end else @@ -140,12 +140,12 @@ function drawLine( startX, startY, endX, endY, colour ) local dx = xDiff / yDiff if maxY >= minY then for y = minY, maxY do - drawPixelInternal( math.floor( x + 0.5 ), y ) + drawPixelInternal(math.floor(x + 0.5), y) x = x + dx end else for y = minY, maxY, -1 do - drawPixelInternal( math.floor( x + 0.5 ), y ) + drawPixelInternal(math.floor(x + 0.5), y) x = x - dx end end @@ -164,7 +164,7 @@ end -- @tparam number endY The end y position of the line. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be -- the current background colour if not specified. -function drawBox( startX, startY, endX, endY, nColour ) +function drawBox(startX, startY, endX, endY, nColour) expect(1, startX, "number") expect(2, startY, "number") expect(3, endX, "number") @@ -177,14 +177,14 @@ function drawBox( startX, startY, endX, endY, nColour ) endY = math.floor(endY) if nColour then - term.setBackgroundColor( nColour ) + term.setBackgroundColor(nColour) end if startX == endX and startY == endY then - drawPixelInternal( startX, startY ) + drawPixelInternal(startX, startY) return end - local minX = math.min( startX, endX ) + local minX = math.min(startX, endX) local maxX, minY, maxY if minX == startX then minY = startY @@ -197,14 +197,14 @@ function drawBox( startX, startY, endX, endY, nColour ) end for x = minX, maxX do - drawPixelInternal( x, minY ) - drawPixelInternal( x, maxY ) + drawPixelInternal(x, minY) + drawPixelInternal(x, maxY) end if maxY - minY >= 2 then for y = minY + 1, maxY - 1 do - drawPixelInternal( minX, y ) - drawPixelInternal( maxX, y ) + drawPixelInternal(minX, y) + drawPixelInternal(maxX, y) end end end @@ -220,7 +220,7 @@ end -- @tparam number endY The end y position of the line. -- @tparam[opt] number colour The @{colors|color} of this pixel. This will be -- the current background colour if not specified. -function drawFilledBox( startX, startY, endX, endY, nColour ) +function drawFilledBox(startX, startY, endX, endY, nColour) expect(1, startX, "number") expect(2, startY, "number") expect(3, endX, "number") @@ -233,14 +233,14 @@ function drawFilledBox( startX, startY, endX, endY, nColour ) endY = math.floor(endY) if nColour then - term.setBackgroundColor( nColour ) + term.setBackgroundColor(nColour) end if startX == endX and startY == endY then - drawPixelInternal( startX, startY ) + drawPixelInternal(startX, startY) return end - local minX = math.min( startX, endX ) + local minX = math.min(startX, endX) local maxX, minY, maxY if minX == startX then minY = startY @@ -254,7 +254,7 @@ function drawFilledBox( startX, startY, endX, endY, nColour ) for x = minX, maxX do for y = minY, maxY do - drawPixelInternal( x, y ) + drawPixelInternal(x, y) end end end @@ -264,7 +264,7 @@ end -- @tparam table image The parsed image data. -- @tparam number xPos The x position to start drawing at. -- @tparam number xPos The y position to start drawing at. -function drawImage( image, xPos, yPos ) +function drawImage(image, xPos, yPos) expect(1, image, "table") expect(2, xPos, "number") expect(3, yPos, "number") @@ -272,8 +272,8 @@ function drawImage( image, xPos, yPos ) local tLine = image[y] for x = 1, #tLine do if tLine[x] > 0 then - term.setBackgroundColor( tLine[x] ) - drawPixelInternal( x + xPos - 1, y + yPos - 1 ) + term.setBackgroundColor(tLine[x]) + drawPixelInternal(x + xPos - 1, y + yPos - 1) end end end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua b/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua index ea4f8d43a..c787eff41 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua @@ -14,13 +14,13 @@ -- -- @module parallel -local function create( ... ) +local function create(...) local tFns = table.pack(...) local tCos = {} for i = 1, tFns.n, 1 do local fn = tFns[i] - if type( fn ) ~= "function" then - error( "bad argument #" .. i .. " (expected function, got " .. type( fn ) .. ")", 3 ) + if type(fn) ~= "function" then + error("bad argument #" .. i .. " (expected function, got " .. type(fn) .. ")", 3) end tCos[i] = coroutine.create(fn) @@ -29,7 +29,7 @@ local function create( ... ) return tCos end -local function runUntilLimit( _routines, _limit ) +local function runUntilLimit(_routines, _limit) local count = #_routines local living = count @@ -40,13 +40,13 @@ local function runUntilLimit( _routines, _limit ) local r = _routines[n] if r then if tFilters[r] == nil or tFilters[r] == eventData[1] or eventData[1] == "terminate" then - local ok, param = coroutine.resume( r, table.unpack( eventData, 1, eventData.n ) ) + local ok, param = coroutine.resume(r, table.unpack(eventData, 1, eventData.n)) if not ok then - error( param, 0 ) + error(param, 0) else tFilters[r] = param end - if coroutine.status( r ) == "dead" then + if coroutine.status(r) == "dead" then _routines[n] = nil living = living - 1 if living <= _limit then @@ -58,7 +58,7 @@ local function runUntilLimit( _routines, _limit ) end for n = 1, count do local r = _routines[n] - if r and coroutine.status( r ) == "dead" then + if r and coroutine.status(r) == "dead" then _routines[n] = nil living = living - 1 if living <= _limit then @@ -66,7 +66,7 @@ local function runUntilLimit( _routines, _limit ) end end end - eventData = table.pack( os.pullEventRaw() ) + eventData = table.pack(os.pullEventRaw()) end end @@ -75,9 +75,9 @@ end -- from the @{parallel.waitForAny} call. -- -- @tparam function ... The functions this task will run -function waitForAny( ... ) - local routines = create( ... ) - return runUntilLimit( routines, #routines - 1 ) +function waitForAny(...) + local routines = create(...) + return runUntilLimit(routines, #routines - 1) end --- Switches between execution of the functions, until all of them are @@ -85,7 +85,7 @@ end -- from the @{parallel.waitForAll} call. -- -- @tparam function ... The functions this task will run -function waitForAll( ... ) - local routines = create( ... ) - return runUntilLimit( routines, 0 ) +function waitForAll(...) + local routines = create(...) + return runUntilLimit(routines, 0) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua b/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua index 9152aa647..ab68db4b6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua @@ -37,13 +37,13 @@ local tHostnames = {} -- -- @tparam string modem The name of the modem to open. -- @throws If there is no such modem with the given name -function open( modem ) +function open(modem) expect(1, modem, "string") - if peripheral.getType( modem ) ~= "modem" then - error( "No such modem: " .. modem, 2 ) + if peripheral.getType(modem) ~= "modem" then + error("No such modem: " .. modem, 2) end - peripheral.call( modem, "open", os.getComputerID() ) - peripheral.call( modem, "open", CHANNEL_BROADCAST ) + peripheral.call(modem, "open", os.getComputerID()) + peripheral.call(modem, "open", CHANNEL_BROADCAST) end --- Close a modem with the given @{peripheral} name, meaning it can no longer @@ -52,20 +52,20 @@ end -- @tparam[opt] string modem The side the modem exists on. If not given, all -- open modems will be closed. -- @throws If there is no such modem with the given name -function close( modem ) +function close(modem) expect(1, modem, "string", "nil") if modem then -- Close a specific modem - if peripheral.getType( modem ) ~= "modem" then - error( "No such modem: " .. modem, 2 ) + if peripheral.getType(modem) ~= "modem" then + error("No such modem: " .. modem, 2) end - peripheral.call( modem, "close", os.getComputerID() ) - peripheral.call( modem, "close", CHANNEL_BROADCAST ) + peripheral.call(modem, "close", os.getComputerID()) + peripheral.call(modem, "close", CHANNEL_BROADCAST) else -- Close all modems - for _, modem in ipairs( peripheral.getNames() ) do - if isOpen( modem ) then - close( modem ) + for _, modem in ipairs(peripheral.getNames()) do + if isOpen(modem) then + close(modem) end end end @@ -76,17 +76,17 @@ end -- @tparam[opt] string modem Which modem to check. If not given, all connected -- modems will be checked. -- @treturn boolean If the given modem is open. -function isOpen( modem ) +function isOpen(modem) expect(1, modem, "string", "nil") if modem then -- Check if a specific modem is open - if peripheral.getType( modem ) == "modem" then - return peripheral.call( modem, "isOpen", os.getComputerID() ) and peripheral.call( modem, "isOpen", CHANNEL_BROADCAST ) + if peripheral.getType(modem) == "modem" then + return peripheral.call(modem, "isOpen", os.getComputerID()) and peripheral.call(modem, "isOpen", CHANNEL_BROADCAST) end else -- Check if any modem is open - for _, modem in ipairs( peripheral.getNames() ) do - if isOpen( modem ) then + for _, modem in ipairs(peripheral.getNames()) do + if isOpen(modem) then return true end end @@ -111,15 +111,15 @@ end -- currently @{rednet.open|open}). Note, this does not guarantee the message was -- actually _received_. -- @see rednet.receive -function send( nRecipient, message, sProtocol ) +function send(nRecipient, message, sProtocol) expect(1, nRecipient, "number") expect(3, sProtocol, "string", "nil") -- Generate a (probably) unique message ID -- We could do other things to guarantee uniqueness, but we really don't need to -- Store it to ensure we don't get our own messages back - local nMessageID = math.random( 1, 2147483647 ) - tReceivedMessages[ nMessageID ] = true - tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = nMessageID + local nMessageID = math.random(1, 2147483647) + tReceivedMessages[nMessageID] = true + tReceivedMessageTimeouts[os.startTimer(30)] = nMessageID -- Create the message local nReplyChannel = os.getComputerID() @@ -133,14 +133,14 @@ function send( nRecipient, message, sProtocol ) local sent = false if nRecipient == os.getComputerID() then -- Loopback to ourselves - os.queueEvent( "rednet_message", nReplyChannel, message, sProtocol ) + os.queueEvent("rednet_message", nReplyChannel, message, sProtocol) sent = true else -- Send on all open modems, to the target and to repeaters - for _, sModem in ipairs( peripheral.getNames() ) do - if isOpen( sModem ) then - peripheral.call( sModem, "transmit", nRecipient, nReplyChannel, tMessage ) - peripheral.call( sModem, "transmit", CHANNEL_REPEAT, nReplyChannel, tMessage ) + for _, sModem in ipairs(peripheral.getNames()) do + if isOpen(sModem) then + peripheral.call(sModem, "transmit", nRecipient, nReplyChannel, tMessage) + peripheral.call(sModem, "transmit", CHANNEL_REPEAT, nReplyChannel, tMessage) sent = true end end @@ -158,9 +158,9 @@ end -- using @{rednet.receive} one can filter to only receive messages sent under a -- particular protocol. -- @see rednet.receive -function broadcast( message, sProtocol ) +function broadcast(message, sProtocol) expect(2, sProtocol, "string", "nil") - send( CHANNEL_BROADCAST, message, sProtocol ) + send(CHANNEL_BROADCAST, message, sProtocol) end --- Wait for a rednet message to be received, or until `nTimeout` seconds have @@ -177,7 +177,7 @@ end -- @treturn[2] nil If the timeout elapsed and no message was received. -- @see rednet.broadcast -- @see rednet.send -function receive( sProtocolFilter, nTimeout ) +function receive(sProtocolFilter, nTimeout) -- The parameters used to be ( nTimeout ), detect this case for backwards compatibility if type(sProtocolFilter) == "number" and nTimeout == nil then sProtocolFilter, nTimeout = nil, sProtocolFilter @@ -189,7 +189,7 @@ function receive( sProtocolFilter, nTimeout ) local timer = nil local sFilter = nil if nTimeout then - timer = os.startTimer( nTimeout ) + timer = os.startTimer(nTimeout) sFilter = nil else sFilter = "rednet_message" @@ -197,7 +197,7 @@ function receive( sProtocolFilter, nTimeout ) -- Wait for events while true do - local sEvent, p1, p2, p3 = os.pullEvent( sFilter ) + local sEvent, p1, p2, p3 = os.pullEvent(sFilter) if sEvent == "rednet_message" then -- Return the first matching rednet_message local nSenderID, message, sProtocol = p1, p2, p3 @@ -231,17 +231,17 @@ end -- @throws If trying to register a hostname which is reserved, or currently in use. -- @see rednet.unhost -- @see rednet.lookup -function host( sProtocol, sHostname ) +function host(sProtocol, sHostname) expect(1, sProtocol, "string") expect(2, sHostname, "string") if sHostname == "localhost" then - error( "Reserved hostname", 2 ) + error("Reserved hostname", 2) end - if tHostnames[ sProtocol ] ~= sHostname then - if lookup( sProtocol, sHostname ) ~= nil then - error( "Hostname in use", 2 ) + if tHostnames[sProtocol] ~= sHostname then + if lookup(sProtocol, sHostname) ~= nil then + error("Hostname in use", 2) end - tHostnames[ sProtocol ] = sHostname + tHostnames[sProtocol] = sHostname end end @@ -249,9 +249,9 @@ end --- respond to @{rednet.lookup} requests. -- -- @tparam string sProtocol The protocol to unregister your self from. -function unhost( sProtocol ) +function unhost(sProtocol) expect(1, sProtocol, "string") - tHostnames[ sProtocol ] = nil + tHostnames[sProtocol] = nil end --- Search the local rednet network for systems @{rednet.host|hosting} the @@ -268,7 +268,7 @@ end -- protocol, or @{nil} if none exist. -- @treturn[2] number|nil The computer ID with the provided hostname and protocol, -- or @{nil} if none exists. -function lookup( sProtocol, sHostname ) +function lookup(sProtocol, sHostname) expect(1, sProtocol, "string") expect(2, sHostname, "string", "nil") @@ -279,30 +279,30 @@ function lookup( sProtocol, sHostname ) end -- Check localhost first - if tHostnames[ sProtocol ] then + if tHostnames[sProtocol] then if sHostname == nil then - table.insert( tResults, os.getComputerID() ) - elseif sHostname == "localhost" or sHostname == tHostnames[ sProtocol ] then + table.insert(tResults, os.getComputerID()) + elseif sHostname == "localhost" or sHostname == tHostnames[sProtocol] then return os.getComputerID() end end if not isOpen() then if tResults then - return table.unpack( tResults ) + return table.unpack(tResults) end return nil end -- Broadcast a lookup packet - broadcast( { + broadcast({ sType = "lookup", sProtocol = sProtocol, sHostname = sHostname, - }, "dns" ) + }, "dns") -- Start a timer - local timer = os.startTimer( 2 ) + local timer = os.startTimer(2) -- Wait for events while true do @@ -313,7 +313,7 @@ function lookup( sProtocol, sHostname ) if sMessageProtocol == "dns" and type(tMessage) == "table" and tMessage.sType == "lookup response" then if tMessage.sProtocol == sProtocol then if sHostname == nil then - table.insert( tResults, nSenderID ) + table.insert(tResults, nSenderID) elseif tMessage.sHostname == sHostname then return nSenderID end @@ -327,7 +327,7 @@ function lookup( sProtocol, sHostname ) end end if tResults then - return table.unpack( tResults ) + return table.unpack(tResults) end return nil end @@ -337,7 +337,7 @@ local bRunning = false --- @local function run() if bRunning then - error( "rednet is already running", 2 ) + error("rednet is already running", 2) end bRunning = true @@ -346,12 +346,12 @@ function run() if sEvent == "modem_message" then -- Got a modem message, process it and add it to the rednet event queue local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4 - if isOpen( sModem ) and ( nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST ) then - if type( tMessage ) == "table" and tMessage.nMessageID then - if not tReceivedMessages[ tMessage.nMessageID ] then - tReceivedMessages[ tMessage.nMessageID ] = true - tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = tMessage.nMessageID - os.queueEvent( "rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol ) + if isOpen(sModem) and (nChannel == os.getComputerID() or nChannel == CHANNEL_BROADCAST) then + if type(tMessage) == "table" and tMessage.nMessageID then + if not tReceivedMessages[tMessage.nMessageID] then + tReceivedMessages[tMessage.nMessageID] = true + tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID + os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol) end end end @@ -360,23 +360,23 @@ function run() -- Got a rednet message (queued from above), respond to dns lookup local nSenderID, tMessage, sProtocol = p1, p2, p3 if sProtocol == "dns" and type(tMessage) == "table" and tMessage.sType == "lookup" then - local sHostname = tHostnames[ tMessage.sProtocol ] + local sHostname = tHostnames[tMessage.sProtocol] if sHostname ~= nil and (tMessage.sHostname == nil or tMessage.sHostname == sHostname) then - rednet.send( nSenderID, { + rednet.send(nSenderID, { sType = "lookup response", sHostname = sHostname, sProtocol = tMessage.sProtocol, - }, "dns" ) + }, "dns") end end elseif sEvent == "timer" then -- Got a timer event, use it to clear the event queue local nTimer = p1 - local nMessage = tReceivedMessageTimeouts[ nTimer ] + local nMessage = tReceivedMessageTimeouts[nTimer] if nMessage then - tReceivedMessageTimeouts[ nTimer ] = nil - tReceivedMessages[ nMessage ] = nil + tReceivedMessageTimeouts[nTimer] = nil + tReceivedMessages[nMessage] = nil end end end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua index 50501bbaf..40044c1a0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua @@ -17,19 +17,19 @@ local tSettings = {} -- serialisable by @{textutils.serialize}. -- @throws If this value cannot be serialised -- @see settings.unset -function set( name, value ) +function set(name, value) expect(1, name, "string") expect(2, value, "number", "string", "boolean", "table") if type(value) == "table" then -- Ensure value is serializeable - value = textutils.unserialize( textutils.serialize(value) ) + value = textutils.unserialize(textutils.serialize(value)) end - tSettings[ name ] = value + tSettings[name] = value end local copy -function copy( value ) +function copy(value) if type(value) == "table" then local result = {} for k, v in pairs(value) do @@ -47,9 +47,9 @@ end -- @param[opt] default The value to use should there be pre-existing value for -- this setting. Defaults to `nil`. -- @return The setting's, or `default` if the setting has not been set. -function get( name, default ) +function get(name, default) expect(1, name, "string") - local result = tSettings[ name ] + local result = tSettings[name] if result ~= nil then return copy(result) else @@ -65,9 +65,9 @@ end -- @tparam string name The name of the setting to unset. -- @see settings.set -- @see settings.clear -function unset( name ) +function unset(name) expect(1, name, "string") - tSettings[ name ] = nil + tSettings[name] = nil end --- Removes the value of all settings. Equivalent to calling @{settings.unset} @@ -84,8 +84,8 @@ end -- settings. function getNames() local result = {} - for k in pairs( tSettings ) do - result[ #result + 1 ] = k + for k in pairs(tSettings) do + result[#result + 1] = k end table.sort(result) return result @@ -102,9 +102,9 @@ end -- corrupted. -- -- @see settings.save -function load( sPath ) +function load(sPath) expect(1, sPath, "string") - local file = fs.open( sPath, "r" ) + local file = fs.open(sPath, "r") if not file then return false end @@ -112,7 +112,7 @@ function load( sPath ) local sText = file.readAll() file.close() - local tFile = textutils.unserialize( sText ) + local tFile = textutils.unserialize(sText) if type(tFile) ~= "table" then return false end @@ -120,7 +120,7 @@ function load( sPath ) for k, v in pairs(tFile) do if type(k) == "string" and (type(v) == "string" or type(v) == "number" or type(v) == "boolean" or type(v) == "table") then - set( k, v ) + set(k, v) end end @@ -136,14 +136,14 @@ end -- @treturn boolean If the settings were successfully saved. -- -- @see settings.load -function save( sPath ) +function save(sPath) expect(1, sPath, "string") - local file = fs.open( sPath, "w" ) + local file = fs.open(sPath, "w") if not file then return false end - file.write( textutils.serialize( tSettings ) ) + file.write(textutils.serialize(tSettings)) file.close() return true diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua index 095fe1e9f..7461a137c 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/term.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/term.lua @@ -8,9 +8,9 @@ local expect = dofile("rom/modules/main/cc/expect.lua").expect local native = term.native and term.native() or term local redirectTarget = native -local function wrap( _sFunction ) - return function( ... ) - return redirectTarget[ _sFunction ]( ... ) +local function wrap(_sFunction) + return function(...) + return redirectTarget[_sFunction](...) end end @@ -34,16 +34,16 @@ local term = _ENV -- @usage -- Redirect to a monitor on the right of the computer. -- term.redirect(peripheral.wrap("right")) -term.redirect = function( target ) +term.redirect = function(target) expect(1, target, "table") if target == term or target == _G.term then - error( "term is not a recommended redirect target, try term.current() instead", 2 ) + error("term is not a recommended redirect target, try term.current() instead", 2) end - for k, v in pairs( native ) do - if type( k ) == "string" and type( v ) == "function" then - if type( target[k] ) ~= "function" then + for k, v in pairs(native) do + if type(k) == "string" and type(v) == "function" then + if type(target[k]) ~= "function" then target[k] = function() - error( "Redirect object is missing method " .. k .. ".", 2 ) + error("Redirect object is missing method " .. k .. ".", 2) end end end @@ -81,8 +81,8 @@ for _, method in ipairs { "nativePaletteColor", "nativePaletteColour" } do native[method] = nil end -for k, v in pairs( native ) do - if type( k ) == "string" and type( v ) == "function" and rawget(term, k) == nil then - term[k] = wrap( k ) +for k, v in pairs(native) do + if type(k) == "string" and type(v) == "function" and rawget(term, k) == nil then + term[k] = wrap(k) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua index 4be4388f7..1ef4fb1ae 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua @@ -15,22 +15,22 @@ local expect = dofile("rom/modules/main/cc/expect.lua").expect -- Defaults to 20. -- @usage textutils.slowWrite("Hello, world!") -- @usage textutils.slowWrite("Hello, world!", 5) -function slowWrite( sText, nRate ) +function slowWrite(sText, nRate) expect(2, nRate, "number", "nil") nRate = nRate or 20 if nRate < 0 then - error( "Rate must be positive", 2 ) + error("Rate must be positive", 2) end local nSleep = 1 / nRate - sText = tostring( sText ) + sText = tostring(sText) local x, y = term.getCursorPos() local len = #sText for n = 1, len do - term.setCursorPos( x, y ) - sleep( nSleep ) - local nLines = write( string.sub( sText, 1, n ) ) + term.setCursorPos(x, y) + sleep(nSleep) + local nLines = write(string.sub(sText, 1, n)) local _, newY = term.getCursorPos() y = newY - nLines end @@ -46,8 +46,8 @@ end -- Defaults to 20. -- @usage textutils.slowPrint("Hello, world!") -- @usage textutils.slowPrint("Hello, world!", 5) -function slowPrint( sText, nRate ) - slowWrite( sText, nRate ) +function slowPrint(sText, nRate) + slowWrite(sText, nRate) print() end @@ -58,7 +58,7 @@ end -- clock (`18:30`) rather than a 12-hour one (`6:30 AM`) -- @treturn string The formatted time -- @usage textutils.formatTime(os.time()) -function formatTime( nTime, bTwentyFourHour ) +function formatTime(nTime, bTwentyFourHour) expect(1, nTime, "number") expect(2, bTwentyFourHour, "boolean", "nil") local sTOD = nil @@ -76,26 +76,26 @@ function formatTime( nTime, bTwentyFourHour ) local nHour = math.floor(nTime) local nMinute = math.floor((nTime - nHour) * 60) if sTOD then - return string.format( "%d:%02d %s", nHour, nMinute, sTOD ) + return string.format("%d:%02d %s", nHour, nMinute, sTOD) else - return string.format( "%d:%02d", nHour, nMinute ) + return string.format("%d:%02d", nHour, nMinute) end end -local function makePagedScroll( _term, _nFreeLines ) +local function makePagedScroll(_term, _nFreeLines) local nativeScroll = _term.scroll local nFreeLines = _nFreeLines or 0 - return function( _n ) + return function(_n) for _ = 1, _n do - nativeScroll( 1 ) + nativeScroll(1) if nFreeLines <= 0 then local _, h = _term.getSize() - _term.setCursorPos( 1, h ) - _term.write( "Press any key to continue" ) - os.pullEvent( "key" ) + _term.setCursorPos(1, h) + _term.write("Press any key to continue") + os.pullEvent("key") _term.clearLine() - _term.setCursorPos( 1, h ) + _term.setCursorPos(1, h) else nFreeLines = nFreeLines - 1 end @@ -120,38 +120,38 @@ end -- @usage -- local width, height = term.getSize() -- textutils.pagedPrint(("This is a rather verbose dose of repetition.\n"):rep(30), height - 2) -function pagedPrint( _sText, _nFreeLines ) +function pagedPrint(_sText, _nFreeLines) expect(2, _nFreeLines, "number", "nil") -- Setup a redirector local oldTerm = term.current() local newTerm = {} - for k, v in pairs( oldTerm ) do + for k, v in pairs(oldTerm) do newTerm[k] = v end - newTerm.scroll = makePagedScroll( oldTerm, _nFreeLines ) - term.redirect( newTerm ) + newTerm.scroll = makePagedScroll(oldTerm, _nFreeLines) + term.redirect(newTerm) -- Print the text local result - local ok, err = pcall( function() + local ok, err = pcall(function() if _sText ~= nil then - result = print( _sText ) + result = print(_sText) else result = print() end - end ) + end) -- Removed the redirector - term.redirect( oldTerm ) + term.redirect(oldTerm) -- Propogate errors if not ok then - error( err, 0 ) + error(err, 0) end return result end -local function tabulateCommon( bPaged, ... ) +local function tabulateCommon(bPaged, ...) local tAll = table.pack(...) for i = 1, tAll.n do expect(i, tAll[i], "number", "table") @@ -159,17 +159,17 @@ local function tabulateCommon( bPaged, ... ) local w, h = term.getSize() local nMaxLen = w / 8 - for n, t in ipairs( tAll ) do + for n, t in ipairs(tAll) do if type(t) == "table" then for nu, sItem in pairs(t) do - if type( sItem ) ~= "string" then - error( "bad argument #" .. n .. "." .. nu .. " (expected string, got " .. type( sItem ) .. ")", 3 ) + if type(sItem) ~= "string" then + error("bad argument #" .. n .. "." .. nu .. " (expected string, got " .. type(sItem) .. ")", 3) end - nMaxLen = math.max( #sItem + 1, nMaxLen ) + nMaxLen = math.max(#sItem + 1, nMaxLen) end end end - local nCols = math.floor( w / nMaxLen ) + local nCols = math.floor(w / nMaxLen) local nLines = 0 local function newLine() if bPaged and nLines >= h - 3 then @@ -180,9 +180,9 @@ local function tabulateCommon( bPaged, ... ) nLines = nLines + 1 end - local function drawCols( _t ) + local function drawCols(_t) local nCol = 1 - for _, s in ipairs( _t ) do + for _, s in ipairs(_t) do if nCol > nCols then nCol = 1 newLine() @@ -190,20 +190,20 @@ local function tabulateCommon( bPaged, ... ) local cx, cy = term.getCursorPos() cx = 1 + (nCol - 1) * nMaxLen - term.setCursorPos( cx, cy ) - term.write( s ) + term.setCursorPos(cx, cy) + term.write(s) nCol = nCol + 1 end print() end - for _, t in ipairs( tAll ) do + for _, t in ipairs(tAll) do if type(t) == "table" then if #t > 0 then - drawCols( t ) + drawCols(t) end elseif type(t) == "number" then - term.setTextColor( t ) + term.setTextColor(t) end end end @@ -218,8 +218,8 @@ end -- -- @tparam {string...}|number ... The rows and text colors to display. -- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) -function tabulate( ... ) - return tabulateCommon( false, ... ) +function tabulate(...) + return tabulateCommon(false, ...) end --- Prints tables in a structured form, stopping and prompting for input should @@ -232,39 +232,39 @@ end -- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) -- @see textutils.tabulate -- @see textutils.pagedPrint -function pagedTabulate( ... ) - return tabulateCommon( true, ... ) +function pagedTabulate(...) + return tabulateCommon(true, ...) end local g_tLuaKeywords = { - [ "and" ] = true, - [ "break" ] = true, - [ "do" ] = true, - [ "else" ] = true, - [ "elseif" ] = true, - [ "end" ] = true, - [ "false" ] = true, - [ "for" ] = true, - [ "function" ] = true, - [ "if" ] = true, - [ "in" ] = true, - [ "local" ] = true, - [ "nil" ] = true, - [ "not" ] = true, - [ "or" ] = true, - [ "repeat" ] = true, - [ "return" ] = true, - [ "then" ] = true, - [ "true" ] = true, - [ "until" ] = true, - [ "while" ] = true, + ["and"] = true, + ["break"] = true, + ["do"] = true, + ["else"] = true, + ["elseif"] = true, + ["end"] = true, + ["false"] = true, + ["for"] = true, + ["function"] = true, + ["if"] = true, + ["in"] = true, + ["local"] = true, + ["nil"] = true, + ["not"] = true, + ["or"] = true, + ["repeat"] = true, + ["return"] = true, + ["then"] = true, + ["true"] = true, + ["until"] = true, + ["while"] = true, } -local function serializeImpl( t, tTracking, sIndent ) +local function serializeImpl(t, tTracking, sIndent) local sType = type(t) if sType == "table" then if tTracking[t] ~= nil then - error( "Cannot serialize table with recursive entries", 0 ) + error("Cannot serialize table with recursive entries", 0) end tTracking[t] = true @@ -278,15 +278,15 @@ local function serializeImpl( t, tTracking, sIndent ) local tSeen = {} for k, v in ipairs(t) do tSeen[k] = true - sResult = sResult .. sSubIndent .. serializeImpl( v, tTracking, sSubIndent ) .. ",\n" + sResult = sResult .. sSubIndent .. serializeImpl(v, tTracking, sSubIndent) .. ",\n" end for k, v in pairs(t) do if not tSeen[k] then local sEntry - if type(k) == "string" and not g_tLuaKeywords[k] and string.match( k, "^[%a_][%a%d_]*$" ) then - sEntry = k .. " = " .. serializeImpl( v, tTracking, sSubIndent ) .. ",\n" + if type(k) == "string" and not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then + sEntry = k .. " = " .. serializeImpl(v, tTracking, sSubIndent) .. ",\n" else - sEntry = "[ " .. serializeImpl( k, tTracking, sSubIndent ) .. " ] = " .. serializeImpl( v, tTracking, sSubIndent ) .. ",\n" + sEntry = "[ " .. serializeImpl(k, tTracking, sSubIndent) .. " ] = " .. serializeImpl(v, tTracking, sSubIndent) .. ",\n" end sResult = sResult .. sSubIndent .. sEntry end @@ -296,13 +296,13 @@ local function serializeImpl( t, tTracking, sIndent ) end elseif sType == "string" then - return string.format( "%q", t ) + return string.format("%q", t) elseif sType == "number" or sType == "boolean" or sType == "nil" then return tostring(t) else - error( "Cannot serialize type " .. sType, 0 ) + error("Cannot serialize type " .. sType, 0) end end @@ -320,14 +320,14 @@ empty_json_array = setmetatable({}, { end, }) -local function serializeJSONImpl( t, tTracking, bNBTStyle ) +local function serializeJSONImpl(t, tTracking, bNBTStyle) local sType = type(t) if t == empty_json_array then return "[]" elseif sType == "table" then if tTracking[t] ~= nil then - error( "Cannot serialize table with recursive entries", 0 ) + error("Cannot serialize table with recursive entries", 0) end tTracking[t] = true @@ -344,9 +344,9 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle ) if type(k) == "string" then local sEntry if bNBTStyle then - sEntry = tostring(k) .. ":" .. serializeJSONImpl( v, tTracking, bNBTStyle ) + sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle) else - sEntry = string.format( "%q", k ) .. ":" .. serializeJSONImpl( v, tTracking, bNBTStyle ) + sEntry = string.format("%q", k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle) end if nObjectSize == 0 then sObjectResult = sObjectResult .. sEntry @@ -357,7 +357,7 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle ) end end for _, v in ipairs(t) do - local sEntry = serializeJSONImpl( v, tTracking, bNBTStyle ) + local sEntry = serializeJSONImpl(v, tTracking, bNBTStyle) if nArraySize == 0 then sArrayResult = sArrayResult .. sEntry else @@ -375,13 +375,13 @@ local function serializeJSONImpl( t, tTracking, bNBTStyle ) end elseif sType == "string" then - return string.format( "%q", t ) + return string.format("%q", t) elseif sType == "number" or sType == "boolean" then return tostring(t) else - error( "Cannot serialize type " .. sType, 0 ) + error("Cannot serialize type " .. sType, 0) end end @@ -394,9 +394,9 @@ end -- @throws If the object contains a value which cannot be -- serialised. This includes functions and tables which appear multiple -- times. -function serialize( t ) +function serialize(t) local tTracking = {} - return serializeImpl( t, tTracking, "" ) + return serializeImpl(t, tTracking, "") end serialise = serialize -- GB version @@ -408,11 +408,11 @@ serialise = serialize -- GB version -- @tparam string s The serialised string to deserialise. -- @return[1] The deserialised object -- @treturn[2] nil If the object could not be deserialised. -function unserialize( s ) +function unserialize(s) expect(1, s, "string") - local func = load( "return " .. s, "unserialize", "t", {} ) + local func = load("return " .. s, "unserialize", "t", {}) if func then - local ok, result = pcall( func ) + local ok, result = pcall(func) if ok then return result end @@ -440,11 +440,11 @@ unserialise = unserialize -- GB version -- serialised. This includes functions and tables which appear multiple -- times. -- @usage textutils.serializeJSON({ values = { 1, "2", true } }) -function serializeJSON( t, bNBTStyle ) +function serializeJSON(t, bNBTStyle) expect(1, t, "table", "string", "number", "boolean") expect(2, bNBTStyle, "boolean", "nil") local tTracking = {} - return serializeJSONImpl( t, tTracking, bNBTStyle or false ) + return serializeJSONImpl(t, tTracking, bNBTStyle or false) end serialiseJSON = serializeJSON -- GB version @@ -454,7 +454,7 @@ serialiseJSON = serializeJSON -- GB version -- @tparam string str The string to encode -- @treturn string The encoded string. -- @usage print("https://example.com/?view=" .. textutils.urlEncode(read())) -function urlEncode( str ) +function urlEncode(str) expect(1, str, "string") if str then str = string.gsub(str, "\n", "\r\n") @@ -466,8 +466,8 @@ function urlEncode( str ) else -- Non-ASCII (encode as UTF-8) return - string.format("%%%02X", 192 + bit32.band( bit32.arshift(n, 6), 31 )) .. - string.format("%%%02X", 128 + bit32.band( n, 63 )) + string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) .. + string.format("%%%02X", 128 + bit32.band(n, 63)) end end) str = string.gsub(str, " ", "+") @@ -493,30 +493,30 @@ local tEmpty = {} -- @see shell.setCompletionFunction -- @see read -- @usage textutils.complete( "pa", getfenv() ) -function complete( sSearchText, tSearchTable ) +function complete(sSearchText, tSearchTable) expect(1, sSearchText, "string") expect(2, tSearchTable, "table", "nil") if g_tLuaKeywords[sSearchText] then return tEmpty end local nStart = 1 - local nDot = string.find( sSearchText, ".", nStart, true ) + local nDot = string.find(sSearchText, ".", nStart, true) local tTable = tSearchTable or _ENV while nDot do - local sPart = string.sub( sSearchText, nStart, nDot - 1 ) - local value = tTable[ sPart ] - if type( value ) == "table" then + local sPart = string.sub(sSearchText, nStart, nDot - 1) + local value = tTable[sPart] + if type(value) == "table" then tTable = value nStart = nDot + 1 - nDot = string.find( sSearchText, ".", nStart, true ) + nDot = string.find(sSearchText, ".", nStart, true) else return tEmpty end end - local nColon = string.find( sSearchText, ":", nStart, true ) + local nColon = string.find(sSearchText, ":", nStart, true) if nColon then - local sPart = string.sub( sSearchText, nStart, nColon - 1 ) - local value = tTable[ sPart ] - if type( value ) == "table" then + local sPart = string.sub(sSearchText, nStart, nColon - 1) + local value = tTable[sPart] + if type(value) == "table" then tTable = value nStart = nColon + 1 else @@ -524,24 +524,24 @@ function complete( sSearchText, tSearchTable ) end end - local sPart = string.sub( sSearchText, nStart ) + local sPart = string.sub(sSearchText, nStart) local nPartLength = #sPart local tResults = {} local tSeen = {} while tTable do - for k, v in pairs( tTable ) do + for k, v in pairs(tTable) do if not tSeen[k] and type(k) == "string" then - if string.find( k, sPart, 1, true ) == 1 then - if not g_tLuaKeywords[k] and string.match( k, "^[%a_][%a%d_]*$" ) then - local sResult = string.sub( k, nPartLength + 1 ) + if string.find(k, sPart, 1, true) == 1 then + if not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then + local sResult = string.sub(k, nPartLength + 1) if nColon then if type(v) == "function" then - table.insert( tResults, sResult .. "(" ) + table.insert(tResults, sResult .. "(") elseif type(v) == "table" then - local tMetatable = getmetatable( v ) - if tMetatable and ( type( tMetatable.__call ) == "function" or type( tMetatable.__call ) == "table" ) then - table.insert( tResults, sResult .. "(" ) + local tMetatable = getmetatable(v) + if tMetatable and (type(tMetatable.__call) == "function" or type(tMetatable.__call) == "table") then + table.insert(tResults, sResult .. "(") end end else @@ -550,21 +550,21 @@ function complete( sSearchText, tSearchTable ) elseif type(v) == "table" and next(v) ~= nil then sResult = sResult .. "." end - table.insert( tResults, sResult ) + table.insert(tResults, sResult) end end end end tSeen[k] = true end - local tMetatable = getmetatable( tTable ) - if tMetatable and type( tMetatable.__index ) == "table" then + local tMetatable = getmetatable(tTable) + if tMetatable and type(tMetatable.__index) == "table" then tTable = tMetatable.__index else tTable = nil end end - table.sort( tResults ) + table.sort(tResults) return tResults end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua index 9b5fab369..f52645524 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua @@ -3,18 +3,18 @@ -- @module turtle if not turtle then - error( "Cannot load turtle API on computer", 2 ) + error("Cannot load turtle API on computer", 2) end native = turtle.native or turtle -local function addCraftMethod( object ) - if peripheral.getType( "left" ) == "workbench" then - object.craft = function( ... ) - return peripheral.call( "left", "craft", ... ) +local function addCraftMethod(object) + if peripheral.getType("left") == "workbench" then + object.craft = function(...) + return peripheral.call("left", "craft", ...) end - elseif peripheral.getType( "right" ) == "workbench" then - object.craft = function( ... ) - return peripheral.call( "right", "craft", ... ) + elseif peripheral.getType("right") == "workbench" then + object.craft = function(...) + return peripheral.call("right", "craft", ...) end else object.craft = nil @@ -23,15 +23,15 @@ end -- Put commands into environment table local env = _ENV -for k, v in pairs( native ) do +for k, v in pairs(native) do if k == "equipLeft" or k == "equipRight" then - env[k] = function( ... ) - local result, err = v( ... ) - addCraftMethod( turtle ) + env[k] = function(...) + local result, err = v(...) + addCraftMethod(turtle) return result, err end else env[k] = v end end -addCraftMethod( env ) +addCraftMethod(env) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua b/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua index e19937bba..05dab327e 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/vector.lua @@ -113,7 +113,7 @@ local vector = { -- @tparam Vector self This vector. -- @treturn number The length of this vector. length = function(self) - return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) + return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) end, --- Divide this vector by its length, producing with the same direction, but @@ -123,7 +123,7 @@ local vector = { -- @treturn Vector The normalised vector -- @usage v:normalize() normalize = function(self) - return self:mul( 1 / self:length() ) + return self:mul(1 / self:length()) end, --- Construct a vector with each dimension rounded to the nearest value. diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua index 5617c2175..aa6e5aab6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua @@ -30,22 +30,22 @@ local expect = dofile("rom/modules/main/cc/expect.lua").expect local tHex = { - [ colors.white ] = "0", - [ colors.orange ] = "1", - [ colors.magenta ] = "2", - [ colors.lightBlue ] = "3", - [ colors.yellow ] = "4", - [ colors.lime ] = "5", - [ colors.pink ] = "6", - [ colors.gray ] = "7", - [ colors.lightGray ] = "8", - [ colors.cyan ] = "9", - [ colors.purple ] = "a", - [ colors.blue ] = "b", - [ colors.brown ] = "c", - [ colors.green ] = "d", - [ colors.red ] = "e", - [ colors.black ] = "f", + [colors.white] = "0", + [colors.orange] = "1", + [colors.magenta] = "2", + [colors.lightBlue] = "3", + [colors.yellow] = "4", + [colors.lime] = "5", + [colors.pink] = "6", + [colors.gray] = "7", + [colors.lightGray] = "8", + [colors.cyan] = "9", + [colors.purple] = "a", + [colors.blue] = "b", + [colors.brown] = "c", + [colors.green] = "d", + [colors.red] = "e", + [colors.black] = "f", } local type = type @@ -70,7 +70,7 @@ local string_sub = string.sub -- @tparam[opt] boolean bStartVisible Whether this window is visible by -- default. Defaults to `true`. -- @treturn Window The constructed window -function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) +function create(parent, nX, nY, nWidth, nHeight, bStartVisible) expect(1, parent, "table") expect(2, nX, "number") expect(3, nY, "number") @@ -79,21 +79,21 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) expect(6, bStartVisible, "boolean", "nil") if parent == term then - error( "term is not a recommended window parent, try term.current() instead", 2 ) + error("term is not a recommended window parent, try term.current() instead", 2) end local sEmptySpaceLine local tEmptyColorLines = {} - local function createEmptyLines( nWidth ) - sEmptySpaceLine = string_rep( " ", nWidth ) + local function createEmptyLines(nWidth) + sEmptySpaceLine = string_rep(" ", nWidth) for n = 0, 15 do local nColor = 2 ^ n local sHex = tHex[nColor] - tEmptyColorLines[nColor] = string_rep( sHex, nWidth ) + tEmptyColorLines[nColor] = string_rep(sHex, nWidth) end end - createEmptyLines( nWidth ) + createEmptyLines(nWidth) -- Setup local bVisible = bStartVisible ~= false @@ -106,8 +106,8 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) local tPalette = {} do local sEmptyText = sEmptySpaceLine - local sEmptyTextColor = tEmptyColorLines[ nTextColor ] - local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ] + local sEmptyTextColor = tEmptyColorLines[nTextColor] + local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for y = 1, nHeight do tLines[y] = { text = sEmptyText, @@ -118,7 +118,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) for i = 0, 15 do local c = 2 ^ i - tPalette[c] = { parent.getPaletteColour( c ) } + tPalette[c] = { parent.getPaletteColour(c) } end end @@ -126,45 +126,45 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) local function updateCursorPos() if nCursorX >= 1 and nCursorY >= 1 and nCursorX <= nWidth and nCursorY <= nHeight then - parent.setCursorPos( nX + nCursorX - 1, nY + nCursorY - 1 ) + parent.setCursorPos(nX + nCursorX - 1, nY + nCursorY - 1) else - parent.setCursorPos( 0, 0 ) + parent.setCursorPos(0, 0) end end local function updateCursorBlink() - parent.setCursorBlink( bCursorBlink ) + parent.setCursorBlink(bCursorBlink) end local function updateCursorColor() - parent.setTextColor( nTextColor ) + parent.setTextColor(nTextColor) end - local function redrawLine( n ) - local tLine = tLines[ n ] - parent.setCursorPos( nX, nY + n - 1 ) - parent.blit( tLine.text, tLine.textColor, tLine.backgroundColor ) + local function redrawLine(n) + local tLine = tLines[n] + parent.setCursorPos(nX, nY + n - 1) + parent.blit(tLine.text, tLine.textColor, tLine.backgroundColor) end local function redraw() for n = 1, nHeight do - redrawLine( n ) + redrawLine(n) end end local function updatePalette() - for k, v in pairs( tPalette ) do - parent.setPaletteColour( k, v[1], v[2], v[3] ) + for k, v in pairs(tPalette) do + parent.setPaletteColour(k, v[1], v[2], v[3]) end end - local function internalBlit( sText, sTextColor, sBackgroundColor ) + local function internalBlit(sText, sTextColor, sBackgroundColor) local nStart = nCursorX local nEnd = nStart + #sText - 1 if nCursorY >= 1 and nCursorY <= nHeight then if nStart <= nWidth and nEnd >= 1 then -- Modify line - local tLine = tLines[ nCursorY ] + local tLine = tLines[nCursorY] if nStart == 1 and nEnd == nWidth then tLine.text = sText tLine.textColor = sTextColor @@ -174,14 +174,14 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) if nStart < 1 then local nClipStart = 1 - nStart + 1 local nClipEnd = nWidth - nStart + 1 - sClippedText = string_sub( sText, nClipStart, nClipEnd ) - sClippedTextColor = string_sub( sTextColor, nClipStart, nClipEnd ) - sClippedBackgroundColor = string_sub( sBackgroundColor, nClipStart, nClipEnd ) + sClippedText = string_sub(sText, nClipStart, nClipEnd) + sClippedTextColor = string_sub(sTextColor, nClipStart, nClipEnd) + sClippedBackgroundColor = string_sub(sBackgroundColor, nClipStart, nClipEnd) elseif nEnd > nWidth then local nClipEnd = nWidth - nStart + 1 - sClippedText = string_sub( sText, 1, nClipEnd ) - sClippedTextColor = string_sub( sTextColor, 1, nClipEnd ) - sClippedBackgroundColor = string_sub( sBackgroundColor, 1, nClipEnd ) + sClippedText = string_sub(sText, 1, nClipEnd) + sClippedTextColor = string_sub(sTextColor, 1, nClipEnd) + sClippedBackgroundColor = string_sub(sBackgroundColor, 1, nClipEnd) else sClippedText = sText sClippedTextColor = sTextColor @@ -194,9 +194,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) local sNewText, sNewTextColor, sNewBackgroundColor if nStart > 1 then local nOldEnd = nStart - 1 - sNewText = string_sub( sOldText, 1, nOldEnd ) .. sClippedText - sNewTextColor = string_sub( sOldTextColor, 1, nOldEnd ) .. sClippedTextColor - sNewBackgroundColor = string_sub( sOldBackgroundColor, 1, nOldEnd ) .. sClippedBackgroundColor + sNewText = string_sub(sOldText, 1, nOldEnd) .. sClippedText + sNewTextColor = string_sub(sOldTextColor, 1, nOldEnd) .. sClippedTextColor + sNewBackgroundColor = string_sub(sOldBackgroundColor, 1, nOldEnd) .. sClippedBackgroundColor else sNewText = sClippedText sNewTextColor = sClippedTextColor @@ -204,9 +204,9 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) end if nEnd < nWidth then local nOldStart = nEnd + 1 - sNewText = sNewText .. string_sub( sOldText, nOldStart, nWidth ) - sNewTextColor = sNewTextColor .. string_sub( sOldTextColor, nOldStart, nWidth ) - sNewBackgroundColor = sNewBackgroundColor .. string_sub( sOldBackgroundColor, nOldStart, nWidth ) + sNewText = sNewText .. string_sub(sOldText, nOldStart, nWidth) + sNewTextColor = sNewTextColor .. string_sub(sOldTextColor, nOldStart, nWidth) + sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth) end tLine.text = sNewText @@ -216,7 +216,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) -- Redraw line if bVisible then - redrawLine( nCursorY ) + redrawLine(nCursorY) end end end @@ -234,25 +234,25 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) -- @type Window local window = {} - function window.write( sText ) - sText = tostring( sText ) - internalBlit( sText, string_rep( tHex[ nTextColor ], #sText ), string_rep( tHex[ nBackgroundColor ], #sText ) ) + function window.write(sText) + sText = tostring(sText) + internalBlit(sText, string_rep(tHex[nTextColor], #sText), string_rep(tHex[nBackgroundColor], #sText)) end - function window.blit( sText, sTextColor, sBackgroundColor ) + function window.blit(sText, sTextColor, sBackgroundColor) if type(sText) ~= "string" then expect(1, sText, "string") end if type(sTextColor) ~= "string" then expect(2, sTextColor, "string") end if type(sBackgroundColor) ~= "string" then expect(3, sBackgroundColor, "string") end if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then - error( "Arguments must be the same length", 2 ) + error("Arguments must be the same length", 2) end - internalBlit( sText, sTextColor, sBackgroundColor ) + internalBlit(sText, sTextColor, sBackgroundColor) end function window.clear() local sEmptyText = sEmptySpaceLine - local sEmptyTextColor = tEmptyColorLines[ nTextColor ] - local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ] + local sEmptyTextColor = tEmptyColorLines[nTextColor] + local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for y = 1, nHeight do tLines[y] = { text = sEmptyText, @@ -270,15 +270,15 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) function window.clearLine() if nCursorY >= 1 and nCursorY <= nHeight then local sEmptyText = sEmptySpaceLine - local sEmptyTextColor = tEmptyColorLines[ nTextColor ] - local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ] - tLines[ nCursorY ] = { + local sEmptyTextColor = tEmptyColorLines[nTextColor] + local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] + tLines[nCursorY] = { text = sEmptyText, textColor = sEmptyTextColor, backgroundColor = sEmptyBackgroundColor, } if bVisible then - redrawLine( nCursorY ) + redrawLine(nCursorY) updateCursorColor() updateCursorPos() end @@ -289,17 +289,17 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) return nCursorX, nCursorY end - function window.setCursorPos( x, y ) + function window.setCursorPos(x, y) if type(x) ~= "number" then expect(1, x, "number") end if type(y) ~= "number" then expect(2, y, "number") end - nCursorX = math.floor( x ) - nCursorY = math.floor( y ) + nCursorX = math.floor(x) + nCursorY = math.floor(y) if bVisible then updateCursorPos() end end - function window.setCursorBlink( blink ) + function window.setCursorBlink(blink) if type(blink) ~= "boolean" then expect(1, blink, "boolean") end bCursorBlink = blink if bVisible then @@ -323,10 +323,10 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) return isColor() end - local function setTextColor( color ) + local function setTextColor(color) if type(color) ~= "number" then expect(1, color, "number") end if tHex[color] == nil then - error( "Invalid color (got " .. color .. ")" , 2 ) + error("Invalid color (got " .. color .. ")" , 2) end nTextColor = color @@ -338,50 +338,50 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) window.setTextColor = setTextColor window.setTextColour = setTextColor - function window.setPaletteColour( colour, r, g, b ) + function window.setPaletteColour(colour, r, g, b) if type(colour) ~= "number" then expect(1, colour, "number") end if tHex[colour] == nil then - error( "Invalid color (got " .. colour .. ")" , 2 ) + error("Invalid color (got " .. colour .. ")" , 2) end local tCol if type(r) == "number" and g == nil and b == nil then - tCol = { colours.unpackRGB( r ) } - tPalette[ colour ] = tCol + tCol = { colours.unpackRGB(r) } + tPalette[colour] = tCol else if type(r) ~= "number" then expect(2, r, "number") end if type(g) ~= "number" then expect(3, g, "number") end if type(b) ~= "number" then expect(4, b, "number") end - tCol = tPalette[ colour ] + tCol = tPalette[colour] tCol[1] = r tCol[2] = g tCol[3] = b end if bVisible then - return parent.setPaletteColour( colour, tCol[1], tCol[2], tCol[3] ) + return parent.setPaletteColour(colour, tCol[1], tCol[2], tCol[3]) end end window.setPaletteColor = window.setPaletteColour - function window.getPaletteColour( colour ) + function window.getPaletteColour(colour) if type(colour) ~= "number" then expect(1, colour, "number") end if tHex[colour] == nil then - error( "Invalid color (got " .. colour .. ")" , 2 ) + error("Invalid color (got " .. colour .. ")" , 2) end - local tCol = tPalette[ colour ] + local tCol = tPalette[colour] return tCol[1], tCol[2], tCol[3] end window.getPaletteColor = window.getPaletteColour - local function setBackgroundColor( color ) + local function setBackgroundColor(color) if type(color) ~= "number" then expect(1, color, "number") end if tHex[color] == nil then - error( "Invalid color (got " .. color .. ")", 2 ) + error("Invalid color (got " .. color .. ")", 2) end nBackgroundColor = color end @@ -393,13 +393,13 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) return nWidth, nHeight end - function window.scroll( n ) + function window.scroll(n) if type(n) ~= "number" then expect(1, n, "number") end if n ~= 0 then local tNewLines = {} local sEmptyText = sEmptySpaceLine - local sEmptyTextColor = tEmptyColorLines[ nTextColor ] - local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ] + local sEmptyTextColor = tEmptyColorLines[nTextColor] + local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for newY = 1, nHeight do local y = newY + n if y >= 1 and y <= nHeight then @@ -448,7 +448,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) end -- Other functions - function window.setVisible( bVis ) + function window.setVisible(bVis) if type(bVis) ~= "boolean" then expect(1, bVis, "boolean") end if bVisible ~= bVis then bVisible = bVis @@ -480,7 +480,7 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) return nX, nY end - function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight, newParent ) + function window.reposition(nNewX, nNewY, nNewWidth, nNewHeight, newParent) if type(nNewX) ~= "number" then expect(1, nNewX, "number") end if type(nNewY) ~= "number" then expect(2, nNewY, "number") end if nNewWidth ~= nil or nNewHeight ~= nil then @@ -496,10 +496,10 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) if nNewWidth and nNewHeight then local tNewLines = {} - createEmptyLines( nNewWidth ) + createEmptyLines(nNewWidth) local sEmptyText = sEmptySpaceLine - local sEmptyTextColor = tEmptyColorLines[ nTextColor ] - local sEmptyBackgroundColor = tEmptyColorLines[ nBackgroundColor ] + local sEmptyTextColor = tEmptyColorLines[nTextColor] + local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] for y = 1, nNewHeight do if y > nHeight then tNewLines[y] = { @@ -513,15 +513,15 @@ function create( parent, nX, nY, nWidth, nHeight, bStartVisible ) tNewLines[y] = tOldLine elseif nNewWidth < nWidth then tNewLines[y] = { - text = string_sub( tOldLine.text, 1, nNewWidth ), - textColor = string_sub( tOldLine.textColor, 1, nNewWidth ), - backgroundColor = string_sub( tOldLine.backgroundColor, 1, nNewWidth ), + text = string_sub(tOldLine.text, 1, nNewWidth), + textColor = string_sub(tOldLine.textColor, 1, nNewWidth), + backgroundColor = string_sub(tOldLine.backgroundColor, 1, nNewWidth), } else tNewLines[y] = { - text = tOldLine.text .. string_sub( sEmptyText, nWidth + 1, nNewWidth ), - textColor = tOldLine.textColor .. string_sub( sEmptyTextColor, nWidth + 1, nNewWidth ), - backgroundColor = tOldLine.backgroundColor .. string_sub( sEmptyBackgroundColor, nWidth + 1, nNewWidth ), + text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, nNewWidth), + textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, nNewWidth), + backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, nNewWidth), } end end diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua index 1791a29a0..2f4c648b6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua @@ -37,9 +37,9 @@ local function expect(index, value, ...) end if name then - error( ("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3 ) + error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3) else - error( ("bad argument #%d (expected %s, got %s)"):format(index, type_names, t), 3 ) + error(("bad argument #%d (expected %s, got %s)"):format(index, type_names, t), 3) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua index 5a34ad389..95b0c76fa 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -320,11 +320,11 @@ end Doc.__tostring = render --- @local local keywords = { - [ "and" ] = true, [ "break" ] = true, [ "do" ] = true, [ "else" ] = true, - [ "elseif" ] = true, [ "end" ] = true, [ "false" ] = true, [ "for" ] = true, - [ "function" ] = true, [ "if" ] = true, [ "in" ] = true, [ "local" ] = true, - [ "nil" ] = true, [ "not" ] = true, [ "or" ] = true, [ "repeat" ] = true, [ "return" ] = true, - [ "then" ] = true, [ "true" ] = true, [ "until" ] = true, [ "while" ] = true, + ["and"] = true, ["break"] = true, ["do"] = true, ["else"] = true, + ["elseif"] = true, ["end"] = true, ["false"] = true, ["for"] = true, + ["function"] = true, ["if"] = true, ["in"] = true, ["local"] = true, + ["nil"] = true, ["not"] = true, ["or"] = true, ["repeat"] = true, ["return"] = true, + ["then"] = true, ["true"] = true, ["until"] = true, ["while"] = true, } local comma = text(",") diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/advanced/bg.lua b/src/main/resources/assets/computercraft/lua/rom/programs/advanced/bg.lua index 806a128cd..907e9f7aa 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/advanced/bg.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/advanced/bg.lua @@ -1,12 +1,12 @@ if not shell.openTab then - printError( "Requires multishell" ) + printError("Requires multishell") return end local tArgs = { ... } if #tArgs > 0 then - shell.openTab( table.unpack( tArgs ) ) + shell.openTab(table.unpack(tArgs)) else - shell.openTab( "shell" ) + shell.openTab("shell") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/advanced/fg.lua b/src/main/resources/assets/computercraft/lua/rom/programs/advanced/fg.lua index 754fdbf8e..efc7832eb 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/advanced/fg.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/advanced/fg.lua @@ -1,18 +1,18 @@ if not shell.openTab then - printError( "Requires multishell" ) + printError("Requires multishell") return end local tArgs = { ... } if #tArgs > 0 then - local nTask = shell.openTab( table.unpack( tArgs ) ) + local nTask = shell.openTab(table.unpack(tArgs)) if nTask then - shell.switchTab( nTask ) + shell.switchTab(nTask) end else - local nTask = shell.openTab( "shell" ) + local nTask = shell.openTab("shell") if nTask then - shell.switchTab( nTask ) + shell.switchTab(nTask) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/advanced/multishell.lua b/src/main/resources/assets/computercraft/lua/rom/programs/advanced/multishell.lua index 75dd3b3da..c675f2dc6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/advanced/multishell.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/advanced/multishell.lua @@ -12,84 +12,84 @@ local bWindowsResized = false local nScrollPos = 1 local bScrollRight = false -local function selectProcess( n ) +local function selectProcess(n) if nCurrentProcess ~= n then if nCurrentProcess then - local tOldProcess = tProcesses[ nCurrentProcess ] - tOldProcess.window.setVisible( false ) + local tOldProcess = tProcesses[nCurrentProcess] + tOldProcess.window.setVisible(false) end nCurrentProcess = n if nCurrentProcess then - local tNewProcess = tProcesses[ nCurrentProcess ] - tNewProcess.window.setVisible( true ) + local tNewProcess = tProcesses[nCurrentProcess] + tNewProcess.window.setVisible(true) tNewProcess.bInteracted = true end end end -local function setProcessTitle( n, sTitle ) - tProcesses[ n ].sTitle = sTitle +local function setProcessTitle(n, sTitle) + tProcesses[n].sTitle = sTitle end -local function resumeProcess( nProcess, sEvent, ... ) - local tProcess = tProcesses[ nProcess ] +local function resumeProcess(nProcess, sEvent, ...) + local tProcess = tProcesses[nProcess] local sFilter = tProcess.sFilter if sFilter == nil or sFilter == sEvent or sEvent == "terminate" then local nPreviousProcess = nRunningProcess nRunningProcess = nProcess - term.redirect( tProcess.terminal ) - local ok, result = coroutine.resume( tProcess.co, sEvent, ... ) + term.redirect(tProcess.terminal) + local ok, result = coroutine.resume(tProcess.co, sEvent, ...) tProcess.terminal = term.current() if ok then tProcess.sFilter = result else - printError( result ) + printError(result) end nRunningProcess = nPreviousProcess end end -local function launchProcess( bFocus, tProgramEnv, sProgramPath, ... ) - local tProgramArgs = table.pack( ... ) +local function launchProcess(bFocus, tProgramEnv, sProgramPath, ...) + local tProgramArgs = table.pack(...) local nProcess = #tProcesses + 1 local tProcess = {} - tProcess.sTitle = fs.getName( sProgramPath ) + tProcess.sTitle = fs.getName(sProgramPath) if bShowMenu then - tProcess.window = window.create( parentTerm, 1, 2, w, h - 1, false ) + tProcess.window = window.create(parentTerm, 1, 2, w, h - 1, false) else - tProcess.window = window.create( parentTerm, 1, 1, w, h, false ) + tProcess.window = window.create(parentTerm, 1, 1, w, h, false) end - tProcess.co = coroutine.create( function() - os.run( tProgramEnv, sProgramPath, table.unpack( tProgramArgs, 1, tProgramArgs.n ) ) + tProcess.co = coroutine.create(function() + os.run(tProgramEnv, sProgramPath, table.unpack(tProgramArgs, 1, tProgramArgs.n)) if not tProcess.bInteracted then - term.setCursorBlink( false ) - print( "Press any key to continue" ) - os.pullEvent( "char" ) + term.setCursorBlink(false) + print("Press any key to continue") + os.pullEvent("char") end - end ) + end) tProcess.sFilter = nil tProcess.terminal = tProcess.window tProcess.bInteracted = false - tProcesses[ nProcess ] = tProcess + tProcesses[nProcess] = tProcess if bFocus then - selectProcess( nProcess ) + selectProcess(nProcess) end - resumeProcess( nProcess ) + resumeProcess(nProcess) return nProcess end -local function cullProcess( nProcess ) - local tProcess = tProcesses[ nProcess ] - if coroutine.status( tProcess.co ) == "dead" then +local function cullProcess(nProcess) + local tProcess = tProcesses[nProcess] + if coroutine.status(tProcess.co) == "dead" then if nCurrentProcess == nProcess then - selectProcess( nil ) + selectProcess(nil) end - table.remove( tProcesses, nProcess ) + table.remove(tProcesses, nProcess) if nCurrentProcess == nil then if nProcess > 1 then - selectProcess( nProcess - 1 ) + selectProcess(nProcess - 1) elseif #tProcesses > 0 then - selectProcess( 1 ) + selectProcess(1) end end if nScrollPos ~= 1 then @@ -103,7 +103,7 @@ end local function cullProcesses() local culled = false for n = #tProcesses, 1, -1 do - culled = culled or cullProcess( n ) + culled = culled or cullProcess(n) end return culled end @@ -121,40 +121,40 @@ end local function redrawMenu() if bShowMenu then -- Draw menu - parentTerm.setCursorPos( 1, 1 ) - parentTerm.setBackgroundColor( menuOtherBgColor ) + parentTerm.setCursorPos(1, 1) + parentTerm.setBackgroundColor(menuOtherBgColor) parentTerm.clearLine() local nCharCount = 0 local nSize = parentTerm.getSize() if nScrollPos ~= 1 then - parentTerm.setTextColor( menuOtherTextColor ) - parentTerm.setBackgroundColor( menuOtherBgColor ) - parentTerm.write( "<" ) + parentTerm.setTextColor(menuOtherTextColor) + parentTerm.setBackgroundColor(menuOtherBgColor) + parentTerm.write("<") nCharCount = 1 end for n = nScrollPos, #tProcesses do if n == nCurrentProcess then - parentTerm.setTextColor( menuMainTextColor ) - parentTerm.setBackgroundColor( menuMainBgColor ) + parentTerm.setTextColor(menuMainTextColor) + parentTerm.setBackgroundColor(menuMainBgColor) else - parentTerm.setTextColor( menuOtherTextColor ) - parentTerm.setBackgroundColor( menuOtherBgColor ) + parentTerm.setTextColor(menuOtherTextColor) + parentTerm.setBackgroundColor(menuOtherBgColor) end - parentTerm.write( " " .. tProcesses[n].sTitle .. " " ) + parentTerm.write(" " .. tProcesses[n].sTitle .. " ") nCharCount = nCharCount + #tProcesses[n].sTitle + 2 end if nCharCount > nSize then - parentTerm.setTextColor( menuOtherTextColor ) - parentTerm.setBackgroundColor( menuOtherBgColor ) - parentTerm.setCursorPos( nSize, 1 ) - parentTerm.write( ">" ) + parentTerm.setTextColor(menuOtherTextColor) + parentTerm.setBackgroundColor(menuOtherBgColor) + parentTerm.setCursorPos(nSize, 1) + parentTerm.write(">") bScrollRight = true else bScrollRight = false end -- Put the cursor back where it should be - local tProcess = tProcesses[ nCurrentProcess ] + local tProcess = tProcesses[nCurrentProcess] if tProcess then tProcess.window.restoreCursor() end @@ -174,15 +174,15 @@ local function resizeWindows() local tProcess = tProcesses[n] local x, y = tProcess.window.getCursorPos() if y > windowHeight then - tProcess.window.scroll( y - windowHeight ) - tProcess.window.setCursorPos( x, windowHeight ) + tProcess.window.scroll(y - windowHeight) + tProcess.window.setCursorPos(x, windowHeight) end - tProcess.window.reposition( 1, windowY, w, windowHeight ) + tProcess.window.reposition(1, windowY, w, windowHeight) end bWindowsResized = true end -local function setMenuVisible( bVis ) +local function setMenuVisible(bVis) if bShowMenu ~= bVis then bShowMenu = bVis resizeWindows() @@ -196,17 +196,17 @@ function multishell.getFocus() return nCurrentProcess end -function multishell.setFocus( n ) +function multishell.setFocus(n) expect(1, n, "number") if n >= 1 and n <= #tProcesses then - selectProcess( n ) + selectProcess(n) redrawMenu() return true end return false end -function multishell.getTitle( n ) +function multishell.getTitle(n) expect(1, n, "number") if n >= 1 and n <= #tProcesses then return tProcesses[n].sTitle @@ -214,11 +214,11 @@ function multishell.getTitle( n ) return nil end -function multishell.setTitle( n, sTitle ) +function multishell.setTitle(n, sTitle) expect(1, n, "number") expect(2, sTitle, "string") if n >= 1 and n <= #tProcesses then - setProcessTitle( n, sTitle ) + setProcessTitle(n, sTitle) redrawMenu() end end @@ -227,14 +227,14 @@ function multishell.getCurrent() return nRunningProcess end -function multishell.launch( tProgramEnv, sProgramPath, ... ) +function multishell.launch(tProgramEnv, sProgramPath, ...) expect(1, tProgramEnv, "table") expect(2, sProgramPath, "string") local previousTerm = term.current() - setMenuVisible( #tProcesses + 1 >= 2 ) - local nResult = launchProcess( false, tProgramEnv, sProgramPath, ... ) + setMenuVisible(#tProcesses + 1 >= 2) + local nResult = launchProcess(false, tProgramEnv, sProgramPath, ...) redrawMenu() - term.redirect( previousTerm ) + term.redirect(previousTerm) return nResult end @@ -244,16 +244,16 @@ end -- Begin parentTerm.clear() -setMenuVisible( false ) -launchProcess( true, { +setMenuVisible(false) +launchProcess(true, { ["shell"] = shell, ["multishell"] = multishell, -}, "/rom/programs/shell.lua" ) +}, "/rom/programs/shell.lua") -- Run processes while #tProcesses > 0 do -- Get the event - local tEventData = table.pack( os.pullEventRaw() ) + local tEventData = table.pack(os.pullEventRaw()) local sEvent = tEventData[1] if sEvent == "term_resize" then -- Resize event @@ -264,9 +264,9 @@ while #tProcesses > 0 do elseif sEvent == "char" or sEvent == "key" or sEvent == "key_up" or sEvent == "paste" or sEvent == "terminate" then -- Keyboard event -- Passthrough to current process - resumeProcess( nCurrentProcess, table.unpack( tEventData, 1, tEventData.n ) ) - if cullProcess( nCurrentProcess ) then - setMenuVisible( #tProcesses >= 2 ) + resumeProcess(nCurrentProcess, table.unpack(tEventData, 1, tEventData.n)) + if cullProcess(nCurrentProcess) then + setMenuVisible(#tProcesses >= 2) redrawMenu() end @@ -289,7 +289,7 @@ while #tProcesses > 0 do for n = nScrollPos, #tProcesses do local tabEnd = tabStart + #tProcesses[n].sTitle + 1 if x >= tabStart and x <= tabEnd then - selectProcess( n ) + selectProcess(n) redrawMenu() break end @@ -298,9 +298,9 @@ while #tProcesses > 0 do end else -- Passthrough to current process - resumeProcess( nCurrentProcess, sEvent, button, x, bShowMenu and y - 1 or y ) - if cullProcess( nCurrentProcess ) then - setMenuVisible( #tProcesses >= 2 ) + resumeProcess(nCurrentProcess, sEvent, button, x, bShowMenu and y - 1 or y) + if cullProcess(nCurrentProcess) then + setMenuVisible(#tProcesses >= 2) redrawMenu() end end @@ -318,9 +318,9 @@ while #tProcesses > 0 do end elseif not (bShowMenu and y == 1) then -- Passthrough to current process - resumeProcess( nCurrentProcess, sEvent, p1, x, bShowMenu and y - 1 or y ) - if cullProcess( nCurrentProcess ) then - setMenuVisible( #tProcesses >= 2 ) + resumeProcess(nCurrentProcess, sEvent, p1, x, bShowMenu and y - 1 or y) + if cullProcess(nCurrentProcess) then + setMenuVisible(#tProcesses >= 2) redrawMenu() end end @@ -330,10 +330,10 @@ while #tProcesses > 0 do -- Passthrough to all processes local nLimit = #tProcesses -- Storing this ensures any new things spawned don't get the event for n = 1, nLimit do - resumeProcess( n, table.unpack( tEventData, 1, tEventData.n ) ) + resumeProcess(n, table.unpack(tEventData, 1, tEventData.n)) end if cullProcesses() then - setMenuVisible( #tProcesses >= 2 ) + setMenuVisible(#tProcesses >= 2) redrawMenu() end end @@ -342,15 +342,15 @@ while #tProcesses > 0 do -- Pass term_resize to all processes local nLimit = #tProcesses -- Storing this ensures any new things spawned don't get the event for n = 1, nLimit do - resumeProcess( n, "term_resize" ) + resumeProcess(n, "term_resize") end bWindowsResized = false if cullProcesses() then - setMenuVisible( #tProcesses >= 2 ) + setMenuVisible(#tProcesses >= 2) redrawMenu() end end end -- Shutdown -term.redirect( parentTerm ) +term.redirect(parentTerm) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/alias.lua b/src/main/resources/assets/computercraft/lua/rom/programs/alias.lua index c4f8c5ffe..d35eb7707 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/alias.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/alias.lua @@ -1,7 +1,7 @@ local tArgs = { ... } if #tArgs > 2 then - print( "Usage: alias " ) + print("Usage: alias ") return end @@ -10,17 +10,17 @@ local sProgram = tArgs[2] if sAlias and sProgram then -- Set alias - shell.setAlias( sAlias, sProgram ) + shell.setAlias(sAlias, sProgram) elseif sAlias then -- Clear alias - shell.clearAlias( sAlias ) + shell.clearAlias(sAlias) else -- List aliases local tAliases = shell.aliases() local tList = {} - for sAlias, sCommand in pairs( tAliases ) do - table.insert( tList, sAlias .. ":" .. sCommand ) + for sAlias, sCommand in pairs(tAliases) do + table.insert(tList, sAlias .. ":" .. sCommand) end - table.sort( tList ) - textutils.pagedTabulate( tList ) + table.sort(tList) + textutils.pagedTabulate(tList) end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/apis.lua b/src/main/resources/assets/computercraft/lua/rom/programs/apis.lua index 7ef16e96f..801477650 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/apis.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/apis.lua @@ -1,15 +1,15 @@ local tApis = {} -for k, v in pairs( _G ) do +for k, v in pairs(_G) do if type(k) == "string" and type(v) == "table" and k ~= "_G" then - table.insert( tApis, k ) + table.insert(tApis, k) end end -table.insert( tApis, "shell" ) -table.insert( tApis, "package" ) +table.insert(tApis, "shell") +table.insert(tApis, "package") if multishell then - table.insert( tApis, "multishell" ) + table.insert(tApis, "multishell") end -table.sort( tApis ) +table.sort(tApis) -textutils.pagedTabulate( tApis ) +textutils.pagedTabulate(tApis) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/cd.lua b/src/main/resources/assets/computercraft/lua/rom/programs/cd.lua index abadf5366..6f05a98ac 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/cd.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/cd.lua @@ -1,14 +1,14 @@ local tArgs = { ... } if #tArgs < 1 then - print( "Usage: cd " ) + print("Usage: cd ") return end -local sNewDir = shell.resolve( tArgs[1] ) -if fs.isDir( sNewDir ) then - shell.setDir( sNewDir ) +local sNewDir = shell.resolve(tArgs[1]) +if fs.isDir(sNewDir) then + shell.setDir(sNewDir) else - print( "Not a directory" ) + print("Not a directory") return end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/clear.lua b/src/main/resources/assets/computercraft/lua/rom/programs/clear.lua index 50283c8d7..d69a29993 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/clear.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/clear.lua @@ -1,2 +1,2 @@ term.clear() -term.setCursorPos( 1, 1 ) +term.setCursorPos(1, 1) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/command/commands.lua b/src/main/resources/assets/computercraft/lua/rom/programs/command/commands.lua index 230d766f7..dacc5626d 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/command/commands.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/command/commands.lua @@ -1,16 +1,16 @@ if not commands then - printError( "Requires a Command Computer." ) + printError("Requires a Command Computer.") return end local tCommands = commands.list() -table.sort( tCommands ) +table.sort(tCommands) if term.isColor() then - term.setTextColor( colors.green ) + term.setTextColor(colors.green) end -print( "Available commands:" ) -term.setTextColor( colors.white ) +print("Available commands:") +term.setTextColor(colors.white) -textutils.pagedTabulate( tCommands ) +textutils.pagedTabulate(tCommands) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/command/exec.lua b/src/main/resources/assets/computercraft/lua/rom/programs/command/exec.lua index c54b3f567..b50c8ec20 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/command/exec.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/command/exec.lua @@ -1,40 +1,40 @@ local tArgs = { ... } if not commands then - printError( "Requires a Command Computer." ) + printError("Requires a Command Computer.") return end if #tArgs == 0 then - printError( "Usage: exec " ) + printError("Usage: exec ") return end -local function printSuccess( text ) +local function printSuccess(text) if term.isColor() then - term.setTextColor( colors.green ) + term.setTextColor(colors.green) end - print( text ) - term.setTextColor( colors.white ) + print(text) + term.setTextColor(colors.white) end -local sCommand = string.lower( tArgs[1] ) +local sCommand = string.lower(tArgs[1]) for n = 2, #tArgs do sCommand = sCommand .. " " .. tArgs[n] end -local bResult, tOutput = commands.exec( sCommand ) +local bResult, tOutput = commands.exec(sCommand) if bResult then - printSuccess( "Success" ) + printSuccess("Success") if #tOutput > 0 then for n = 1, #tOutput do - print( tOutput[n] ) + print(tOutput[n]) end end else - printError( "Failed" ) + printError("Failed") if #tOutput > 0 then for n = 1, #tOutput do - print( tOutput[n] ) + print(tOutput[n]) end end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/copy.lua b/src/main/resources/assets/computercraft/lua/rom/programs/copy.lua index c8d66459b..d50132f82 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/copy.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/copy.lua @@ -1,32 +1,32 @@ local tArgs = { ... } if #tArgs < 2 then - print( "Usage: cp " ) + print("Usage: cp ") return end -local sSource = shell.resolve( tArgs[1] ) -local sDest = shell.resolve( tArgs[2] ) -local tFiles = fs.find( sSource ) +local sSource = shell.resolve(tArgs[1]) +local sDest = shell.resolve(tArgs[2]) +local tFiles = fs.find(sSource) if #tFiles > 0 then - for _, sFile in ipairs( tFiles ) do - if fs.isDir( sDest ) then - fs.copy( sFile, fs.combine( sDest, fs.getName(sFile) ) ) + for _, sFile in ipairs(tFiles) do + if fs.isDir(sDest) then + fs.copy(sFile, fs.combine(sDest, fs.getName(sFile))) elseif #tFiles == 1 then - if fs.exists( sDest ) then - printError( "Destination exists" ) - elseif fs.isReadOnly( sDest ) then - printError( "Destination is read-only" ) - elseif fs.getFreeSpace( sDest ) < fs.getSize( sFile ) then - printError( "Not enough space" ) + if fs.exists(sDest) then + printError("Destination exists") + elseif fs.isReadOnly(sDest) then + printError("Destination is read-only") + elseif fs.getFreeSpace(sDest) < fs.getSize(sFile) then + printError("Not enough space") else - fs.copy( sFile, sDest ) + fs.copy(sFile, sDest) end else - printError( "Cannot overwrite file multiple times" ) + printError("Cannot overwrite file multiple times") return end end else - printError( "No matching files" ) + printError("No matching files") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/drive.lua b/src/main/resources/assets/computercraft/lua/rom/programs/drive.lua index 807cf88b4..799b65ecc 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/drive.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/drive.lua @@ -3,19 +3,19 @@ local tArgs = { ... } -- Get where a directory is mounted local sPath = shell.dir() if tArgs[1] ~= nil then - sPath = shell.resolve( tArgs[1] ) + sPath = shell.resolve(tArgs[1]) end -if fs.exists( sPath ) then - write( fs.getDrive( sPath ) .. " (" ) - local nSpace = fs.getFreeSpace( sPath ) +if fs.exists(sPath) then + write(fs.getDrive(sPath) .. " (") + local nSpace = fs.getFreeSpace(sPath) if nSpace >= 1000 * 1000 then - print( math.floor( nSpace / (100 * 1000) ) / 10 .. "MB remaining)" ) + print(math.floor(nSpace / (100 * 1000)) / 10 .. "MB remaining)") elseif nSpace >= 1000 then - print( math.floor( nSpace / 100 ) / 10 .. "KB remaining)" ) + print(math.floor(nSpace / 100) / 10 .. "KB remaining)") else - print( nSpace .. "B remaining)" ) + print(nSpace .. "B remaining)") end else - print( "No such path" ) + print("No such path") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua b/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua index 0125d5b12..a6ea4d8d0 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/edit.lua @@ -1,22 +1,22 @@ -- Get file to edit local tArgs = { ... } if #tArgs == 0 then - print( "Usage: edit " ) + print("Usage: edit ") return end -- Error checking -local sPath = shell.resolve( tArgs[1] ) -local bReadOnly = fs.isReadOnly( sPath ) -if fs.exists( sPath ) and fs.isDir( sPath ) then - print( "Cannot edit a directory." ) +local sPath = shell.resolve(tArgs[1]) +local bReadOnly = fs.isReadOnly(sPath) +if fs.exists(sPath) and fs.isDir(sPath) then + print("Cannot edit a directory.") return end -- Create .lua files by default -if not fs.exists( sPath ) and not string.find( sPath, "%." ) then +if not fs.exists(sPath) and not string.find(sPath, "%.") then local sExtension = settings.get("edit.default_extension", "") - if sExtension ~= "" and type( sExtension ) == "string" then + if sExtension ~= "" and type(sExtension) == "string" then sPath = sPath .. "." .. sExtension end end @@ -51,59 +51,59 @@ local bMenu = false local nMenuItem = 1 local tMenuItems = {} if not bReadOnly then - table.insert( tMenuItems, "Save" ) + table.insert(tMenuItems, "Save") end if shell.openTab then - table.insert( tMenuItems, "Run" ) + table.insert(tMenuItems, "Run") end -if peripheral.find( "printer" ) then - table.insert( tMenuItems, "Print" ) +if peripheral.find("printer") then + table.insert(tMenuItems, "Print") end -table.insert( tMenuItems, "Exit" ) +table.insert(tMenuItems, "Exit") local sStatus = "Press Ctrl to access menu" if #sStatus > w - 5 then sStatus = "Press Ctrl for menu" end -local function load( _sPath ) +local function load(_sPath) tLines = {} - if fs.exists( _sPath ) then - local file = io.open( _sPath, "r" ) + if fs.exists(_sPath) then + local file = io.open(_sPath, "r") local sLine = file:read() while sLine do - table.insert( tLines, sLine ) + table.insert(tLines, sLine) sLine = file:read() end file:close() end if #tLines == 0 then - table.insert( tLines, "" ) + table.insert(tLines, "") end end -local function save( _sPath ) +local function save(_sPath) -- Create intervening folder local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len()) - if not fs.exists( sDir ) then - fs.makeDir( sDir ) + if not fs.exists(sDir) then + fs.makeDir(sDir) end -- Save local file, fileerr local function innerSave() - file, fileerr = fs.open( _sPath, "w" ) + file, fileerr = fs.open(_sPath, "w") if file then - for _, sLine in ipairs( tLines ) do - file.write( sLine .. "\n" ) + for _, sLine in ipairs(tLines) do + file.write(sLine .. "\n") end else - error( "Failed to open " .. _sPath ) + error("Failed to open " .. _sPath) end end - local ok, err = pcall( innerSave ) + local ok, err = pcall(innerSave) if file then file.close() end @@ -134,38 +134,38 @@ local tKeywords = { ["while"] = true, } -local function tryWrite( sLine, regex, colour ) - local match = string.match( sLine, regex ) +local function tryWrite(sLine, regex, colour) + local match = string.match(sLine, regex) if match then if type(colour) == "number" then - term.setTextColour( colour ) + term.setTextColour(colour) else - term.setTextColour( colour(match) ) + term.setTextColour(colour(match)) end - term.write( match ) - term.setTextColour( textColour ) - return string.sub( sLine, #match + 1 ) + term.write(match) + term.setTextColour(textColour) + return string.sub(sLine, #match + 1) end return nil end -local function writeHighlighted( sLine ) +local function writeHighlighted(sLine) while #sLine > 0 do sLine = - tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or - tryWrite( sLine, "^%-%-.*", commentColour ) or - tryWrite( sLine, "^\"\"", stringColour ) or - tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or - tryWrite( sLine, "^\'\'", stringColour ) or - tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or - tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or - tryWrite( sLine, "^[%w_]+", function( match ) - if tKeywords[ match ] then + tryWrite(sLine, "^%-%-%[%[.-%]%]", commentColour) or + tryWrite(sLine, "^%-%-.*", commentColour) or + tryWrite(sLine, "^\"\"", stringColour) or + tryWrite(sLine, "^\".-[^\\]\"", stringColour) or + tryWrite(sLine, "^\'\'", stringColour) or + tryWrite(sLine, "^\'.-[^\\]\'", stringColour) or + tryWrite(sLine, "^%[%[.-%]%]", stringColour) or + tryWrite(sLine, "^[%w_]+", function(match) + if tKeywords[match] then return keywordColour end return textColour - end ) or - tryWrite( sLine, "^[^%w_]", textColour ) + end) or + tryWrite(sLine, "^[^%w_]", textColour) end end @@ -173,14 +173,14 @@ local tCompletions local nCompletion local tCompleteEnv = _ENV -local function complete( sLine ) - if settings.get( "edit.autocomplete" ) then - local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.:]+$" ) +local function complete(sLine) + if settings.get("edit.autocomplete") then + local nStartPos = string.find(sLine, "[a-zA-Z0-9_%.:]+$") if nStartPos then - sLine = string.sub( sLine, nStartPos ) + sLine = string.sub(sLine, nStartPos) end if #sLine > 0 then - return textutils.complete( sLine, tCompleteEnv ) + return textutils.complete(sLine, tCompleteEnv) end end return nil @@ -189,7 +189,7 @@ end local function recomplete() local sLine = tLines[y] if not bMenu and not bReadOnly and x == #sLine + 1 then - tCompletions = complete( sLine ) + tCompletions = complete(sLine) if tCompletions and #tCompletions > 0 then nCompletion = 1 else @@ -201,85 +201,85 @@ local function recomplete() end end -local function writeCompletion( sLine ) +local function writeCompletion(sLine) if nCompletion then - local sCompletion = tCompletions[ nCompletion ] - term.setTextColor( colours.white ) - term.setBackgroundColor( colours.grey ) - term.write( sCompletion ) - term.setTextColor( textColour ) - term.setBackgroundColor( bgColour ) + local sCompletion = tCompletions[nCompletion] + term.setTextColor(colours.white) + term.setBackgroundColor(colours.grey) + term.write(sCompletion) + term.setTextColor(textColour) + term.setBackgroundColor(bgColour) end end local function redrawText() local cursorX, cursorY = x, y for y = 1, h - 1 do - term.setCursorPos( 1 - scrollX, y ) + term.setCursorPos(1 - scrollX, y) term.clearLine() - local sLine = tLines[ y + scrollY ] + local sLine = tLines[y + scrollY] if sLine ~= nil then - writeHighlighted( sLine ) + writeHighlighted(sLine) if cursorY == y and cursorX == #sLine + 1 then writeCompletion() end end end - term.setCursorPos( x - scrollX, y - scrollY ) + term.setCursorPos(x - scrollX, y - scrollY) end local function redrawLine(_nY) local sLine = tLines[_nY] if sLine then - term.setCursorPos( 1 - scrollX, _nY - scrollY ) + term.setCursorPos(1 - scrollX, _nY - scrollY) term.clearLine() - writeHighlighted( sLine ) + writeHighlighted(sLine) if _nY == y and x == #sLine + 1 then writeCompletion() end - term.setCursorPos( x - scrollX, _nY - scrollY ) + term.setCursorPos(x - scrollX, _nY - scrollY) end end local function redrawMenu() -- Clear line - term.setCursorPos( 1, h ) + term.setCursorPos(1, h) term.clearLine() -- Draw line numbers - term.setCursorPos( w - #( "Ln " .. y ) + 1, h ) - term.setTextColour( highlightColour ) - term.write( "Ln " ) - term.setTextColour( textColour ) - term.write( y ) + term.setCursorPos(w - #("Ln " .. y) + 1, h) + term.setTextColour(highlightColour) + term.write("Ln ") + term.setTextColour(textColour) + term.write(y) - term.setCursorPos( 1, h ) + term.setCursorPos(1, h) if bMenu then -- Draw menu - term.setTextColour( textColour ) - for nItem, sItem in pairs( tMenuItems ) do + term.setTextColour(textColour) + for nItem, sItem in pairs(tMenuItems) do if nItem == nMenuItem then - term.setTextColour( highlightColour ) - term.write( "[" ) - term.setTextColour( textColour ) - term.write( sItem ) - term.setTextColour( highlightColour ) - term.write( "]" ) - term.setTextColour( textColour ) + term.setTextColour(highlightColour) + term.write("[") + term.setTextColour(textColour) + term.write(sItem) + term.setTextColour(highlightColour) + term.write("]") + term.setTextColour(textColour) else - term.write( " " .. sItem .. " " ) + term.write(" " .. sItem .. " ") end end else -- Draw status - term.setTextColour( highlightColour ) - term.write( sStatus ) - term.setTextColour( textColour ) + term.setTextColour(highlightColour) + term.write(sStatus) + term.setTextColour(textColour) end -- Reset cursor - term.setCursorPos( x - scrollX, y - scrollY ) + term.setCursorPos(x - scrollX, y - scrollY) end local tMenuFuncs = { @@ -287,7 +287,7 @@ local tMenuFuncs = { if bReadOnly then sStatus = "Access denied" else - local ok, _, fileerr = save( sPath ) + local ok, _, fileerr = save(sPath) if ok then sStatus = "Saved to " .. sPath else @@ -301,14 +301,14 @@ local tMenuFuncs = { redrawMenu() end, Print = function() - local printer = peripheral.find( "printer" ) + local printer = peripheral.find("printer") if not printer then sStatus = "No printer attached" return end local nPage = 0 - local sName = fs.getName( sPath ) + local sName = fs.getName(sPath) if printer.getInkLevel() < 1 then sStatus = "Printer out of ink" return @@ -326,7 +326,7 @@ local tMenuFuncs = { } printerTerminal.scroll = function() if nPage == 1 then - printer.setPageTitle( sName .. " (page " .. nPage .. ")" ) + printer.setPageTitle(sName .. " (page " .. nPage .. ")") end while not printer.newPage() do @@ -338,38 +338,38 @@ local tMenuFuncs = { sStatus = "Printer output tray full, please empty" end - term.redirect( screenTerminal ) + term.redirect(screenTerminal) redrawMenu() - term.redirect( printerTerminal ) + term.redirect(printerTerminal) sleep(0.5) end nPage = nPage + 1 if nPage == 1 then - printer.setPageTitle( sName ) + printer.setPageTitle(sName) else - printer.setPageTitle( sName .. " (page " .. nPage .. ")" ) + printer.setPageTitle(sName .. " (page " .. nPage .. ")") end end bMenu = false - term.redirect( printerTerminal ) - local ok, error = pcall( function() + term.redirect(printerTerminal) + local ok, error = pcall(function() term.scroll() - for _, sLine in ipairs( tLines ) do - print( sLine ) + for _, sLine in ipairs(tLines) do + print(sLine) end - end ) - term.redirect( screenTerminal ) + end) + term.redirect(screenTerminal) if not ok then - print( error ) + print(error) end while not printer.endPage() do sStatus = "Printer output tray full, please empty" redrawMenu() - sleep( 0.5 ) + sleep(0.5) end bMenu = true @@ -385,15 +385,15 @@ local tMenuFuncs = { end, Run = function() local sTempPath = "/.temp" - local ok = save( sTempPath ) + local ok = save(sTempPath) if ok then - local nTask = shell.openTab( sTempPath ) + local nTask = shell.openTab(sTempPath) if nTask then - shell.switchTab( nTask ) + shell.switchTab(nTask) else sStatus = "Error starting Task" end - fs.delete( sTempPath ) + fs.delete(sTempPath) else sStatus = "Error saving to " .. sTempPath end @@ -401,16 +401,16 @@ local tMenuFuncs = { end, } -local function doMenuItem( _n ) +local function doMenuItem(_n) tMenuFuncs[tMenuItems[_n]]() if bMenu then bMenu = false - term.setCursorBlink( true ) + term.setCursorBlink(true) end redrawMenu() end -local function setCursor( newX, newY ) +local function setCursor(newX, newY) local _, oldY = x, y x, y = newX, newY local screenX = x - scrollX @@ -441,12 +441,12 @@ local function setCursor( newX, newY ) if bRedraw then redrawText() elseif y ~= oldY then - redrawLine( oldY ) - redrawLine( y ) + redrawLine(oldY) + redrawLine(y) else - redrawLine( y ) + redrawLine(y) end - term.setCursorPos( screenX, screenY ) + term.setCursorPos(screenX, screenY) redrawMenu() end @@ -454,10 +454,10 @@ end -- Actual program functionality begins load(sPath) -term.setBackgroundColour( bgColour ) +term.setBackgroundColour(bgColour) term.clear() term.setCursorPos(x, y) -term.setCursorBlink( true ) +term.setCursorBlink(true) recomplete() redrawText() @@ -466,9 +466,9 @@ redrawMenu() local function acceptCompletion() if nCompletion then -- Append the completion - local sCompletion = tCompletions[ nCompletion ] + local sCompletion = tCompletions[nCompletion] tLines[y] = tLines[y] .. sCompletion - setCursor( x + #sCompletion , y ) + setCursor(x + #sCompletion , y) end end @@ -490,7 +490,7 @@ while bRunning do elseif y > 1 then -- Move cursor up setCursor( - math.min( x, #tLines[y - 1] + 1 ), + math.min(x, #tLines[y - 1] + 1), y - 1 ) end @@ -511,7 +511,7 @@ while bRunning do elseif y < #tLines then -- Move cursor down setCursor( - math.min( x, #tLines[y + 1] + 1 ), + math.min(x, #tLines[y + 1] + 1), y + 1 ) end @@ -527,7 +527,7 @@ while bRunning do -- Indent line local sLine = tLines[y] tLines[y] = string.sub(sLine, 1, x - 1) .. " " .. string.sub(sLine, x) - setCursor( x + 4, y ) + setCursor(x + 4, y) end end @@ -542,7 +542,7 @@ while bRunning do newY = 1 end setCursor( - math.min( x, #tLines[newY] + 1 ), + math.min(x, #tLines[newY] + 1), newY ) end @@ -557,8 +557,8 @@ while bRunning do else newY = #tLines end - local newX = math.min( x, #tLines[newY] + 1 ) - setCursor( newX, newY ) + local newX = math.min(x, #tLines[newY] + 1) + setCursor(newX, newY) end elseif param == keys.home then @@ -576,7 +576,7 @@ while bRunning do -- Move cursor to the end local nLimit = #tLines[y] + 1 if x < nLimit then - setCursor( nLimit, y ) + setCursor(nLimit, y) end end @@ -585,9 +585,9 @@ while bRunning do if not bMenu then if x > 1 then -- Move cursor left - setCursor( x - 1, y ) + setCursor(x - 1, y) elseif x == 1 and y > 1 then - setCursor( #tLines[y - 1] + 1, y - 1 ) + setCursor(#tLines[y - 1] + 1, y - 1) end else -- Move menu left @@ -604,13 +604,13 @@ while bRunning do local nLimit = #tLines[y] + 1 if x < nLimit then -- Move cursor right - setCursor( x + 1, y ) + setCursor(x + 1, y) elseif nCompletion and x == #tLines[y] + 1 then -- Accept autocomplete acceptCompletion() elseif x == nLimit and y < #tLines then -- Go to next line - setCursor( 1, y + 1 ) + setCursor(1, y + 1) end else -- Move menu right @@ -632,7 +632,7 @@ while bRunning do redrawLine(y) elseif y < #tLines then tLines[y] = tLines[y] .. tLines[y + 1] - table.remove( tLines, y + 1 ) + table.remove(tLines, y + 1) recomplete() redrawText() end @@ -646,17 +646,17 @@ while bRunning do local sLine = tLines[y] if x > 4 and string.sub(sLine, x - 4, x - 1) == " " and not string.sub(sLine, 1, x - 1):find("%S") then tLines[y] = string.sub(sLine, 1, x - 5) .. string.sub(sLine, x) - setCursor( x - 4, y ) + setCursor(x - 4, y) else tLines[y] = string.sub(sLine, 1, x - 2) .. string.sub(sLine, x) - setCursor( x - 1, y ) + setCursor(x - 1, y) end elseif y > 1 then -- Remove newline local sPrevLen = #tLines[y - 1] tLines[y - 1] = tLines[y - 1] .. tLines[y] - table.remove( tLines, y ) - setCursor( sPrevLen + 1, y - 1 ) + table.remove(tLines, y) + setCursor(sPrevLen + 1, y - 1) redrawText() end end @@ -671,13 +671,13 @@ while bRunning do spaces = 0 end tLines[y] = string.sub(sLine, 1, x - 1) - table.insert( tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x) ) - setCursor( spaces + 1, y + 1 ) + table.insert(tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x)) + setCursor(spaces + 1, y + 1) redrawText() elseif bMenu then -- Menu selection - doMenuItem( nMenuItem ) + doMenuItem(nMenuItem) end @@ -685,9 +685,9 @@ while bRunning do -- Menu toggle bMenu = not bMenu if bMenu then - term.setCursorBlink( false ) + term.setCursorBlink(false) else - term.setCursorBlink( true ) + term.setCursorBlink(true) end redrawMenu() @@ -698,13 +698,13 @@ while bRunning do -- Input text local sLine = tLines[y] tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x) - setCursor( x + 1, y ) + setCursor(x + 1, y) elseif bMenu then -- Select menu items - for n, sMenuItem in ipairs( tMenuItems ) do + for n, sMenuItem in ipairs(tMenuItems) do if string.lower(string.sub(sMenuItem, 1, 1)) == string.lower(param) then - doMenuItem( n ) + doMenuItem(n) break end end @@ -715,13 +715,13 @@ while bRunning do -- Close menu if open if bMenu then bMenu = false - term.setCursorBlink( true ) + term.setCursorBlink(true) redrawMenu() end -- Input text local sLine = tLines[y] tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x) - setCursor( x + #param , y ) + setCursor(x + #param , y) end elseif sEvent == "mouse_click" then @@ -730,9 +730,9 @@ while bRunning do -- Left click local cx, cy = param2, param3 if cy < h then - local newY = math.min( math.max( scrollY + cy, 1 ), #tLines ) - local newX = math.min( math.max( scrollX + cx, 1 ), #tLines[newY] + 1 ) - setCursor( newX, newY ) + local newY = math.min(math.max(scrollY + cy, 1), #tLines) + local newX = math.min(math.max(scrollX + cx, 1), #tLines[newY] + 1) + setCursor(newX, newY) end end end @@ -761,7 +761,7 @@ while bRunning do elseif sEvent == "term_resize" then w, h = term.getSize() - setCursor( x, y ) + setCursor(x, y) redrawMenu() redrawText() @@ -770,5 +770,5 @@ end -- Cleanup term.clear() -term.setCursorBlink( false ) -term.setCursorPos( 1, 1 ) +term.setCursorBlink(false) +term.setCursorPos(1, 1) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/eject.lua b/src/main/resources/assets/computercraft/lua/rom/programs/eject.lua index ad47bbd99..635c2f4e8 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/eject.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/eject.lua @@ -2,17 +2,17 @@ -- Get arguments local tArgs = { ... } if #tArgs == 0 then - print( "Usage: eject " ) + print("Usage: eject ") return end local sDrive = tArgs[1] -- Check the disk exists -local bPresent = disk.isPresent( sDrive ) +local bPresent = disk.isPresent(sDrive) if not bPresent then - print( "Nothing in " .. sDrive .. " drive" ) + print("Nothing in " .. sDrive .. " drive") return end -disk.eject( sDrive ) +disk.eject(sDrive) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua index d2f3a5159..fbe228091 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/paint.lua @@ -32,7 +32,7 @@ if not term.isColour() then end -- Determines if the file exists, and can be edited on this computer -local tArgs = {...} +local tArgs = { ... } if #tArgs == 0 then print("Usage: paint ") return @@ -45,9 +45,9 @@ if fs.exists(sPath) and fs.isDir(sPath) then end -- Create .nfp files by default -if not fs.exists( sPath ) and not string.find( sPath, "%." ) then +if not fs.exists(sPath) and not string.find(sPath, "%.") then local sExtension = settings.get("paint.default_extension", "") - if sExtension ~= "" and type( sExtension ) == "string" then + if sExtension ~= "" and type(sExtension) == "string" then sPath = sPath .. "." .. sExtension end end @@ -57,7 +57,7 @@ end -- Functions -- --------------- -local function getCanvasPixel( x, y ) +local function getCanvasPixel(x, y) if canvas[y] then return canvas[y][x] end @@ -69,12 +69,12 @@ end params: colour = the number to convert to a hex value returns: a string representing the chosen colour ]] -local function getCharOf( colour ) +local function getCharOf(colour) -- Incorrect values always convert to nil if type(colour) == "number" then - local value = math.floor( math.log(colour) / math.log(2) ) + 1 + local value = math.floor(math.log(colour) / math.log(2)) + 1 if value >= 1 and value <= 16 then - return string.sub( "0123456789abcdef", value, value ) + return string.sub("0123456789abcdef", value, value) end end return " " @@ -87,9 +87,9 @@ end ]] local tColourLookup = {} for n = 1, 16 do - tColourLookup[ string.byte( "0123456789abcdef", n, n ) ] = 2 ^ (n - 1) + tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1) end -local function getColourOf( char ) +local function getColourOf(char) -- Values not in the hex table are transparent (canvas coloured) return tColourLookup[char] end @@ -107,9 +107,9 @@ local function load(path) while sLine do local line = {} for x = 1, w - 2 do - line[x] = getColourOf( string.byte(sLine, x, x) ) + line[x] = getColourOf(string.byte(sLine, x, x)) end - table.insert( canvas, line ) + table.insert(canvas, line) sLine = file.readLine() end file.close() @@ -128,7 +128,7 @@ local function save(path) fs.makeDir(sDir) end - local file, err = fs.open( path, "w" ) + local file, err = fs.open(path, "w") if not file then return false, err end @@ -140,13 +140,13 @@ local function save(path) local sLine = "" local nLastChar = 0 for x = 1, w - 2 do - local c = getCharOf( getCanvasPixel( x, y ) ) + local c = getCharOf(getCanvasPixel(x, y)) sLine = sLine .. c if c ~= " " then nLastChar = x end end - sLine = string.sub( sLine, 1, nLastChar ) + sLine = string.sub(sLine, 1, nLastChar) tLines[y] = sLine if #sLine > 0 then nLastLine = y @@ -155,7 +155,7 @@ local function save(path) -- Save out for n = 1, nLastLine do - file.writeLine( tLines[ n ] ) + file.writeLine(tLines[n]) end file.close() return true @@ -176,38 +176,38 @@ local function drawInterface() -- Colour Picker for i = 1, 16 do term.setCursorPos(w - 1, i) - term.setBackgroundColour( 2 ^ (i - 1) ) + term.setBackgroundColour(2 ^ (i - 1)) term.write(" ") end term.setCursorPos(w - 1, 17) - term.setBackgroundColour( canvasColour ) - term.setTextColour( colours.grey ) + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) term.write("\127\127") -- Left and Right Selected Colours do term.setCursorPos(w - 1, 18) if leftColour ~= nil then - term.setBackgroundColour( leftColour ) + term.setBackgroundColour(leftColour) term.write(" ") else - term.setBackgroundColour( canvasColour ) - term.setTextColour( colours.grey ) + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) term.write("\127") end if rightColour ~= nil then - term.setBackgroundColour( rightColour ) + term.setBackgroundColour(rightColour) term.write(" ") else - term.setBackgroundColour( canvasColour ) - term.setTextColour( colours.grey ) + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) term.write("\127") end end -- Padding - term.setBackgroundColour( canvasColour ) + term.setBackgroundColour(canvasColour) for i = 20, h - 1 do term.setCursorPos(w - 1, i) term.write(" ") @@ -218,15 +218,15 @@ end Converts a single pixel of a single line of the canvas and draws it returns: nil ]] -local function drawCanvasPixel( x, y ) - local pixel = getCanvasPixel( x, y ) +local function drawCanvasPixel(x, y) + local pixel = getCanvasPixel(x, y) if pixel then - term.setBackgroundColour( pixel or canvasColour ) + term.setBackgroundColour(pixel or canvasColour) term.setCursorPos(x, y) term.write(" ") else - term.setBackgroundColour( canvasColour ) - term.setTextColour( colours.grey ) + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) term.setCursorPos(x, y) term.write("\127") end @@ -236,9 +236,9 @@ end Converts each colour in a single line of the canvas and draws it returns: nil ]] -local function drawCanvasLine( y ) +local function drawCanvasLine(y) for x = 1, w - 2 do - drawCanvasPixel( x, y ) + drawCanvasPixel(x, y) end end @@ -248,7 +248,7 @@ end ]] local function drawCanvas() for y = 1, h - 1 do - drawCanvasLine( y ) + drawCanvasLine(y) end end @@ -377,7 +377,7 @@ local function handleEvents() end canvas[p3][p2] = paintColour - drawCanvasPixel( p2, p3 ) + drawCanvasPixel(p2, p3) end elseif id == "key" then if p1 == keys.leftCtrl or p1 == keys.rightCtrl then diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua index 3f68e3859..7cc1233a6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/advanced/redirection.lua @@ -59,10 +59,10 @@ local cR4 = colors.yellow local tArgs = { ... } --Functions-- -local function printCentred( yc, stg ) +local function printCentred(yc, stg) local xc = math.floor((TermW - #stg) / 2) + 1 term.setCursorPos(xc, yc) - term.write( stg ) + term.write(stg) end local function centerOrgin() @@ -173,9 +173,9 @@ end local function loadLevel(nNum) sLevelTitle = "Level " .. nNum if nNum == nil then return error("nNum == nil") end - local sDir = fs.getDir( shell.getRunningProgram() ) + local sDir = fs.getDir(shell.getRunningProgram()) local sLevelD = sDir .. "/levels/" .. tostring(nNum) .. ".dat" - if not ( fs.exists(sLevelD) or fs.isDir(sLevelD) ) then return error("Level Not Exists : " .. sLevelD) end + if not (fs.exists(sLevelD) or fs.isDir(sLevelD)) then return error("Level Not Exists : " .. sLevelD) end fLevel = fs.open(sLevelD, "r") local wl = true Blocks = tonumber(string.sub(fLevel.readLine(), 1, 1)) @@ -512,18 +512,18 @@ local function gRender(sContext) end function InterFace.drawBar() - term.setBackgroundColor( colors.black ) - term.setTextColor( InterFace.cTitle ) - printCentred( 1, " " .. sLevelTitle .. " " ) + term.setBackgroundColor(colors.black) + term.setTextColor(InterFace.cTitle) + printCentred(1, " " .. sLevelTitle .. " ") term.setCursorPos(1, 1) - term.setBackgroundColor( cW ) - write( " " ) - term.setBackgroundColor( colors.black ) - write( " x " .. tostring(Blocks) .. " " ) + term.setBackgroundColor(cW) + write(" ") + term.setBackgroundColor(colors.black) + write(" x " .. tostring(Blocks) .. " ") - term.setCursorPos( TermW - 8, TermH ) - term.setBackgroundColor( colors.black ) + term.setCursorPos(TermW - 8, TermH) + term.setBackgroundColor(colors.black) term.setTextColour(InterFace.cSpeedD) write(" <<") if bPaused then @@ -539,9 +539,9 @@ function InterFace.drawBar() end write(" >>") - term.setCursorPos( TermW - 1, 1 ) - term.setBackgroundColor( colors.black ) - term.setTextColour( InterFace.cExit ) + term.setCursorPos(TermW - 1, 1) + term.setBackgroundColor(colors.black) + term.setTextColour(InterFace.cExit) write(" X") term.setBackgroundColor(colors.black) end @@ -612,7 +612,7 @@ local function startG(LevelN) elseif isExit == "retry" then return LevelN elseif fExit == "yes" then - if fs.exists( fs.getDir( shell.getRunningProgram() ) .. "/levels/" .. tostring(LevelN + 1) .. ".dat" ) then + if fs.exists(fs.getDir(shell.getRunningProgram()) .. "/levels/" .. tostring(LevelN + 1) .. ".dat") then return LevelN + 1 else return nil @@ -629,26 +629,26 @@ local ok, err = true, nil --Menu-- local sStartLevel = tArgs[1] if ok and not sStartLevel then - ok, err = pcall( function() + ok, err = pcall(function() term.setTextColor(colors.white) - term.setBackgroundColor( colors.black ) + term.setBackgroundColor(colors.black) term.clear() drawStars() - term.setTextColor( colors.red ) - printCentred( TermH / 2 - 1, " REDIRECTION " ) - printCentred( TermH / 2 - 0, " ComputerCraft Edition " ) - term.setTextColor( colors.yellow ) - printCentred( TermH / 2 + 2, " Click to Begin " ) - os.pullEvent( "mouse_click" ) - end ) + term.setTextColor(colors.red) + printCentred(TermH / 2 - 1, " REDIRECTION ") + printCentred(TermH / 2 - 0, " ComputerCraft Edition ") + term.setTextColor(colors.yellow) + printCentred(TermH / 2 + 2, " Click to Begin ") + os.pullEvent("mouse_click") + end) end --Game-- if ok then - ok, err = pcall( function() + ok, err = pcall(function() local nLevel if sStartLevel then - nLevel = tonumber( sStartLevel ) + nLevel = tonumber(sStartLevel) else nLevel = 1 end @@ -656,36 +656,36 @@ if ok then reset() nLevel = startG(nLevel) end - end ) + end) end --Upsell screen-- if ok then - ok, err = pcall( function() + ok, err = pcall(function() term.setTextColor(colors.white) - term.setBackgroundColor( colors.black ) + term.setBackgroundColor(colors.black) term.clear() drawStars() - term.setTextColor( colors.red ) + term.setTextColor(colors.red) if TermW >= 40 then - printCentred( TermH / 2 - 1, " Thank you for playing Redirection " ) - printCentred( TermH / 2 - 0, " ComputerCraft Edition " ) - printCentred( TermH / 2 + 2, " Check out the full game: " ) - term.setTextColor( colors.yellow ) - printCentred( TermH / 2 + 3, " http://www.redirectiongame.com " ) + printCentred(TermH / 2 - 1, " Thank you for playing Redirection ") + printCentred(TermH / 2 - 0, " ComputerCraft Edition ") + printCentred(TermH / 2 + 2, " Check out the full game: ") + term.setTextColor(colors.yellow) + printCentred(TermH / 2 + 3, " http://www.redirectiongame.com ") else - printCentred( TermH / 2 - 2, " Thank you for " ) - printCentred( TermH / 2 - 1, " playing Redirection " ) - printCentred( TermH / 2 - 0, " ComputerCraft Edition " ) - printCentred( TermH / 2 + 2, " Check out the full game: " ) - term.setTextColor( colors.yellow ) - printCentred( TermH / 2 + 3, " www.redirectiongame.com " ) + printCentred(TermH / 2 - 2, " Thank you for ") + printCentred(TermH / 2 - 1, " playing Redirection ") + printCentred(TermH / 2 - 0, " ComputerCraft Edition ") + printCentred(TermH / 2 + 2, " Check out the full game: ") + term.setTextColor(colors.yellow) + printCentred(TermH / 2 + 3, " www.redirectiongame.com ") end parallel.waitForAll( function() sleep(2) end, - function() os.pullEvent( "mouse_click" ) end + function() os.pullEvent("mouse_click") end ) - end ) + end) end --Clear and exit-- @@ -695,9 +695,9 @@ term.setBackgroundColor(colors.black) term.clear() if not ok then if err == "Terminated" then - print( "Check out the full version of Redirection:" ) - print( "http://www.redirectiongame.com" ) + print("Check out the full version of Redirection:") + print("http://www.redirectiongame.com") else - printError( err ) + printError(err) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua index 48e06a950..0f08b59be 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/adventure.lua @@ -9,15 +9,15 @@ local tBiomes = { "in frozen tundra", } -local function hasTrees( _nBiome ) +local function hasTrees(_nBiome) return _nBiome <= 3 end -local function hasStone( _nBiome ) +local function hasStone(_nBiome) return _nBiome == 4 end -local function hasRivers( _nBiome ) +local function hasRivers(_nBiome) return _nBiome ~= 3 and _nBiome ~= 5 end @@ -338,14 +338,14 @@ local tDayCycle = { } local function getTimeOfDay() - return math.fmod( math.floor(nTurn / 3), #tDayCycle ) + 1 + return math.fmod(math.floor(nTurn / 3), #tDayCycle) + 1 end local function isSunny() return getTimeOfDay() < 10 end -local function getRoom( x, y, z, dontCreate ) +local function getRoom(x, y, z, dontCreate) tMap[x] = tMap[x] or {} tMap[x][y] = tMap[x][y] or {} if not tMap[x][y][z] and dontCreate ~= true then @@ -360,26 +360,26 @@ local function getRoom( x, y, z, dontCreate ) -- Room is above ground -- Pick biome - room.nBiome = math.random( 1, #tBiomes ) - room.trees = hasTrees( room.nBiome ) + room.nBiome = math.random(1, #tBiomes) + room.trees = hasTrees(room.nBiome) -- Add animals if math.random(1, 3) == 1 then for _ = 1, math.random(1, 2) do - local sAnimal = tAnimals[ math.random( 1, #tAnimals ) ] - room.items[ sAnimal ] = items[ sAnimal ] + local sAnimal = tAnimals[math.random(1, #tAnimals)] + room.items[sAnimal] = items[sAnimal] end end -- Add surface ore - if math.random(1, 5) == 1 or hasStone( room.nBiome ) then - room.items[ "some stone" ] = items[ "some stone" ] + if math.random(1, 5) == 1 or hasStone(room.nBiome) then + room.items["some stone"] = items["some stone"] end if math.random(1, 8) == 1 then - room.items[ "some coal" ] = items[ "some coal" ] + room.items["some coal"] = items["some coal"] end - if math.random(1, 8) == 1 and hasRivers( room.nBiome ) then - room.items[ "a river" ] = items[ "a river" ] + if math.random(1, 8) == 1 and hasRivers(room.nBiome) then + room.items["a river"] = items["a river"] end -- Add exits @@ -390,15 +390,15 @@ local function getRoom( x, y, z, dontCreate ) ["west"] = true, } if math.random(1, 8) == 1 then - room.exits["down"] = true + room.exits.down = true room.items["a cave entrance"] = items["a cave entrance"] end else -- Room is underground -- Add exits - local function tryExit( sDir, sOpp, x, y, z ) - local adj = getRoom( x, y, z, true ) + local function tryExit(sDir, sOpp, x, y, z) + local adj = getRoom(x, y, z, true) if adj then if adj.exits[sOpp] then room.exits[sDir] = true @@ -411,34 +411,34 @@ local function getRoom( x, y, z, dontCreate ) end if y == -1 then - local above = getRoom( x, y + 1, z ) - if above.exits["down"] then - room.exits["up"] = true + local above = getRoom(x, y + 1, z) + if above.exits.down then + room.exits.up = true room.items["an exit to the surface"] = items["an exit to the surface"] end else - tryExit( "up", "down", x, y + 1, z ) + tryExit("up", "down", x, y + 1, z) end if y > -3 then - tryExit( "down", "up", x, y - 1, z ) + tryExit("down", "up", x, y - 1, z) end - tryExit( "east", "west", x - 1, y, z ) - tryExit( "west", "east", x + 1, y, z ) - tryExit( "north", "south", x, y, z + 1 ) - tryExit( "south", "north", x, y, z - 1 ) + tryExit("east", "west", x - 1, y, z) + tryExit("west", "east", x + 1, y, z) + tryExit("north", "south", x, y, z + 1) + tryExit("south", "north", x, y, z - 1) -- Add ores - room.items[ "some stone" ] = items[ "some stone" ] + room.items["some stone"] = items["some stone"] if math.random(1, 3) == 1 then - room.items[ "some coal" ] = items[ "some coal" ] + room.items["some coal"] = items["some coal"] end if math.random(1, 8) == 1 then - room.items[ "some iron" ] = items[ "some iron" ] + room.items["some iron"] = items["some iron"] end if y == -3 and math.random(1, 15) == 1 then - room.items[ "some diamond" ] = items[ "some diamond" ] + room.items["some diamond"] = items["some diamond"] end -- Turn out the lights @@ -448,8 +448,8 @@ local function getRoom( x, y, z, dontCreate ) return tMap[x][y][z] end -local function itemize( t ) - local item = next( t ) +local function itemize(t) + local item = next(t) if item == nil then return "nothing" end @@ -458,9 +458,9 @@ local function itemize( t ) while item do text = text .. item - local nextItem = next( t, item ) + local nextItem = next(t, item) if nextItem ~= nil then - local nextNextItem = next( t, nextItem ) + local nextNextItem = next(t, nextItem) if nextNextItem == nil then text = text .. " and " else @@ -472,13 +472,13 @@ local function itemize( t ) return text end -local function findItem( _tList, _sQuery ) - for sItem, tItem in pairs( _tList ) do +local function findItem(_tList, _sQuery) + for sItem, tItem in pairs(_tList) do if sItem == _sQuery then return sItem end if tItem.aliases ~= nil then - for _, sAlias in pairs( tItem.aliases ) do + for _, sAlias in pairs(tItem.aliases) do if sAlias == _sQuery then return sItem end @@ -606,91 +606,91 @@ local tMatches = { } local commands = {} -local function doCommand( text ) +local function doCommand(text) if text == "" then - commands[ "noinput" ]() + commands.noinput() return end - for sCommand, t in pairs( tMatches ) do - for _, sMatch in pairs( t ) do - local tCaptures = { string.match( text, "^" .. sMatch .. "$" ) } + for sCommand, t in pairs(tMatches) do + for _, sMatch in pairs(t) do + local tCaptures = { string.match(text, "^" .. sMatch .. "$") } if #tCaptures ~= 0 then - local fnCommand = commands[ sCommand ] + local fnCommand = commands[sCommand] if #tCaptures == 1 and tCaptures[1] == sMatch then fnCommand() else - fnCommand( table.unpack( tCaptures ) ) + fnCommand(table.unpack(tCaptures)) end return end end end - commands[ "badinput" ]() + commands.badinput() end function commands.wait() - print( "Time passes..." ) + print("Time passes...") end -function commands.look( _sTarget ) - local room = getRoom( x, y, z ) +function commands.look(_sTarget) + local room = getRoom(x, y, z) if room.dark then - print( "It is pitch dark." ) + print("It is pitch dark.") return end if _sTarget == nil then -- Look at the world if y == 0 then - io.write( "You are standing " .. tBiomes[room.nBiome] .. ". " ) - print( tDayCycle[ getTimeOfDay() ] ) + io.write("You are standing " .. tBiomes[room.nBiome] .. ". ") + print(tDayCycle[getTimeOfDay()]) else - io.write( "You are underground. " ) - if next( room.exits ) ~= nil then - print( "You can travel " .. itemize( room.exits ) .. "." ) + io.write("You are underground. ") + if next(room.exits) ~= nil then + print("You can travel " .. itemize(room.exits) .. ".") else print() end end - if next( room.items ) ~= nil then - print( "There is " .. itemize( room.items ) .. " here." ) + if next(room.items) ~= nil then + print("There is " .. itemize(room.items) .. " here.") end if room.trees then - print( "There are trees here." ) + print("There are trees here.") end else -- Look at stuff if room.trees and (_sTarget == "tree" or _sTarget == "trees") then - print( "The trees look easy to break." ) + print("The trees look easy to break.") elseif _sTarget == "self" or _sTarget == "myself" then - print( "Very handsome." ) + print("Very handsome.") else local tItem = nil - local sItem = findItem( room.items, _sTarget ) + local sItem = findItem(room.items, _sTarget) if sItem then tItem = room.items[sItem] else - sItem = findItem( inventory, _sTarget ) + sItem = findItem(inventory, _sTarget) if sItem then tItem = inventory[sItem] end end if tItem then - print( tItem.desc or "You see nothing special about " .. sItem .. "." ) + print(tItem.desc or "You see nothing special about " .. sItem .. ".") else - print( "You don't see any " .. _sTarget .. " here." ) + print("You don't see any " .. _sTarget .. " here.") end end end end -function commands.go( _sDir ) - local room = getRoom( x, y, z ) +function commands.go(_sDir) + local room = getRoom(x, y, z) if _sDir == nil then - print( "Go where?" ) + print("Go where?") return end @@ -700,7 +700,7 @@ function commands.go( _sDir ) if nGoWest > #tGoWest then nGoWest = 1 end - print( tGoWest[ nGoWest ] ) + print(tGoWest[nGoWest]) else if nGoWest > 0 or nTurn > 6 then nGoWest = nil @@ -709,7 +709,7 @@ function commands.go( _sDir ) end if room.exits[_sDir] == nil then - print( "You can't go that way." ) + print("You can't go that way.") return end @@ -726,98 +726,98 @@ function commands.go( _sDir ) elseif _sDir == "down" then y = y - 1 else - print( "I don't understand that direction." ) + print("I don't understand that direction.") return end nTimeInRoom = 0 - doCommand( "look" ) + doCommand("look") end -function commands.dig( _sDir, _sTool ) - local room = getRoom( x, y, z ) +function commands.dig(_sDir, _sTool) + local room = getRoom(x, y, z) if _sDir == nil then - print( "Dig where?" ) + print("Dig where?") return end local sTool = nil local tTool = nil if _sTool ~= nil then - sTool = findItem( inventory, _sTool ) + sTool = findItem(inventory, _sTool) if not sTool then - print( "You're not carrying a " .. _sTool .. "." ) + print("You're not carrying a " .. _sTool .. ".") return end - tTool = inventory[ sTool ] + tTool = inventory[sTool] end - local bActuallyDigging = room.exits[ _sDir ] ~= true + local bActuallyDigging = room.exits[_sDir] ~= true if bActuallyDigging then if sTool == nil or tTool.toolType ~= "pick" then - print( "You need to use a pickaxe to dig through stone." ) + print("You need to use a pickaxe to dig through stone.") return end end if _sDir == "north" then - room.exits["north"] = true + room.exits.north = true z = z + 1 - getRoom( x, y, z ).exits["south"] = true + getRoom(x, y, z).exits.south = true elseif _sDir == "south" then - room.exits["south"] = true + room.exits.south = true z = z - 1 - getRoom( x, y, z ).exits["north"] = true + getRoom(x, y, z).exits.north = true elseif _sDir == "east" then - room.exits["east"] = true + room.exits.east = true x = x - 1 - getRoom( x, y, z ).exits["west"] = true + getRoom(x, y, z).exits.west = true elseif _sDir == "west" then - room.exits["west"] = true + room.exits.west = true x = x + 1 - getRoom( x, y, z ).exits["east"] = true + getRoom(x, y, z).exits.east = true elseif _sDir == "up" then if y == 0 then - print( "You can't dig that way." ) + print("You can't dig that way.") return end - room.exits["up"] = true + room.exits.up = true if y == -1 then - room.items[ "an exit to the surface" ] = items[ "an exit to the surface" ] + room.items["an exit to the surface"] = items["an exit to the surface"] end y = y + 1 - room = getRoom( x, y, z ) - room.exits["down"] = true + room = getRoom(x, y, z) + room.exits.down = true if y == 0 then - room.items[ "a cave entrance" ] = items[ "a cave entrance" ] + room.items["a cave entrance"] = items["a cave entrance"] end elseif _sDir == "down" then if y <= -3 then - print( "You hit bedrock." ) + print("You hit bedrock.") return end - room.exits["down"] = true + room.exits.down = true if y == 0 then - room.items[ "a cave entrance" ] = items[ "a cave entrance" ] + room.items["a cave entrance"] = items["a cave entrance"] end y = y - 1 - room = getRoom( x, y, z ) - room.exits["up"] = true + room = getRoom(x, y, z) + room.exits.up = true if y == -1 then - room.items[ "an exit to the surface" ] = items[ "an exit to the surface" ] + room.items["an exit to the surface"] = items["an exit to the surface"] end else - print( "I don't understand that direction." ) + print("I don't understand that direction.") return end @@ -825,179 +825,179 @@ function commands.dig( _sDir, _sTool ) if bActuallyDigging then if _sDir == "down" and y == -1 or _sDir == "up" and y == 0 then - inventory[ "some dirt" ] = items[ "some dirt" ] - inventory[ "some stone" ] = items[ "some stone" ] - print( "You dig " .. _sDir .. " using " .. sTool .. " and collect some dirt and stone." ) + inventory["some dirt"] = items["some dirt"] + inventory["some stone"] = items["some stone"] + print("You dig " .. _sDir .. " using " .. sTool .. " and collect some dirt and stone.") else - inventory[ "some stone" ] = items[ "some stone" ] - print( "You dig " .. _sDir .. " using " .. sTool .. " and collect some stone." ) + inventory["some stone"] = items["some stone"] + print("You dig " .. _sDir .. " using " .. sTool .. " and collect some stone.") end end nTimeInRoom = 0 - doCommand( "look" ) + doCommand("look") end function commands.inventory() - print( "You are carrying " .. itemize( inventory ) .. "." ) + print("You are carrying " .. itemize(inventory) .. ".") end -function commands.drop( _sItem ) +function commands.drop(_sItem) if _sItem == nil then - print( "Drop what?" ) + print("Drop what?") return end - local room = getRoom( x, y, z ) - local sItem = findItem( inventory, _sItem ) + local room = getRoom(x, y, z) + local sItem = findItem(inventory, _sItem) if sItem then - local tItem = inventory[ sItem ] + local tItem = inventory[sItem] if tItem.droppable == false then - print( "You can't drop that." ) + print("You can't drop that.") else - room.items[ sItem ] = tItem - inventory[ sItem ] = nil - print( "Dropped." ) + room.items[sItem] = tItem + inventory[sItem] = nil + print("Dropped.") end else - print( "You don't have a " .. _sItem .. "." ) + print("You don't have a " .. _sItem .. ".") end end -function commands.place( _sItem ) +function commands.place(_sItem) if _sItem == nil then - print( "Place what?" ) + print("Place what?") return end if _sItem == "torch" or _sItem == "a torch" then - local room = getRoom( x, y, z ) + local room = getRoom(x, y, z) if inventory["some torches"] or inventory["a torch"] then inventory["a torch"] = nil room.items["a torch"] = items["a torch"] if room.dark then - print( "The cave lights up under the torchflame." ) + print("The cave lights up under the torchflame.") room.dark = false elseif y == 0 and not isSunny() then - print( "The night gets a little brighter." ) + print("The night gets a little brighter.") else - print( "Placed." ) + print("Placed.") end else - print( "You don't have torches." ) + print("You don't have torches.") end return end - commands.drop( _sItem ) + commands.drop(_sItem) end -function commands.take( _sItem ) +function commands.take(_sItem) if _sItem == nil then - print( "Take what?" ) + print("Take what?") return end - local room = getRoom( x, y, z ) - local sItem = findItem( room.items, _sItem ) + local room = getRoom(x, y, z) + local sItem = findItem(room.items, _sItem) if sItem then - local tItem = room.items[ sItem ] + local tItem = room.items[sItem] if tItem.heavy == true then - print( "You can't carry " .. sItem .. "." ) + print("You can't carry " .. sItem .. ".") elseif tItem.ore == true then - print( "You need to mine this ore." ) + print("You need to mine this ore.") else if tItem.infinite ~= true then - room.items[ sItem ] = nil + room.items[sItem] = nil end - inventory[ sItem ] = tItem + inventory[sItem] = tItem if inventory["some torches"] and inventory["a torch"] then inventory["a torch"] = nil end if sItem == "a torch" and y < 0 then room.dark = true - print( "The cave plunges into darkness." ) + print("The cave plunges into darkness.") else - print( "Taken." ) + print("Taken.") end end else - print( "You don't see a " .. _sItem .. " here." ) + print("You don't see a " .. _sItem .. " here.") end end -function commands.mine( _sItem, _sTool ) +function commands.mine(_sItem, _sTool) if _sItem == nil then - print( "Mine what?" ) + print("Mine what?") return end if _sTool == nil then - print( "Mine " .. _sItem .. " with what?" ) + print("Mine " .. _sItem .. " with what?") return end - commands.cbreak( _sItem, _sTool ) + commands.cbreak(_sItem, _sTool) end -function commands.attack( _sItem, _sTool ) +function commands.attack(_sItem, _sTool) if _sItem == nil then - print( "Attack what?" ) + print("Attack what?") return end - commands.cbreak( _sItem, _sTool ) + commands.cbreak(_sItem, _sTool) end -function commands.cbreak( _sItem, _sTool ) +function commands.cbreak(_sItem, _sTool) if _sItem == nil then - print( "Break what?" ) + print("Break what?") return end local sTool = nil if _sTool ~= nil then - sTool = findItem( inventory, _sTool ) + sTool = findItem(inventory, _sTool) if sTool == nil then - print( "You're not carrying a " .. _sTool .. "." ) + print("You're not carrying a " .. _sTool .. ".") return end end - local room = getRoom( x, y, z ) + local room = getRoom(x, y, z) if _sItem == "tree" or _sItem == "trees" or _sItem == "a tree" then - print( "The tree breaks into blocks of wood, which you pick up." ) - inventory[ "some wood" ] = items[ "some wood" ] + print("The tree breaks into blocks of wood, which you pick up.") + inventory["some wood"] = items["some wood"] return elseif _sItem == "self" or _sItem == "myself" then if term.isColour() then - term.setTextColour( colours.red ) + term.setTextColour(colours.red) end - print( "You have died." ) - print( "Score: &e0" ) - term.setTextColour( colours.white ) + print("You have died.") + print("Score: &e0") + term.setTextColour(colours.white) bRunning = false return end - local sItem = findItem( room.items, _sItem ) + local sItem = findItem(room.items, _sItem) if sItem then - local tItem = room.items[ sItem ] + local tItem = room.items[sItem] if tItem.ore == true then -- Breaking ore if not sTool then - print( "You need a tool to break this ore." ) + print("You need a tool to break this ore.") return end - local tTool = inventory[ sTool ] + local tTool = inventory[sTool] if tTool.tool then if tTool.toolLevel < tItem.toolLevel then - print( sTool .. " is not strong enough to break this ore." ) + print(sTool .. " is not strong enough to break this ore.") elseif tTool.toolType ~= tItem.toolType then - print( "You need a different kind of tool to break this ore." ) + print("You need a different kind of tool to break this ore.") else - print( "The ore breaks, dropping " .. sItem .. ", which you pick up." ) - inventory[ sItem ] = items[ sItem ] + print("The ore breaks, dropping " .. sItem .. ", which you pick up.") + inventory[sItem] = items[sItem] if tItem.infinite ~= true then - room.items[ sItem ] = nil + room.items[sItem] = nil end end else @@ -1009,21 +1009,21 @@ function commands.cbreak( _sItem, _sTool ) local toolLevel = 0 local tTool = nil if sTool then - tTool = inventory[ sTool ] + tTool = inventory[sTool] if tTool.toolType == "sword" then toolLevel = tTool.toolLevel end end local tChances = { 0.2, 0.4, 0.55, 0.8, 1 } - if math.random() <= tChances[ toolLevel + 1 ] then - room.items[ sItem ] = nil - print( "The " .. tItem.aliases[1] .. " dies." ) + if math.random() <= tChances[toolLevel + 1] then + room.items[sItem] = nil + print("The " .. tItem.aliases[1] .. " dies.") if tItem.drops then - for _, sDrop in pairs( tItem.drops ) do + for _, sDrop in pairs(tItem.drops) do if not room.items[sDrop] then - print( "The " .. tItem.aliases[1] .. " dropped " .. sDrop .. "." ) + print("The " .. tItem.aliases[1] .. " dropped " .. sDrop .. ".") room.items[sDrop] = items[sDrop] end end @@ -1033,112 +1033,112 @@ function commands.cbreak( _sItem, _sTool ) room.nMonsters = room.nMonsters - 1 end else - print( "The " .. tItem.aliases[1] .. " is injured by your blow." ) + print("The " .. tItem.aliases[1] .. " is injured by your blow.") end if tItem.hitDrops then - for _, sDrop in pairs( tItem.hitDrops ) do + for _, sDrop in pairs(tItem.hitDrops) do if not room.items[sDrop] then - print( "The " .. tItem.aliases[1] .. " dropped " .. sDrop .. "." ) + print("The " .. tItem.aliases[1] .. " dropped " .. sDrop .. ".") room.items[sDrop] = items[sDrop] end end end else - print( "You can't break " .. sItem .. "." ) + print("You can't break " .. sItem .. ".") end else - print( "You don't see a " .. _sItem .. " here." ) + print("You don't see a " .. _sItem .. " here.") end end -function commands.craft( _sItem ) +function commands.craft(_sItem) if _sItem == nil then - print( "Craft what?" ) + print("Craft what?") return end if _sItem == "computer" or _sItem == "a computer" then - print( "By creating a computer in a computer in a computer, you tear a hole in the spacetime continuum from which no mortal being can escape." ) + print("By creating a computer in a computer in a computer, you tear a hole in the spacetime continuum from which no mortal being can escape.") if term.isColour() then - term.setTextColour( colours.red ) + term.setTextColour(colours.red) end - print( "You have died." ) - print( "Score: &e0" ) - term.setTextColour( colours.white ) + print("You have died.") + print("Score: &e0") + term.setTextColour(colours.white) bRunning = false return end - local sItem = findItem( items, _sItem ) - local tRecipe = sItem and tRecipes[ sItem ] or nil + local sItem = findItem(items, _sItem) + local tRecipe = sItem and tRecipes[sItem] or nil if tRecipe then - for _, sReq in ipairs( tRecipe ) do + for _, sReq in ipairs(tRecipe) do if inventory[sReq] == nil then - print( "You don't have the items you need to craft " .. sItem .. "." ) + print("You don't have the items you need to craft " .. sItem .. ".") return end end - for _, sReq in ipairs( tRecipe ) do + for _, sReq in ipairs(tRecipe) do inventory[sReq] = nil end - inventory[ sItem ] = items[ sItem ] + inventory[sItem] = items[sItem] if inventory["some torches"] and inventory["a torch"] then inventory["a torch"] = nil end - print( "Crafted." ) + print("Crafted.") else - print( "You don't know how to make " .. (sItem or _sItem) .. "." ) + print("You don't know how to make " .. (sItem or _sItem) .. ".") end end -function commands.build( _sThing, _sMaterial ) +function commands.build(_sThing, _sMaterial) if _sThing == nil then - print( "Build what?" ) + print("Build what?") return end local sMaterial = nil if _sMaterial == nil then - for sItem, tItem in pairs( inventory ) do + for sItem, tItem in pairs(inventory) do if tItem.material then sMaterial = sItem break end end if sMaterial == nil then - print( "You don't have any building materials." ) + print("You don't have any building materials.") return end else - sMaterial = findItem( inventory, _sMaterial ) + sMaterial = findItem(inventory, _sMaterial) if not sMaterial then - print( "You don't have any " .. _sMaterial ) + print("You don't have any " .. _sMaterial) return end if inventory[sMaterial].material ~= true then - print( sMaterial .. " is not a good building material." ) + print(sMaterial .. " is not a good building material.") return end end local alias = nil if string.sub(_sThing, 1, 1) == "a" then - alias = string.match( _sThing, "a ([%a ]+)" ) + alias = string.match(_sThing, "a ([%a ]+)") end - local room = getRoom( x, y, z ) + local room = getRoom(x, y, z) inventory[sMaterial] = nil - room.items[ _sThing ] = { + room.items[_sThing] = { heavy = true, aliases = { alias }, desc = "As you look at your creation (made from " .. sMaterial .. "), you feel a swelling sense of pride.", } - print( "Your construction is complete." ) + print("Your construction is complete.") end function commands.help() @@ -1147,32 +1147,32 @@ function commands.help() "To get around the world, type actions, and the adventure will " .. "be read back to you. The actions availiable to you are go, look, inspect, inventory, " .. "take, drop, place, punch, attack, mine, dig, craft, build, eat and exit." - print( sText ) + print(sText) end -function commands.eat( _sItem ) +function commands.eat(_sItem) if _sItem == nil then - print( "Eat what?" ) + print("Eat what?") return end - local sItem = findItem( inventory, _sItem ) + local sItem = findItem(inventory, _sItem) if not sItem then - print( "You don't have any " .. _sItem .. "." ) + print("You don't have any " .. _sItem .. ".") return end local tItem = inventory[sItem] if tItem.food then - print( "That was delicious!" ) + print("That was delicious!") inventory[sItem] = nil if bInjured then - print( "You are no longer injured." ) + print("You are no longer injured.") bInjured = false end else - print( "You can't eat " .. sItem .. "." ) + print("You can't eat " .. sItem .. ".") end end @@ -1195,7 +1195,7 @@ function commands.badinput() "That doesn't make any sense.", "What?", } - print( tResponses[ math.random(1, #tResponses) ] ) + print(tResponses[math.random(1, #tResponses)]) end function commands.noinput() @@ -1206,7 +1206,7 @@ function commands.noinput() "Don't be shy.", "Use your words.", } - print( tResponses[ math.random(1, #tResponses) ] ) + print(tResponses[math.random(1, #tResponses)]) end local function simulate() @@ -1218,20 +1218,20 @@ local function simulate() for sz = -2, 2 do local h = y + sy if h >= -3 and h <= 0 then - local room = getRoom( x + sx, h, z + sz ) + local room = getRoom(x + sx, h, z + sz) -- Spawn monsters if room.nMonsters < 2 and (h == 0 and not isSunny() and not room.items["a torch"] or room.dark) and math.random(1, 6) == 1 then - local sMonster = tMonsters[ math.random(1, #tMonsters) ] - if room.items[ sMonster ] == nil then - room.items[ sMonster ] = items[ sMonster ] + local sMonster = tMonsters[math.random(1, #tMonsters)] + if room.items[sMonster] == nil then + room.items[sMonster] = items[sMonster] room.nMonsters = room.nMonsters + 1 if sx == 0 and sy == 0 and sz == 0 and not room.dark then - print( "From the shadows, " .. sMonster .. " appears." ) + print("From the shadows, " .. sMonster .. " appears.") bNewMonstersThisRoom = true end end @@ -1239,11 +1239,11 @@ local function simulate() -- Burn monsters if h == 0 and isSunny() then - for _, sMonster in ipairs( tMonsters ) do + for _, sMonster in ipairs(tMonsters) do if room.items[sMonster] and items[sMonster].nocturnal then room.items[sMonster] = nil if sx == 0 and sy == 0 and sz == 0 and not room.dark then - print( "With the sun high in the sky, the " .. items[sMonster].aliases[1] .. " bursts into flame and dies." ) + print("With the sun high in the sky, the " .. items[sMonster].aliases[1] .. " bursts into flame and dies.") end room.nMonsters = room.nMonsters - 1 end @@ -1255,35 +1255,35 @@ local function simulate() end -- Make monsters attack - local room = getRoom( x, y, z ) + local room = getRoom(x, y, z) if nTimeInRoom >= 2 and not bNewMonstersThisRoom then - for _, sMonster in ipairs( tMonsters ) do + for _, sMonster in ipairs(tMonsters) do if room.items[sMonster] then if math.random(1, 4) == 1 and not (y == 0 and isSunny() and sMonster == "a spider") then if sMonster == "a creeper" then if room.dark then - print( "A creeper explodes." ) + print("A creeper explodes.") else - print( "The creeper explodes." ) + print("The creeper explodes.") end room.items[sMonster] = nil room.nMonsters = room.nMonsters - 1 else if room.dark then - print( "A " .. items[sMonster].aliases[1] .. " attacks you." ) + print("A " .. items[sMonster].aliases[1] .. " attacks you.") else - print( "The " .. items[sMonster].aliases[1] .. " attacks you." ) + print("The " .. items[sMonster].aliases[1] .. " attacks you.") end end if bInjured then if term.isColour() then - term.setTextColour( colours.red ) + term.setTextColour(colours.red) end - print( "You have died." ) - print( "Score: &e0" ) - term.setTextColour( colours.white ) + print("You have died.") + print("Score: &e0") + term.setTextColour(colours.white) bRunning = false return else @@ -1299,10 +1299,10 @@ local function simulate() -- Always print this if bInjured then if term.isColour() then - term.setTextColour( colours.red ) + term.setTextColour(colours.red) end - print( "You are injured." ) - term.setTextColour( colours.white ) + print("You are injured.") + term.setTextColour(colours.white) end -- Advance time @@ -1310,19 +1310,19 @@ local function simulate() nTimeInRoom = nTimeInRoom + 1 end -doCommand( "look" ) +doCommand("look") simulate() local tCommandHistory = {} while bRunning do if term.isColour() then - term.setTextColour( colours.yellow ) + term.setTextColour(colours.yellow) end - write( "? " ) - term.setTextColour( colours.white ) + write("? ") + term.setTextColour(colours.white) - local sRawLine = read( nil, tCommandHistory ) - table.insert( tCommandHistory, sRawLine ) + local sRawLine = read(nil, tCommandHistory) + table.insert(tCommandHistory, sRawLine) local sLine = nil for match in string.gmatch(sRawLine, "%a+") do @@ -1333,7 +1333,7 @@ while bRunning do end end - doCommand( sLine or "" ) + doCommand(sLine or "") if bRunning then simulate() end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua index 5ec03d2ce..322b4a887 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/dj.lua @@ -1,10 +1,10 @@ local tArgs = { ... } local function printUsage() - print( "Usages:" ) - print( "dj play" ) - print( "dj play " ) - print( "dj stop" ) + print("Usages:") + print("dj play") + print("dj play ") + print("dj stop") end if #tArgs > 2 then @@ -23,24 +23,24 @@ elseif sCommand == "play" or sCommand == nil then if sName == nil then -- No disc specified, pick one at random local tNames = {} - for _, sName in ipairs( peripheral.getNames() ) do - if disk.isPresent( sName ) and disk.hasAudio( sName ) then - table.insert( tNames, sName ) + for _, sName in ipairs(peripheral.getNames()) do + if disk.isPresent(sName) and disk.hasAudio(sName) then + table.insert(tNames, sName) end end if #tNames == 0 then - print( "No Music Discs in attached disk drives" ) + print("No Music Discs in attached disk drives") return end - sName = tNames[ math.random(1, #tNames) ] + sName = tNames[math.random(1, #tNames)] end -- Play the disc - if disk.isPresent( sName ) and disk.hasAudio( sName ) then - print( "Playing " .. disk.getAudioTitle( sName ) ) - disk.playAudio( sName ) + if disk.isPresent(sName) and disk.hasAudio(sName) then + print("Playing " .. disk.getAudioTitle(sName)) + disk.playAudio(sName) else - print( "No Music Disc in disk drive: " .. sName ) + print("No Music Disc in disk drive: " .. sName) return end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/hello.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/hello.lua index d22399410..0afdf9e77 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/hello.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/hello.lua @@ -1,5 +1,5 @@ if term.isColour() then - term.setTextColour( 2 ^ math.random(0, 15) ) + term.setTextColour(2 ^ math.random(0, 15)) end -textutils.slowPrint( "Hello World!" ) -term.setTextColour( colours.white ) +textutils.slowPrint("Hello World!") +term.setTextColour(colours.white) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua b/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua index e02654d1f..fe753b032 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/fun/worm.lua @@ -15,11 +15,11 @@ else fruitColour = colours.white end -local function printCentred( y, s ) +local function printCentred(y, s) local x = math.floor((w - #s) / 2) term.setCursorPos(x, y) --term.clearLine() - term.write( s ) + term.write(s) end local xVel, yVel = 1, 0 @@ -65,9 +65,9 @@ local function addFruit() if fruit.snake == nil and fruit.wall == nil and fruit.fruit == nil then screen[x][y] = { fruit = true } term.setCursorPos(x, y) - term.setBackgroundColour( fruitColour ) + term.setBackgroundColour(fruitColour) term.write(" ") - term.setBackgroundColour( colours.black ) + term.setBackgroundColour(colours.black) break end end @@ -79,23 +79,23 @@ local function addFruit() end local function drawMenu() - term.setTextColour( headingColour ) + term.setTextColour(headingColour) term.setCursorPos(1, 1) - term.write( "SCORE " ) + term.write("SCORE ") - term.setTextColour( textColour ) + term.setTextColour(textColour) term.setCursorPos(7, 1) - term.write( tostring(nScore) ) + term.write(tostring(nScore)) - term.setTextColour( headingColour ) + term.setTextColour(headingColour) term.setCursorPos(w - 11, 1) - term.write( "DIFFICULTY " ) + term.write("DIFFICULTY ") - term.setTextColour( textColour ) + term.setTextColour(textColour) term.setCursorPos(w, 1) - term.write( tostring(nDifficulty or "?") ) + term.write(tostring(nDifficulty or "?")) - term.setTextColour( colours.white ) + term.setTextColour(colours.white) end local function update( ) @@ -150,9 +150,9 @@ local function update( ) end term.setCursorPos(xPos, yPos) - term.setBackgroundColour( wormColour ) + term.setBackgroundColour(wormColour) term.write(" ") - term.setBackgroundColour( colours.black ) + term.setBackgroundColour(colours.black) drawMenu() end @@ -163,29 +163,29 @@ local function drawFrontend() --term.setTextColour( titleColour ) --printCentred( math.floor(h/2) - 4, " W O R M " ) - term.setTextColour( headingColour ) - printCentred( math.floor(h / 2) - 3, "" ) - printCentred( math.floor(h / 2) - 2, " SELECT DIFFICULTY " ) - printCentred( math.floor(h / 2) - 1, "" ) + term.setTextColour(headingColour) + printCentred(math.floor(h / 2) - 3, "") + printCentred(math.floor(h / 2) - 2, " SELECT DIFFICULTY ") + printCentred(math.floor(h / 2) - 1, "") - printCentred( math.floor(h / 2) + 0, " " ) - printCentred( math.floor(h / 2) + 1, " " ) - printCentred( math.floor(h / 2) + 2, " " ) - printCentred( math.floor(h / 2) - 1 + nDifficulty, " [ ] " ) + printCentred(math.floor(h / 2) + 0, " ") + printCentred(math.floor(h / 2) + 1, " ") + printCentred(math.floor(h / 2) + 2, " ") + printCentred(math.floor(h / 2) - 1 + nDifficulty, " [ ] ") - term.setTextColour( textColour ) - printCentred( math.floor(h / 2) + 0, "EASY" ) - printCentred( math.floor(h / 2) + 1, "MEDIUM" ) - printCentred( math.floor(h / 2) + 2, "HARD" ) - printCentred( math.floor(h / 2) + 3, "" ) + term.setTextColour(textColour) + printCentred(math.floor(h / 2) + 0, "EASY") + printCentred(math.floor(h / 2) + 1, "MEDIUM") + printCentred(math.floor(h / 2) + 2, "HARD") + printCentred(math.floor(h / 2) + 3, "") - term.setTextColour( colours.white ) + term.setTextColour(colours.white) end drawMenu() drawFrontend() while true do - local _, key = os.pullEvent( "key" ) + local _, key = os.pullEvent("key") if key == keys.up or key == keys.w then -- Up if nDifficulty > 1 then @@ -226,7 +226,7 @@ while bRunning do local event, p1 = os.pullEvent() if event == "timer" and p1 == timer then timer = os.startTimer(nInterval) - update( false ) + update(false) elseif event == "key" then local key = p1 @@ -257,24 +257,24 @@ while bRunning do end -- Display the gameover screen -term.setTextColour( headingColour ) -printCentred( math.floor(h / 2) - 2, " " ) -printCentred( math.floor(h / 2) - 1, " G A M E O V E R " ) +term.setTextColour(headingColour) +printCentred(math.floor(h / 2) - 2, " ") +printCentred(math.floor(h / 2) - 1, " G A M E O V E R ") -term.setTextColour( textColour ) -printCentred( math.floor(h / 2) + 0, " " ) -printCentred( math.floor(h / 2) + 1, " FINAL SCORE " .. nScore .. " " ) -printCentred( math.floor(h / 2) + 2, " " ) -term.setTextColour( colours.white ) +term.setTextColour(textColour) +printCentred(math.floor(h / 2) + 0, " ") +printCentred(math.floor(h / 2) + 1, " FINAL SCORE " .. nScore .. " ") +printCentred(math.floor(h / 2) + 2, " ") +term.setTextColour(colours.white) local timer = os.startTimer(2.5) repeat local e, p = os.pullEvent() if e == "timer" and p == timer then - term.setTextColour( textColour ) - printCentred( math.floor(h / 2) + 2, " PRESS ANY KEY " ) - printCentred( math.floor(h / 2) + 3, " " ) - term.setTextColour( colours.white ) + term.setTextColour(textColour) + printCentred(math.floor(h / 2) + 2, " PRESS ANY KEY ") + printCentred(math.floor(h / 2) + 3, " ") + term.setTextColour(colours.white) end until e == "char" diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/gps.lua b/src/main/resources/assets/computercraft/lua/rom/programs/gps.lua index c5556c079..9e1ff0538 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/gps.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/gps.lua @@ -1,9 +1,9 @@ local function printUsage() - print( "Usages:" ) - print( "gps host" ) - print( "gps host " ) - print( "gps locate" ) + print("Usages:") + print("gps host") + print("gps host ") + print("gps locate") end local tArgs = { ... } @@ -16,27 +16,27 @@ end if sCommand == "locate" then -- "gps locate" -- Just locate this computer (this will print the results) - gps.locate( 2, true ) + gps.locate(2, true) elseif sCommand == "host" then -- "gps host" -- Act as a GPS host if pocket then - print( "GPS Hosts must be stationary" ) + print("GPS Hosts must be stationary") return end -- Find a modem local sModemSide = nil - for _, sSide in ipairs( rs.getSides() ) do - if peripheral.getType( sSide ) == "modem" and peripheral.call( sSide, "isWireless" ) then + for _, sSide in ipairs(rs.getSides()) do + if peripheral.getType(sSide) == "modem" and peripheral.call(sSide, "isWireless") then sModemSide = sSide break end end if sModemSide == nil then - print( "No wireless modems found. 1 required." ) + print("No wireless modems found. 1 required.") return end @@ -51,31 +51,31 @@ elseif sCommand == "host" then printUsage() return end - print( "Position is " .. x .. "," .. y .. "," .. z ) + print("Position is " .. x .. "," .. y .. "," .. z) else -- Position is to be determined using locate - x, y, z = gps.locate( 2, true ) + x, y, z = gps.locate(2, true) if x == nil then - print( "Run \"gps host \" to set position manually" ) + print("Run \"gps host \" to set position manually") return end end -- Open a channel - local modem = peripheral.wrap( sModemSide ) - print( "Opening channel on modem " .. sModemSide ) - modem.open( gps.CHANNEL_GPS ) + local modem = peripheral.wrap(sModemSide) + print("Opening channel on modem " .. sModemSide) + modem.open(gps.CHANNEL_GPS) -- Serve requests indefinately local nServed = 0 while true do - local e, p1, p2, p3, p4, p5 = os.pullEvent( "modem_message" ) + local e, p1, p2, p3, p4, p5 = os.pullEvent("modem_message") if e == "modem_message" then -- We received a message from a modem local sSide, sChannel, sReplyChannel, sMessage, nDistance = p1, p2, p3, p4, p5 if sSide == sModemSide and sChannel == gps.CHANNEL_GPS and sMessage == "PING" and nDistance then -- We received a ping message on the GPS channel, send a response - modem.transmit( sReplyChannel, gps.CHANNEL_GPS, { x, y, z } ) + modem.transmit(sReplyChannel, gps.CHANNEL_GPS, { x, y, z }) -- Print the number of requests handled nServed = nServed + 1 @@ -83,7 +83,7 @@ elseif sCommand == "host" then local _, y = term.getCursorPos() term.setCursorPos(1, y - 1) end - print( nServed .. " GPS requests served" ) + print(nServed .. " GPS requests served") end end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/help.lua b/src/main/resources/assets/computercraft/lua/rom/programs/help.lua index c8653616a..63ece3ab4 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/help.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/help.lua @@ -7,20 +7,20 @@ else end if sTopic == "index" then - print( "Help topics available:" ) + print("Help topics available:") local tTopics = help.topics() - textutils.pagedTabulate( tTopics ) + textutils.pagedTabulate(tTopics) return end -local sFile = help.lookup( sTopic ) -local file = sFile ~= nil and io.open( sFile ) or nil +local sFile = help.lookup(sTopic) +local file = sFile ~= nil and io.open(sFile) or nil if file then local sContents = file:read("*a") file:close() local _, nHeight = term.getSize() - textutils.pagedPrint( sContents, nHeight - 3 ) + textutils.pagedPrint(sContents, nHeight - 3) else - print( "No help available" ) + print("No help available") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua b/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua index 49266c20e..a45ea1a2a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/http/pastebin.lua @@ -1,9 +1,9 @@ local function printUsage() - print( "Usages:" ) - print( "pastebin put " ) - print( "pastebin get " ) - print( "pastebin run " ) + print("Usages:") + print("pastebin put ") + print("pastebin get ") + print("pastebin run ") end local tArgs = { ... } @@ -13,8 +13,8 @@ if #tArgs < 2 then end if not http then - printError( "Pastebin requires the http API" ) - printError( "Set http.enabled to true in CC: Tweaked's config" ) + printError("Pastebin requires the http API") + printError("Set http.enabled to true in CC: Tweaked's config") return end @@ -29,7 +29,7 @@ local function extractId(paste) } for i = 1, #patterns do - local code = paste:match( patterns[i] ) + local code = paste:match(patterns[i]) if code then return code end end @@ -37,36 +37,36 @@ local function extractId(paste) end local function get(url) - local paste = extractId( url ) + local paste = extractId(url) if not paste then - io.stderr:write( "Invalid pastebin code.\n" ) - io.write( "The code is the ID at the end of the pastebin.com URL.\n" ) + io.stderr:write("Invalid pastebin code.\n") + io.write("The code is the ID at the end of the pastebin.com URL.\n") return end - write( "Connecting to pastebin.com... " ) + write("Connecting to pastebin.com... ") -- Add a cache buster so that spam protection is re-checked local cacheBuster = ("%x"):format(math.random(0, 2 ^ 30)) local response, err = http.get( - "https://pastebin.com/raw/" .. textutils.urlEncode( paste ) .. "?cb=" .. cacheBuster + "https://pastebin.com/raw/" .. textutils.urlEncode(paste) .. "?cb=" .. cacheBuster ) if response then -- If spam protection is activated, we get redirected to /paste with Content-Type: text/html local headers = response.getResponseHeaders() - if not headers["Content-Type"] or not headers["Content-Type"]:find( "^text/plain" ) then - io.stderr:write( "Failed.\n" ) - print( "Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode( paste ) ) + if not headers["Content-Type"] or not headers["Content-Type"]:find("^text/plain") then + io.stderr:write("Failed.\n") + print("Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode(paste)) return end - print( "Success." ) + print("Success.") local sResponse = response.readAll() response.close() return sResponse else - io.stderr:write( "Failed.\n" ) + io.stderr:write("Failed.\n") print(err) end end @@ -76,20 +76,20 @@ if sCommand == "put" then -- Upload a file to pastebin.com -- Determine file to upload local sFile = tArgs[2] - local sPath = shell.resolve( sFile ) - if not fs.exists( sPath ) or fs.isDir( sPath ) then - print( "No such file" ) + local sPath = shell.resolve(sFile) + if not fs.exists(sPath) or fs.isDir(sPath) then + print("No such file") return end -- Read in the file - local sName = fs.getName( sPath ) - local file = fs.open( sPath, "r" ) + local sName = fs.getName(sPath) + local file = fs.open(sPath, "r") local sText = file.readAll() file.close() -- POST the contents to pastebin - write( "Connecting to pastebin.com... " ) + write("Connecting to pastebin.com... ") local key = "0ec2eb25b6166c0c27a394ae118ad829" local response = http.post( "https://pastebin.com/api/api_post.php", @@ -101,17 +101,17 @@ if sCommand == "put" then ) if response then - print( "Success." ) + print("Success.") local sResponse = response.readAll() response.close() - local sCode = string.match( sResponse, "[^/]+$" ) - print( "Uploaded as " .. sResponse ) - print( "Run \"pastebin get " .. sCode .. "\" to download anywhere" ) + local sCode = string.match(sResponse, "[^/]+$") + print("Uploaded as " .. sResponse) + print("Run \"pastebin get " .. sCode .. "\" to download anywhere") else - print( "Failed." ) + print("Failed.") end elseif sCommand == "get" then @@ -124,20 +124,20 @@ elseif sCommand == "get" then -- Determine file to download local sCode = tArgs[2] local sFile = tArgs[3] - local sPath = shell.resolve( sFile ) - if fs.exists( sPath ) then - print( "File already exists" ) + local sPath = shell.resolve(sFile) + if fs.exists(sPath) then + print("File already exists") return end -- GET the contents from pastebin local res = get(sCode) if res then - local file = fs.open( sPath, "w" ) - file.write( res ) + local file = fs.open(sPath, "w") + file.write(res) file.close() - print( "Downloaded as " .. sFile ) + print("Downloaded as " .. sFile) end elseif sCommand == "run" then local sCode = tArgs[2] @@ -146,12 +146,12 @@ elseif sCommand == "run" then if res then local func, err = load(res, sCode, "t", _ENV) if not func then - printError( err ) + printError(err) return end local success, msg = pcall(func, select(3, ...)) if not success then - printError( msg ) + printError(msg) end end else diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua b/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua index 6c5894e77..dbfa3aeb7 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/http/wget.lua @@ -1,15 +1,15 @@ local function printUsage() - print( "Usage:" ) - print( "wget [filename]" ) - print( "wget run " ) + print("Usage:") + print("wget [filename]") + print("wget run ") end local tArgs = { ... } local run = false if tArgs[1] == "run" then - table.remove( tArgs, 1 ) + table.remove(tArgs, 1) run = true end @@ -18,36 +18,36 @@ if #tArgs < 1 then return end -local url = table.remove( tArgs, 1 ) +local url = table.remove(tArgs, 1) if not http then - printError( "wget requires the http API" ) - printError( "Set http.enabled to true in CC: Tweaked's config" ) + printError("wget requires the http API") + printError("Set http.enabled to true in CC: Tweaked's config") return end -local function getFilename( sUrl ) - sUrl = sUrl:gsub( "[#?].*" , "" ):gsub( "/+$" , "" ) - return sUrl:match( "/([^/]+)$" ) +local function getFilename(sUrl) + sUrl = sUrl:gsub("[#?].*" , ""):gsub("/+$" , "") + return sUrl:match("/([^/]+)$") end -local function get( sUrl ) +local function get(sUrl) -- Check if the URL is valid - local ok, err = http.checkURL( url ) + local ok, err = http.checkURL(url) if not ok then - printError( err or "Invalid URL." ) + printError(err or "Invalid URL.") return end - write( "Connecting to " .. sUrl .. "... " ) + write("Connecting to " .. sUrl .. "... ") - local response = http.get( sUrl , nil , true ) + local response = http.get(sUrl , nil , true) if not response then - print( "Failed." ) + print("Failed.") return nil end - print( "Success." ) + print("Success.") local sResponse = response.readAll() response.close() @@ -66,22 +66,22 @@ if run then local ok, err = pcall(func, table.unpack(tArgs)) if not ok then - printError( err ) + printError(err) end else - local sFile = tArgs[1] or getFilename( url ) - local sPath = shell.resolve( sFile ) - if fs.exists( sPath ) then - print( "File already exists" ) + local sFile = tArgs[1] or getFilename(url) + local sPath = shell.resolve(sFile) + if fs.exists(sPath) then + print("File already exists") return end local res = get(url) if not res then return end - local file = fs.open( sPath, "wb" ) - file.write( res ) + local file = fs.open(sPath, "wb") + file.write(res) file.close() - print( "Downloaded as " .. sFile ) + print("Downloaded as " .. sFile) end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/id.lua b/src/main/resources/assets/computercraft/lua/rom/programs/id.lua index 439fc9f3c..dda736612 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/id.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/id.lua @@ -2,28 +2,28 @@ local sDrive = nil local tArgs = { ... } if #tArgs > 0 then - sDrive = tostring( tArgs[1] ) + sDrive = tostring(tArgs[1]) end if sDrive == nil then - print( "This is computer #" .. os.getComputerID() ) + print("This is computer #" .. os.getComputerID()) local label = os.getComputerLabel() if label then - print( "This computer is labelled \"" .. label .. "\"" ) + print("This computer is labelled \"" .. label .. "\"") end else - local bData = disk.hasData( sDrive ) + local bData = disk.hasData(sDrive) if not bData then - print( "No disk in drive " .. sDrive ) + print("No disk in drive " .. sDrive) return end - print( "The disk is #" .. disk.getID( sDrive ) ) + print("The disk is #" .. disk.getID(sDrive)) - local label = disk.getLabel( sDrive ) + local label = disk.getLabel(sDrive) if label then - print( "The disk is labelled \"" .. label .. "\"" ) + print("The disk is labelled \"" .. label .. "\"") end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/label.lua b/src/main/resources/assets/computercraft/lua/rom/programs/label.lua index ea23277a9..d0e712a05 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/label.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/label.lua @@ -1,67 +1,67 @@ local function printUsage() - print( "Usages:" ) - print( "label get" ) - print( "label get " ) - print( "label set " ) - print( "label set " ) - print( "label clear" ) - print( "label clear " ) + print("Usages:") + print("label get") + print("label get ") + print("label set ") + print("label set ") + print("label clear") + print("label clear ") end -local function checkDrive( sDrive ) - if peripheral.getType( sDrive ) == "drive" then +local function checkDrive(sDrive) + if peripheral.getType(sDrive) == "drive" then -- Check the disk exists - local bData = disk.hasData( sDrive ) + local bData = disk.hasData(sDrive) if not bData then - print( "No disk in " .. sDrive .. " drive" ) + print("No disk in " .. sDrive .. " drive") return false end else - print( "No disk drive named " .. sDrive ) + print("No disk drive named " .. sDrive) return false end return true end -local function get( sDrive ) +local function get(sDrive) if sDrive ~= nil then - if checkDrive( sDrive ) then - local sLabel = disk.getLabel( sDrive ) + if checkDrive(sDrive) then + local sLabel = disk.getLabel(sDrive) if sLabel then - print( "Disk label is \"" .. sLabel .. "\"" ) + print("Disk label is \"" .. sLabel .. "\"") else - print( "No Disk label" ) + print("No Disk label") end end else local sLabel = os.getComputerLabel() if sLabel then - print( "Computer label is \"" .. sLabel .. "\"" ) + print("Computer label is \"" .. sLabel .. "\"") else - print( "No Computer label" ) + print("No Computer label") end end end -local function set( sDrive, sText ) +local function set(sDrive, sText) if sDrive ~= nil then - if checkDrive( sDrive ) then - disk.setLabel( sDrive, sText ) - local sLabel = disk.getLabel( sDrive ) + if checkDrive(sDrive) then + disk.setLabel(sDrive, sText) + local sLabel = disk.getLabel(sDrive) if sLabel then - print( "Disk label set to \"" .. sLabel .. "\"" ) + print("Disk label set to \"" .. sLabel .. "\"") else - print( "Disk label cleared" ) + print("Disk label cleared") end end else - os.setComputerLabel( sText ) + os.setComputerLabel(sText) local sLabel = os.getComputerLabel() if sLabel then - print( "Computer label set to \"" .. sLabel .. "\"" ) + print("Computer label set to \"" .. sLabel .. "\"") else - print( "Computer label cleared" ) + print("Computer label cleared") end end end @@ -71,27 +71,27 @@ local sCommand = tArgs[1] if sCommand == "get" then -- Get a label if #tArgs == 1 then - get( nil ) + get(nil) elseif #tArgs == 2 then - get( tArgs[2] ) + get(tArgs[2]) else printUsage() end elseif sCommand == "set" then -- Set a label if #tArgs == 2 then - set( nil, tArgs[2] ) + set(nil, tArgs[2]) elseif #tArgs == 3 then - set( tArgs[2], tArgs[3] ) + set(tArgs[2], tArgs[3]) else printUsage() end elseif sCommand == "clear" then -- Clear a label if #tArgs == 1 then - set( nil, nil ) + set(nil, nil) elseif #tArgs == 2 then - set( tArgs[2], nil ) + set(tArgs[2], nil) else printUsage() end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/list.lua b/src/main/resources/assets/computercraft/lua/rom/programs/list.lua index f493d8ae5..759130bb6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/list.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/list.lua @@ -4,35 +4,35 @@ local tArgs = { ... } -- Get all the files in the directory local sDir = shell.dir() if tArgs[1] ~= nil then - sDir = shell.resolve( tArgs[1] ) + sDir = shell.resolve(tArgs[1]) end -if not fs.isDir( sDir ) then - printError( "Not a directory" ) +if not fs.isDir(sDir) then + printError("Not a directory") return end -- Sort into dirs/files, and calculate column count -local tAll = fs.list( sDir ) +local tAll = fs.list(sDir) local tFiles = {} local tDirs = {} -local bShowHidden = settings.get( "list.show_hidden" ) -for _, sItem in pairs( tAll ) do - if bShowHidden or string.sub( sItem, 1, 1 ) ~= "." then - local sPath = fs.combine( sDir, sItem ) - if fs.isDir( sPath ) then - table.insert( tDirs, sItem ) +local bShowHidden = settings.get("list.show_hidden") +for _, sItem in pairs(tAll) do + if bShowHidden or string.sub(sItem, 1, 1) ~= "." then + local sPath = fs.combine(sDir, sItem) + if fs.isDir(sPath) then + table.insert(tDirs, sItem) else - table.insert( tFiles, sItem ) + table.insert(tFiles, sItem) end end end -table.sort( tDirs ) -table.sort( tFiles ) +table.sort(tDirs) +table.sort(tFiles) if term.isColour() then - textutils.pagedTabulate( colors.green, tDirs, colors.white, tFiles ) + textutils.pagedTabulate(colors.green, tDirs, colors.white, tFiles) else - textutils.pagedTabulate( tDirs, tFiles ) + textutils.pagedTabulate(tDirs, tFiles) end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua b/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua index 6514e6390..5eea4dd81 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/lua.lua @@ -1,8 +1,8 @@ local tArgs = { ... } if #tArgs > 0 then - print( "This is an interactive Lua prompt." ) - print( "To run a lua program, just type its name." ) + print("This is an interactive Lua prompt.") + print("To run a lua program, just type its name.") return end @@ -15,11 +15,11 @@ local tEnv = { __tostring = function() return "Call exit() to exit." end, __call = function() bRunning = false end, }), - ["_echo"] = function( ... ) + ["_echo"] = function(...) return ... end, } -setmetatable( tEnv, { __index = _ENV } ) +setmetatable(tEnv, { __index = _ENV }) -- Replace our package.path, so that it loads from the current directory, rather -- than from /rom/programs. This makes it a little more friendly to use and @@ -39,38 +39,38 @@ do end if term.isColour() then - term.setTextColour( colours.yellow ) + term.setTextColour(colours.yellow) end -print( "Interactive Lua prompt." ) -print( "Call exit() to exit." ) -term.setTextColour( colours.white ) +print("Interactive Lua prompt.") +print("Call exit() to exit.") +term.setTextColour(colours.white) while bRunning do --if term.isColour() then -- term.setTextColour( colours.yellow ) --end - write( "lua> " ) + write("lua> ") --term.setTextColour( colours.white ) - local s = read( nil, tCommandHistory, function( sLine ) - if settings.get( "lua.autocomplete" ) then - local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.:]+$" ) + local s = read(nil, tCommandHistory, function(sLine) + if settings.get("lua.autocomplete") then + local nStartPos = string.find(sLine, "[a-zA-Z0-9_%.:]+$") if nStartPos then - sLine = string.sub( sLine, nStartPos ) + sLine = string.sub(sLine, nStartPos) end if #sLine > 0 then - return textutils.complete( sLine, tEnv ) + return textutils.complete(sLine, tEnv) end end return nil - end ) + end) if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then - table.insert( tCommandHistory, s ) + table.insert(tCommandHistory, s) end local nForcePrint = 0 - local func, e = load( s, "=lua", "t", tEnv ) - local func2 = load( "return _echo(" .. s .. ");", "=lua", "t", tEnv ) + local func, e = load(s, "=lua", "t", tEnv) + local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv) if not func then if func2 then func = func2 @@ -84,11 +84,11 @@ while bRunning do end if func then - local tResults = table.pack( pcall( func ) ) + local tResults = table.pack(pcall(func)) if tResults[1] then local n = 1 while n < tResults.n or n <= nForcePrint do - local value = tResults[ n + 1 ] + local value = tResults[n + 1] local ok, serialised = pcall(pretty.pretty, value) if ok then pretty.print(serialised) @@ -98,10 +98,10 @@ while bRunning do n = n + 1 end else - printError( tResults[2] ) + printError(tResults[2]) end else - printError( e ) + printError(e) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/mkdir.lua b/src/main/resources/assets/computercraft/lua/rom/programs/mkdir.lua index 3bdb5e840..bbdd08002 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/mkdir.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/mkdir.lua @@ -1,17 +1,17 @@ local tArgs = { ... } if #tArgs < 1 then - print( "Usage: mkdir " ) + print("Usage: mkdir ") return end -for _, v in ipairs( tArgs ) do - local sNewDir = shell.resolve( v ) - if fs.exists( sNewDir ) and not fs.isDir( sNewDir ) then - printError( v .. ": Destination exists" ) - elseif fs.isReadOnly( sNewDir ) then - printError( v .. ": Access denied" ) +for _, v in ipairs(tArgs) do + local sNewDir = shell.resolve(v) + if fs.exists(sNewDir) and not fs.isDir(sNewDir) then + printError(v .. ": Destination exists") + elseif fs.isReadOnly(sNewDir) then + printError(v .. ": Access denied") else - fs.makeDir( sNewDir ) + fs.makeDir(sNewDir) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua b/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua index 8860d8bde..e6daed9e8 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/monitor.lua @@ -1,5 +1,5 @@ local function printUsage() - print( "Usage: monitor " ) + print("Usage: monitor ") return end @@ -10,65 +10,65 @@ if #tArgs < 2 then end local sName = tArgs[1] -if peripheral.getType( sName ) ~= "monitor" then - print( "No monitor named " .. sName ) +if peripheral.getType(sName) ~= "monitor" then + print("No monitor named " .. sName) return end local sProgram = tArgs[2] -local sPath = shell.resolveProgram( sProgram ) +local sPath = shell.resolveProgram(sProgram) if sPath == nil then - print( "No such program: " .. sProgram ) + print("No such program: " .. sProgram) return end -print( "Running " .. sProgram .. " on monitor " .. sName ) +print("Running " .. sProgram .. " on monitor " .. sName) -local monitor = peripheral.wrap( sName ) -local previousTerm = term.redirect( monitor ) +local monitor = peripheral.wrap(sName) +local previousTerm = term.redirect(monitor) -local co = coroutine.create( function() - shell.run( sProgram, table.unpack( tArgs, 3 ) ) -end ) +local co = coroutine.create(function() + shell.run(sProgram, table.unpack(tArgs, 3)) +end) -local function resume( ... ) - local ok, param = coroutine.resume( co, ... ) +local function resume(...) + local ok, param = coroutine.resume(co, ...) if not ok then - printError( param ) + printError(param) end return param end local timers = {} -local ok, param = pcall( function() +local ok, param = pcall(function() local sFilter = resume() - while coroutine.status( co ) ~= "dead" do - local tEvent = table.pack( os.pullEventRaw() ) + while coroutine.status(co) ~= "dead" do + local tEvent = table.pack(os.pullEventRaw()) if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then - sFilter = resume( table.unpack( tEvent, 1, tEvent.n ) ) + sFilter = resume(table.unpack(tEvent, 1, tEvent.n)) end - if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then + if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then if tEvent[1] == "monitor_touch" and tEvent[2] == sName then - timers[os.startTimer( 0.1 )] = { tEvent[3], tEvent[4] } - sFilter = resume( "mouse_click", 1, table.unpack( tEvent, 3, tEvent.n ) ) + timers[os.startTimer(0.1)] = { tEvent[3], tEvent[4] } + sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n)) end end - if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then + if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then if tEvent[1] == "monitor_resize" and tEvent[2] == sName then - sFilter = resume( "term_resize" ) + sFilter = resume("term_resize") end end - if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then + if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then if tEvent[1] == "timer" and timers[tEvent[2]] then - sFilter = resume( "mouse_up", 1, table.unpack( timers[tEvent[2]], 1, 2 ) ) + sFilter = resume("mouse_up", 1, table.unpack(timers[tEvent[2]], 1, 2)) timers[tEvent[2]] = nil end end end -end ) +end) -term.redirect( previousTerm ) +term.redirect(previousTerm) if not ok then - printError( param ) + printError(param) end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua b/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua index a7142fb85..c8c75a40b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/motd.lua @@ -1,6 +1,6 @@ local tMotd = {} -for sPath in string.gmatch(settings.get( "motd.path" ), "[^:]+") do +for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do if fs.exists(sPath) then for sLine in io.lines(sPath) do table.insert(tMotd, sLine) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/move.lua b/src/main/resources/assets/computercraft/lua/rom/programs/move.lua index 5b2dd6266..0ebc5f7f7 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/move.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/move.lua @@ -1,24 +1,24 @@ local tArgs = { ... } if #tArgs < 2 then - print( "Usage: mv " ) + print("Usage: mv ") return end -local sSource = shell.resolve( tArgs[1] ) -local sDest = shell.resolve( tArgs[2] ) -local tFiles = fs.find( sSource ) +local sSource = shell.resolve(tArgs[1]) +local sDest = shell.resolve(tArgs[2]) +local tFiles = fs.find(sSource) if #tFiles > 0 then - for _, sFile in ipairs( tFiles ) do - if fs.isDir( sDest ) then - fs.move( sFile, fs.combine( sDest, fs.getName(sFile) ) ) + for _, sFile in ipairs(tFiles) do + if fs.isDir(sDest) then + fs.move(sFile, fs.combine(sDest, fs.getName(sFile))) elseif #tFiles == 1 then - fs.move( sFile, sDest ) + fs.move(sFile, sDest) else - printError( "Cannot overwrite file multiple times" ) + printError("Cannot overwrite file multiple times") return end end else - printError( "No matching files" ) + printError("No matching files") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/peripherals.lua b/src/main/resources/assets/computercraft/lua/rom/programs/peripherals.lua index 33b7e9c9a..290619614 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/peripherals.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/peripherals.lua @@ -1,10 +1,10 @@ local tPeripherals = peripheral.getNames() -print( "Attached Peripherals:" ) +print("Attached Peripherals:") if #tPeripherals > 0 then for n = 1, #tPeripherals do local sPeripheral = tPeripherals[n] - print( sPeripheral .. " (" .. peripheral.getType( sPeripheral ) .. ")" ) + print(sPeripheral .. " (" .. peripheral.getType(sPeripheral) .. ")") end else - print( "None" ) + print("None") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/equip.lua b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/equip.lua index 62aa7c98d..b4fdb399a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/equip.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/equip.lua @@ -1,11 +1,11 @@ if not pocket then - printError( "Requires a Pocket Computer" ) + printError("Requires a Pocket Computer") return end local ok, err = pocket.equipBack() if not ok then - printError( err ) + printError(err) else - print( "Item equipped" ) + print("Item equipped") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua index 330042936..deb01c380 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/falling.lua @@ -160,7 +160,7 @@ local block_T = { local blocks = { block_line, block_square, block_s1, block_s2, block_L1, block_L2, block_T } -local points = {4, 10, 30, 120} +local points = { 4, 10, 30, 120 } local function lpad(text, amt) text = tostring(text) @@ -456,7 +456,7 @@ local function playGame() sleep(.25) for r = 1, #rows do table.remove(pit, rows[r]) - table.insert(pit, 1, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + table.insert(pit, 1, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) end redrawPit() lines = lines + #rows @@ -496,7 +496,7 @@ local function playGame() while not halt do - local e = {os.pullEvent()} + local e = { os.pullEvent() } if e[1] == "timer" then if e[2] == dropTimer then blockFall() @@ -517,7 +517,7 @@ local function playGame() elseif key == keys.space then hidePit() msgBox("Paused") - while ({os.pullEvent("key")})[2] ~= keys.space do end + while ({ os.pullEvent("key") })[2] ~= keys.space do end redrawPit() drawBlockAt(curBlock, curX, curY, curRot) dropTimer = os.startTimer(dropSpeed) @@ -606,7 +606,7 @@ local function runMenu() drawMenu() while true do - local event = {os.pullEvent()} + local event = { os.pullEvent() } if event[1] == "key" then local key = event[2] if key == keys.right or key == keys.d and selected == 1 then diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/unequip.lua b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/unequip.lua index 28308d407..8cd1ef444 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/pocket/unequip.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/pocket/unequip.lua @@ -1,11 +1,11 @@ if not pocket then - printError( "Requires a Pocket Computer" ) + printError("Requires a Pocket Computer") return end local ok, err = pocket.unequipBack() if not ok then - printError( err ) + printError(err) else - print( "Item unequipped" ) + print("Item unequipped") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/programs.lua b/src/main/resources/assets/computercraft/lua/rom/programs/programs.lua index 1fe382799..015264d94 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/programs.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/programs.lua @@ -5,5 +5,5 @@ if #tArgs > 0 and tArgs[1] == "all" then bAll = true end -local tPrograms = shell.programs( bAll ) -textutils.pagedTabulate( tPrograms ) +local tPrograms = shell.programs(bAll) +textutils.pagedTabulate(tPrograms) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/reboot.lua b/src/main/resources/assets/computercraft/lua/rom/programs/reboot.lua index 2ea87bb2e..2824cc0c6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/reboot.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/reboot.lua @@ -1,8 +1,8 @@ if term.isColour() then - term.setTextColour( colours.yellow ) + term.setTextColour(colours.yellow) end -print( "Goodbye" ) -term.setTextColour( colours.white ) +print("Goodbye") +term.setTextColour(colours.white) -sleep( 1 ) +sleep(1) os.reboot() diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua index ac8f4bc65..c632104b1 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/chat.lua @@ -2,29 +2,29 @@ local tArgs = { ... } local function printUsage() - print( "Usages:" ) - print( "chat host " ) - print( "chat join " ) + print("Usages:") + print("chat host ") + print("chat join ") end local sOpenedModem = nil local function openModem() - for _, sModem in ipairs( peripheral.getNames() ) do - if peripheral.getType( sModem ) == "modem" then - if not rednet.isOpen( sModem ) then - rednet.open( sModem ) + for _, sModem in ipairs(peripheral.getNames()) do + if peripheral.getType(sModem) == "modem" then + if not rednet.isOpen(sModem) then + rednet.open(sModem) sOpenedModem = sModem end return true end end - print( "No modems found." ) + print("No modems found.") return false end local function closeModem() if sOpenedModem ~= nil then - rednet.close( sOpenedModem ) + rednet.close(sOpenedModem) sOpenedModem = nil end end @@ -53,54 +53,54 @@ if sCommand == "host" then if not openModem() then return end - rednet.host( "chat", sHostname ) - print( "0 users connected." ) + rednet.host("chat", sHostname) + print("0 users connected.") local tUsers = {} local nUsers = 0 - local function send( sText, nUserID ) + local function send(sText, nUserID) if nUserID then - local tUser = tUsers[ nUserID ] + local tUser = tUsers[nUserID] if tUser then - rednet.send( tUser.nID, { + rednet.send(tUser.nID, { sType = "text", nUserID = nUserID, sText = sText, - }, "chat" ) + }, "chat") end else - for nUserID, tUser in pairs( tUsers ) do - rednet.send( tUser.nID, { + for nUserID, tUser in pairs(tUsers) do + rednet.send(tUser.nID, { sType = "text", nUserID = nUserID, sText = sText, - }, "chat" ) + }, "chat") end end end -- Setup ping pong local tPingPongTimer = {} - local function ping( nUserID ) - local tUser = tUsers[ nUserID ] - rednet.send( tUser.nID, { + local function ping(nUserID) + local tUser = tUsers[nUserID] + rednet.send(tUser.nID, { sType = "ping to client", nUserID = nUserID, - }, "chat" ) + }, "chat") - local timer = os.startTimer( 15 ) + local timer = os.startTimer(15) tUser.bPingPonged = false - tPingPongTimer[ timer ] = nUserID + tPingPongTimer[timer] = nUserID end local function printUsers() local _, y = term.getCursorPos() - term.setCursorPos( 1, y - 1 ) + term.setCursorPos(1, y - 1) term.clearLine() if nUsers == 1 then - print( nUsers .. " user connected." ) + print(nUsers .. " user connected.") else - print( nUsers .. " users connected." ) + print(nUsers .. " users connected.") end end @@ -108,18 +108,18 @@ if sCommand == "host" then local ok, error = pcall(parallel.waitForAny, function() while true do - local _, timer = os.pullEvent( "timer" ) - local nUserID = tPingPongTimer[ timer ] - if nUserID and tUsers[ nUserID ] then - local tUser = tUsers[ nUserID ] + local _, timer = os.pullEvent("timer") + local nUserID = tPingPongTimer[timer] + if nUserID and tUsers[nUserID] then + local tUser = tUsers[nUserID] if tUser then if not tUser.bPingPonged then - send( "* " .. tUser.sUsername .. " has timed out" ) - tUsers[ nUserID ] = nil + send("* " .. tUser.sUsername .. " has timed out") + tUsers[nUserID] = nil nUsers = nUsers - 1 printUsers() else - ping( nUserID ) + ping(nUserID) end end end @@ -129,91 +129,91 @@ if sCommand == "host" then while true do local tCommands tCommands = { - ["me"] = function( tUser, sContent ) + ["me"] = function(tUser, sContent) if #sContent > 0 then - send( "* " .. tUser.sUsername .. " " .. sContent ) + send("* " .. tUser.sUsername .. " " .. sContent) else - send( "* Usage: /me [words]", tUser.nUserID ) + send("* Usage: /me [words]", tUser.nUserID) end end, - ["nick"] = function( tUser, sContent ) + ["nick"] = function(tUser, sContent) if #sContent > 0 then local sOldName = tUser.sUsername tUser.sUsername = sContent - send( "* " .. sOldName .. " is now known as " .. tUser.sUsername ) + send("* " .. sOldName .. " is now known as " .. tUser.sUsername) else - send( "* Usage: /nick [nickname]", tUser.nUserID ) + send("* Usage: /nick [nickname]", tUser.nUserID) end end, - ["users"] = function( tUser, sContent ) - send( "* Connected Users:", tUser.nUserID ) + ["users"] = function(tUser, sContent) + send("* Connected Users:", tUser.nUserID) local sUsers = "*" - for _, tUser in pairs( tUsers ) do + for _, tUser in pairs(tUsers) do sUsers = sUsers .. " " .. tUser.sUsername end - send( sUsers, tUser.nUserID ) + send(sUsers, tUser.nUserID) end, - ["help"] = function( tUser, sContent ) - send( "* Available commands:", tUser.nUserID ) + ["help"] = function(tUser, sContent) + send("* Available commands:", tUser.nUserID) local sCommands = "*" - for sCommand in pairs( tCommands ) do + for sCommand in pairs(tCommands) do sCommands = sCommands .. " /" .. sCommand end - send( sCommands .. " /logout", tUser.nUserID ) + send(sCommands .. " /logout", tUser.nUserID) end, } - local nSenderID, tMessage = rednet.receive( "chat" ) - if type( tMessage ) == "table" then + local nSenderID, tMessage = rednet.receive("chat") + if type(tMessage) == "table" then if tMessage.sType == "login" then -- Login from new client local nUserID = tMessage.nUserID local sUsername = tMessage.sUsername if nUserID and sUsername then - tUsers[ nUserID ] = { + tUsers[nUserID] = { nID = nSenderID, nUserID = nUserID, sUsername = sUsername, } nUsers = nUsers + 1 printUsers() - send( "* " .. sUsername .. " has joined the chat" ) - ping( nUserID ) + send("* " .. sUsername .. " has joined the chat") + ping(nUserID) end else -- Something else from existing client local nUserID = tMessage.nUserID - local tUser = tUsers[ nUserID ] + local tUser = tUsers[nUserID] if tUser and tUser.nID == nSenderID then if tMessage.sType == "logout" then - send( "* " .. tUser.sUsername .. " has left the chat" ) - tUsers[ nUserID ] = nil + send("* " .. tUser.sUsername .. " has left the chat") + tUsers[nUserID] = nil nUsers = nUsers - 1 printUsers() elseif tMessage.sType == "chat" then local sMessage = tMessage.sText if sMessage then - local sCommand = string.match( sMessage, "^/([a-z]+)" ) + local sCommand = string.match(sMessage, "^/([a-z]+)") if sCommand then - local fnCommand = tCommands[ sCommand ] + local fnCommand = tCommands[sCommand] if fnCommand then - local sContent = string.sub( sMessage, #sCommand + 3 ) - fnCommand( tUser, sContent ) + local sContent = string.sub(sMessage, #sCommand + 3) + fnCommand(tUser, sContent) else - send( "* Unrecognised command: /" .. sCommand, tUser.nUserID ) + send("* Unrecognised command: /" .. sCommand, tUser.nUserID) end else - send( "<" .. tUser.sUsername .. "> " .. tMessage.sText ) + send("<" .. tUser.sUsername .. "> " .. tMessage.sText) end end elseif tMessage.sType == "ping to server" then - rednet.send( tUser.nID, { + rednet.send(tUser.nID, { sType = "pong to client", nUserID = nUserID, - }, "chat" ) + }, "chat") elseif tMessage.sType == "pong to server" then tUser.bPingPonged = true @@ -226,17 +226,17 @@ if sCommand == "host" then end ) if not ok then - printError( error ) + printError(error) end -- Unhost server - for nUserID, tUser in pairs( tUsers ) do - rednet.send( tUser.nID, { + for nUserID, tUser in pairs(tUsers) do + rednet.send(tUser.nID, { sType = "kick", nUserID = nUserID, - }, "chat" ) + }, "chat") end - rednet.unhost( "chat" ) + rednet.unhost("chat") closeModem() elseif sCommand == "join" then @@ -253,80 +253,80 @@ elseif sCommand == "join" then if not openModem() then return end - write( "Looking up " .. sHostname .. "... " ) - local nHostID = rednet.lookup( "chat", sHostname ) + write("Looking up " .. sHostname .. "... ") + local nHostID = rednet.lookup("chat", sHostname) if nHostID == nil then - print( "Failed." ) + print("Failed.") return else - print( "Success." ) + print("Success.") end -- Login - local nUserID = math.random( 1, 2147483647 ) - rednet.send( nHostID, { + local nUserID = math.random(1, 2147483647) + rednet.send(nHostID, { sType = "login", nUserID = nUserID, sUsername = sUsername, - }, "chat" ) + }, "chat") -- Setup ping pong local bPingPonged = true - local pingPongTimer = os.startTimer( 0 ) + local pingPongTimer = os.startTimer(0) local function ping() - rednet.send( nHostID, { + rednet.send(nHostID, { sType = "ping to server", nUserID = nUserID, - }, "chat" ) + }, "chat") bPingPonged = false - pingPongTimer = os.startTimer( 15 ) + pingPongTimer = os.startTimer(15) end -- Handle messages local w, h = term.getSize() local parentTerm = term.current() - local titleWindow = window.create( parentTerm, 1, 1, w, 1, true ) - local historyWindow = window.create( parentTerm, 1, 2, w, h - 2, true ) - local promptWindow = window.create( parentTerm, 1, h, w, 1, true ) - historyWindow.setCursorPos( 1, h - 2 ) + local titleWindow = window.create(parentTerm, 1, 1, w, 1, true) + local historyWindow = window.create(parentTerm, 1, 2, w, h - 2, true) + local promptWindow = window.create(parentTerm, 1, h, w, 1, true) + historyWindow.setCursorPos(1, h - 2) term.clear() - term.setTextColour( textColour ) - term.redirect( promptWindow ) + term.setTextColour(textColour) + term.redirect(promptWindow) promptWindow.restoreCursor() local function drawTitle() local w = titleWindow.getSize() local sTitle = sUsername .. " on " .. sHostname - titleWindow.setTextColour( highlightColour ) - titleWindow.setCursorPos( math.floor( w / 2 - #sTitle / 2 ), 1 ) + titleWindow.setTextColour(highlightColour) + titleWindow.setCursorPos(math.floor(w / 2 - #sTitle / 2), 1) titleWindow.clearLine() - titleWindow.write( sTitle ) + titleWindow.write(sTitle) promptWindow.restoreCursor() end - local function printMessage( sMessage ) - term.redirect( historyWindow ) + local function printMessage(sMessage) + term.redirect(historyWindow) print() - if string.match( sMessage, "^%*" ) then + if string.match(sMessage, "^%*") then -- Information - term.setTextColour( highlightColour ) - write( sMessage ) - term.setTextColour( textColour ) + term.setTextColour(highlightColour) + write(sMessage) + term.setTextColour(textColour) else -- Chat - local sUsernameBit = string.match( sMessage, "^<[^>]*>" ) + local sUsernameBit = string.match(sMessage, "^<[^>]*>") if sUsernameBit then - term.setTextColour( highlightColour ) - write( sUsernameBit ) - term.setTextColour( textColour ) - write( string.sub( sMessage, #sUsernameBit + 1 ) ) + term.setTextColour(highlightColour) + write(sUsernameBit) + term.setTextColour(textColour) + write(string.sub(sMessage, #sUsernameBit + 1)) else - write( sMessage ) + write(sMessage) end end - term.redirect( promptWindow ) + term.redirect(promptWindow) promptWindow.restoreCursor() end @@ -339,7 +339,7 @@ elseif sCommand == "join" then if sEvent == "timer" then if timer == pingPongTimer then if not bPingPonged then - printMessage( "Server timeout." ) + printMessage("Server timeout.") return else ping() @@ -348,28 +348,28 @@ elseif sCommand == "join" then elseif sEvent == "term_resize" then local w, h = parentTerm.getSize() - titleWindow.reposition( 1, 1, w, 1 ) - historyWindow.reposition( 1, 2, w, h - 2 ) - promptWindow.reposition( 1, h, w, 1 ) + titleWindow.reposition(1, 1, w, 1) + historyWindow.reposition(1, 2, w, h - 2) + promptWindow.reposition(1, h, w, 1) end end end, function() while true do - local nSenderID, tMessage = rednet.receive( "chat" ) - if nSenderID == nHostID and type( tMessage ) == "table" and tMessage.nUserID == nUserID then + local nSenderID, tMessage = rednet.receive("chat") + if nSenderID == nHostID and type(tMessage) == "table" and tMessage.nUserID == nUserID then if tMessage.sType == "text" then local sText = tMessage.sText if sText then - printMessage( sText ) + printMessage(sText) end elseif tMessage.sType == "ping to client" then - rednet.send( nSenderID, { + rednet.send(nSenderID, { sType = "pong to server", nUserID = nUserID, - }, "chat" ) + }, "chat") elseif tMessage.sType == "pong to client" then bPingPonged = true @@ -384,48 +384,48 @@ elseif sCommand == "join" then function() local tSendHistory = {} while true do - promptWindow.setCursorPos( 1, 1 ) + promptWindow.setCursorPos(1, 1) promptWindow.clearLine() - promptWindow.setTextColor( highlightColour ) - promptWindow.write( ": " ) - promptWindow.setTextColor( textColour ) + promptWindow.setTextColor(highlightColour) + promptWindow.write(": ") + promptWindow.setTextColor(textColour) - local sChat = read( nil, tSendHistory ) - if string.match( sChat, "^/logout" ) then + local sChat = read(nil, tSendHistory) + if string.match(sChat, "^/logout") then break else - rednet.send( nHostID, { + rednet.send(nHostID, { sType = "chat", nUserID = nUserID, sText = sChat, - }, "chat" ) - table.insert( tSendHistory, sChat ) + }, "chat") + table.insert(tSendHistory, sChat) end end end ) -- Close the windows - term.redirect( parentTerm ) + term.redirect(parentTerm) -- Print error notice local _, h = term.getSize() - term.setCursorPos( 1, h ) + term.setCursorPos(1, h) term.clearLine() - term.setCursorBlink( false ) + term.setCursorBlink(false) if not ok then - printError( error ) + printError(error) end -- Logout - rednet.send( nHostID, { + rednet.send(nHostID, { sType = "logout", nUserID = nUserID, - }, "chat" ) + }, "chat") closeModem() -- Print disconnection notice - print( "Disconnected." ) + print("Disconnected.") else -- "chat somethingelse" diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/repeat.lua b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/repeat.lua index c6bad5634..b9a9bcb94 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/rednet/repeat.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/rednet/repeat.lua @@ -1,40 +1,40 @@ -- Find modems local tModems = {} -for _, sModem in ipairs( peripheral.getNames() ) do - if peripheral.getType( sModem ) == "modem" then - table.insert( tModems, sModem ) +for _, sModem in ipairs(peripheral.getNames()) do + if peripheral.getType(sModem) == "modem" then + table.insert(tModems, sModem) end end if #tModems == 0 then - print( "No modems found." ) + print("No modems found.") return elseif #tModems == 1 then - print( "1 modem found." ) + print("1 modem found.") else - print( #tModems .. " modems found." ) + print(#tModems .. " modems found.") end -local function open( nChannel ) +local function open(nChannel) for n = 1, #tModems do local sModem = tModems[n] - peripheral.call( sModem, "open", nChannel ) + peripheral.call(sModem, "open", nChannel) end end -local function close( nChannel ) +local function close(nChannel) for n = 1, #tModems do local sModem = tModems[n] - peripheral.call( sModem, "close", nChannel ) + peripheral.call(sModem, "close", nChannel) end end -- Open channels -print( "0 messages repeated." ) -open( rednet.CHANNEL_REPEAT ) +print("0 messages repeated.") +open(rednet.CHANNEL_REPEAT) -- Main loop (terminate to break) -local ok, error = pcall( function() +local ok, error = pcall(function() local tReceivedMessages = {} local tReceivedMessageTimeouts = {} local nTransmittedMessages = 0 @@ -44,28 +44,28 @@ local ok, error = pcall( function() if sEvent == "modem_message" then -- Got a modem message, rebroadcast it if it's a rednet thing if nChannel == rednet.CHANNEL_REPEAT then - if type( tMessage ) == "table" and tMessage.nMessageID and tMessage.nRecipient and type(tMessage.nRecipient) == "number" then - if not tReceivedMessages[ tMessage.nMessageID ] then + if type(tMessage) == "table" and tMessage.nMessageID and tMessage.nRecipient and type(tMessage.nRecipient) == "number" then + if not tReceivedMessages[tMessage.nMessageID] then -- Ensure we only repeat a message once - tReceivedMessages[ tMessage.nMessageID ] = true - tReceivedMessageTimeouts[ os.startTimer( 30 ) ] = tMessage.nMessageID + tReceivedMessages[tMessage.nMessageID] = true + tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID -- Send on all other open modems, to the target and to other repeaters for n = 1, #tModems do local sOtherModem = tModems[n] - peripheral.call( sOtherModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage ) - peripheral.call( sOtherModem, "transmit", tMessage.nRecipient, nReplyChannel, tMessage ) + peripheral.call(sOtherModem, "transmit", rednet.CHANNEL_REPEAT, nReplyChannel, tMessage) + peripheral.call(sOtherModem, "transmit", tMessage.nRecipient, nReplyChannel, tMessage) end -- Log the event nTransmittedMessages = nTransmittedMessages + 1 local _, y = term.getCursorPos() - term.setCursorPos( 1, y - 1 ) + term.setCursorPos(1, y - 1) term.clearLine() if nTransmittedMessages == 1 then - print( nTransmittedMessages .. " message repeated." ) + print(nTransmittedMessages .. " message repeated.") else - print( nTransmittedMessages .. " messages repeated." ) + print(nTransmittedMessages .. " messages repeated.") end end end @@ -74,18 +74,18 @@ local ok, error = pcall( function() elseif sEvent == "timer" then -- Got a timer event, use it to clear the message history local nTimer = sModem - local nMessageID = tReceivedMessageTimeouts[ nTimer ] + local nMessageID = tReceivedMessageTimeouts[nTimer] if nMessageID then - tReceivedMessageTimeouts[ nTimer ] = nil - tReceivedMessages[ nMessageID ] = nil + tReceivedMessageTimeouts[nTimer] = nil + tReceivedMessages[nMessageID] = nil end end end -end ) +end) if not ok then - printError( error ) + printError(error) end -- Close channels -close( rednet.CHANNEL_REPEAT ) +close(rednet.CHANNEL_REPEAT) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/redstone.lua b/src/main/resources/assets/computercraft/lua/rom/programs/redstone.lua index ff7f87a48..35fae9f04 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/redstone.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/redstone.lua @@ -2,64 +2,64 @@ local tArgs = { ... } local function printUsage() - print( "Usages:" ) - print( "redstone probe" ) - print( "redstone set " ) - print( "redstone set " ) - print( "redstone pulse " ) + print("Usages:") + print("redstone probe") + print("redstone set ") + print("redstone set ") + print("redstone pulse ") end local sCommand = tArgs[1] if sCommand == "probe" then -- "redstone probe" -- Regular input - print( "Redstone inputs: " ) + print("Redstone inputs: ") local count = 0 local bundledCount = 0 - for _, sSide in ipairs( redstone.getSides() ) do - if redstone.getBundledInput( sSide ) > 0 then + for _, sSide in ipairs(redstone.getSides()) do + if redstone.getBundledInput(sSide) > 0 then bundledCount = bundledCount + 1 end - if redstone.getInput( sSide ) then + if redstone.getInput(sSide) then if count > 0 then - io.write( ", " ) + io.write(", ") end - io.write( sSide ) + io.write(sSide) count = count + 1 end end if count > 0 then - print( "." ) + print(".") else - print( "None." ) + print("None.") end -- Bundled input if bundledCount > 0 then print() - print( "Bundled inputs:" ) - for _, sSide in ipairs( redstone.getSides() ) do - local nInput = redstone.getBundledInput( sSide ) + print("Bundled inputs:") + for _, sSide in ipairs(redstone.getSides()) do + local nInput = redstone.getBundledInput(sSide) if nInput ~= 0 then - write( sSide .. ": " ) + write(sSide .. ": ") local count = 0 - for sColour, nColour in pairs( colors ) do - if type( nColour ) == "number" and colors.test( nInput, nColour ) then + for sColour, nColour in pairs(colors) do + if type(nColour) == "number" and colors.test(nInput, nColour) then if count > 0 then - write( ", " ) + write(", ") end if term.isColour() then - term.setTextColour( nColour ) + term.setTextColour(nColour) end - write( sColour ) + write(sColour) if term.isColour() then - term.setTextColour( colours.white ) + term.setTextColour(colours.white) end count = count + 1 end end - print( "." ) + print(".") end end end @@ -67,13 +67,13 @@ if sCommand == "probe" then elseif sCommand == "pulse" then -- "redstone pulse" local sSide = tArgs[2] - local nCount = tonumber( tArgs[3] ) or 1 - local nPeriod = tonumber( tArgs[4] ) or 0.5 + local nCount = tonumber(tArgs[3]) or 1 + local nPeriod = tonumber(tArgs[4]) or 0.5 for _ = 1, nCount do - redstone.setOutput( sSide, true ) - sleep( nPeriod / 2 ) - redstone.setOutput( sSide, false ) - sleep( nPeriod / 2 ) + redstone.setOutput(sSide, true) + sleep(nPeriod / 2) + redstone.setOutput(sSide, false) + sleep(nPeriod / 2) end elseif sCommand == "set" then @@ -84,30 +84,30 @@ elseif sCommand == "set" then local sColour = tArgs[3] local nColour = colors[sColour] or colours[sColour] if type(nColour) ~= "number" then - printError( "No such color" ) + printError("No such color") return end local sValue = tArgs[4] if sValue == "true" then - rs.setBundledOutput( sSide, colors.combine( rs.getBundledOutput( sSide ), nColour ) ) + rs.setBundledOutput(sSide, colors.combine(rs.getBundledOutput(sSide), nColour)) elseif sValue == "false" then - rs.setBundledOutput( sSide, colors.subtract( rs.getBundledOutput( sSide ), nColour ) ) + rs.setBundledOutput(sSide, colors.subtract(rs.getBundledOutput(sSide), nColour)) else - print( "Value must be boolean" ) + print("Value must be boolean") end else -- Regular output local sValue = tArgs[3] local nValue = tonumber(sValue) if sValue == "true" then - rs.setOutput( sSide, true ) + rs.setOutput(sSide, true) elseif sValue == "false" then - rs.setOutput( sSide, false ) + rs.setOutput(sSide, false) elseif nValue and nValue >= 0 and nValue <= 15 then - rs.setAnalogOutput( sSide, nValue ) + rs.setAnalogOutput(sSide, nValue) else - print( "Value must be boolean or 0-15" ) + print("Value must be boolean or 0-15") end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/rename.lua b/src/main/resources/assets/computercraft/lua/rom/programs/rename.lua index 4583adf62..a90c1f8ad 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/rename.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/rename.lua @@ -1,21 +1,21 @@ local tArgs = { ... } if #tArgs < 2 then - print( "Usage: rename " ) + print("Usage: rename ") return end -local sSource = shell.resolve( tArgs[1] ) -local sDest = shell.resolve( tArgs[2] ) +local sSource = shell.resolve(tArgs[1]) +local sDest = shell.resolve(tArgs[2]) -if not fs.exists( sSource ) then - printError( "No matching files" ) +if not fs.exists(sSource) then + printError("No matching files") return -elseif fs.exists( sDest ) then - printError( "Destination exists" ) +elseif fs.exists(sDest) then + printError("Destination exists") return -elseif fs.isReadOnly( sDest ) then - printError( "Destination is read-only" ) +elseif fs.isReadOnly(sDest) then + printError("Destination is read-only") return end -fs.move( sSource, sDest ) +fs.move(sSource, sDest) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/set.lua b/src/main/resources/assets/computercraft/lua/rom/programs/set.lua index f2bacabcc..a826d8ad6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/set.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/set.lua @@ -4,7 +4,7 @@ if #tArgs == 0 then -- "set" local _, y = term.getCursorPos() local tSettings = {} - for n, sName in ipairs( settings.getNames() ) do + for n, sName in ipairs(settings.getNames()) do tSettings[n] = textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName)) end textutils.pagedPrint(table.concat(tSettings, "\n"), y - 3) @@ -12,7 +12,7 @@ if #tArgs == 0 then elseif #tArgs == 1 then -- "set foo" local sName = tArgs[1] - print( textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName)) ) + print(textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName))) else -- "set foo bar" @@ -31,15 +31,15 @@ else value = sValue end - local oldValue = settings.get( sValue ) + local oldValue = settings.get(sValue) if value ~= nil then - settings.set( sName, value ) - print( textutils.serialize(sName) .. " set to " .. textutils.serialize(value) ) + settings.set(sName, value) + print(textutils.serialize(sName) .. " set to " .. textutils.serialize(value)) else - settings.unset( sName ) - print( textutils.serialize(sName) .. " unset" ) + settings.unset(sName) + print(textutils.serialize(sName) .. " unset") end if value ~= oldValue then - settings.save( ".settings" ) + settings.save(".settings") end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua b/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua index 5628f0c6f..8a63138f8 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua @@ -5,7 +5,7 @@ local parentShell = shell local parentTerm = term.current() if multishell then - multishell.setTitle( multishell.getCurrent(), "shell" ) + multishell.setTitle(multishell.getCurrent(), "shell") end local bExit = false @@ -16,10 +16,10 @@ local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {} local tProgramStack = {} local shell = {} -local function createShellEnv( sDir ) +local function createShellEnv(sDir) local tEnv = {} - tEnv[ "shell" ] = shell - tEnv[ "multishell" ] = multishell + tEnv.shell = shell + tEnv.multishell = multishell local package = {} package.loaded = { @@ -40,14 +40,14 @@ local function createShellEnv( sDir ) package.config = "/\n;\n?\n!\n-" package.preload = {} package.loaders = { - function( name ) + function(name) if package.preload[name] then return package.preload[name] else return nil, "no field package.preload['" .. name .. "']" end end, - function( name ) + function(name) local fname = string.gsub(name, "%.", "/") local sError = "" for pattern in string.gmatch(package.path, "[^;]+") do @@ -56,7 +56,7 @@ local function createShellEnv( sDir ) sPath = fs.combine(sDir, sPath) end if fs.exists(sPath) and not fs.isDir(sPath) then - local fnFile, sError = loadfile( sPath, nil, tEnv ) + local fnFile, sError = loadfile(sPath, nil, tEnv) if fnFile then return fnFile, sPath else @@ -74,7 +74,7 @@ local function createShellEnv( sDir ) } local sentinel = {} - local function require( name ) + local function require(name) expect(1, name, "string") if package.loaded[name] == sentinel then error("loop or previous error loading module '" .. name .. "'", 0) @@ -100,8 +100,8 @@ local function createShellEnv( sDir ) error(sError, 2) end - tEnv["package"] = package - tEnv["require"] = require + tEnv.package = package + tEnv.require = require return tEnv end @@ -118,52 +118,52 @@ else bgColour = colours.black end -local function run( _sCommand, ... ) - local sPath = shell.resolveProgram( _sCommand ) +local function run(_sCommand, ...) + local sPath = shell.resolveProgram(_sCommand) if sPath ~= nil then tProgramStack[#tProgramStack + 1] = sPath if multishell then - local sTitle = fs.getName( sPath ) + local sTitle = fs.getName(sPath) if sTitle:sub(-4) == ".lua" then sTitle = sTitle:sub(1, -5) end - multishell.setTitle( multishell.getCurrent(), sTitle ) + multishell.setTitle(multishell.getCurrent(), sTitle) end - local sDir = fs.getDir( sPath ) - local env = createShellEnv( sDir ) - env[ "arg" ] = { [0] = _sCommand, ... } - local result = os.run( env, sPath, ... ) + local sDir = fs.getDir(sPath) + local env = createShellEnv(sDir) + env.arg = { [0] = _sCommand, ... } + local result = os.run(env, sPath, ...) tProgramStack[#tProgramStack] = nil if multishell then if #tProgramStack > 0 then - local sTitle = fs.getName( tProgramStack[#tProgramStack] ) + local sTitle = fs.getName(tProgramStack[#tProgramStack]) if sTitle:sub(-4) == ".lua" then sTitle = sTitle:sub(1, -5) end - multishell.setTitle( multishell.getCurrent(), sTitle ) + multishell.setTitle(multishell.getCurrent(), sTitle) else - multishell.setTitle( multishell.getCurrent(), "shell" ) + multishell.setTitle(multishell.getCurrent(), "shell") end end return result else - printError( "No such program" ) + printError("No such program") return false end end -local function tokenise( ... ) - local sLine = table.concat( { ... }, " " ) +local function tokenise(...) + local sLine = table.concat({ ... }, " ") local tWords = {} local bQuoted = false - for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do + for match in string.gmatch(sLine .. "\"", "(.-)\"") do if bQuoted then - table.insert( tWords, match ) + table.insert(tWords, match) else - for m in string.gmatch( match, "[^ \t]+" ) do - table.insert( tWords, m ) + for m in string.gmatch(match, "[^ \t]+") do + table.insert(tWords, m) end end bQuoted = not bQuoted @@ -172,11 +172,11 @@ local function tokenise( ... ) end -- Install shell API -function shell.run( ... ) - local tWords = tokenise( ... ) +function shell.run(...) + local tWords = tokenise(...) local sCommand = tWords[1] if sCommand then - return run( sCommand, table.unpack( tWords, 2 ) ) + return run(sCommand, table.unpack(tWords, 2)) end return false end @@ -189,10 +189,10 @@ function shell.dir() return sDir end -function shell.setDir( _sDir ) +function shell.setDir(_sDir) expect(1, _sDir, "string") - if not fs.isDir( _sDir ) then - error( "Not a directory", 2 ) + if not fs.isDir(_sDir) then + error("Not a directory", 2) end sDir = _sDir end @@ -201,46 +201,46 @@ function shell.path() return sPath end -function shell.setPath( _sPath ) +function shell.setPath(_sPath) expect(1, _sPath, "string") sPath = _sPath end -function shell.resolve( _sPath ) +function shell.resolve(_sPath) expect(1, _sPath, "string") - local sStartChar = string.sub( _sPath, 1, 1 ) + local sStartChar = string.sub(_sPath, 1, 1) if sStartChar == "/" or sStartChar == "\\" then - return fs.combine( "", _sPath ) + return fs.combine("", _sPath) else - return fs.combine( sDir, _sPath ) + return fs.combine(sDir, _sPath) end end -local function pathWithExtension( _sPath, _sExt ) +local function pathWithExtension(_sPath, _sExt) local nLen = #sPath - local sEndChar = string.sub( _sPath, nLen, nLen ) + local sEndChar = string.sub(_sPath, nLen, nLen) -- Remove any trailing slashes so we can add an extension to the path safely if sEndChar == "/" or sEndChar == "\\" then - _sPath = string.sub( _sPath, 1, nLen - 1 ) + _sPath = string.sub(_sPath, 1, nLen - 1) end return _sPath .. "." .. _sExt end -function shell.resolveProgram( _sCommand ) +function shell.resolveProgram(_sCommand) expect(1, _sCommand, "string") -- Substitute aliases firsts - if tAliases[ _sCommand ] ~= nil then - _sCommand = tAliases[ _sCommand ] + if tAliases[_sCommand] ~= nil then + _sCommand = tAliases[_sCommand] end -- If the path is a global path, use it directly if _sCommand:find("/") or _sCommand:find("\\") then - local sPath = shell.resolve( _sCommand ) - if fs.exists( sPath ) and not fs.isDir( sPath ) then + local sPath = shell.resolve(_sCommand) + if fs.exists(sPath) and not fs.isDir(sPath) then return sPath else - local sPathLua = pathWithExtension( sPath, "lua" ) - if fs.exists( sPathLua ) and not fs.isDir( sPathLua ) then + local sPathLua = pathWithExtension(sPath, "lua") + if fs.exists(sPathLua) and not fs.isDir(sPathLua) then return sPathLua end end @@ -249,12 +249,12 @@ function shell.resolveProgram( _sCommand ) -- Otherwise, look on the path variable for sPath in string.gmatch(sPath, "[^:]+") do - sPath = fs.combine( shell.resolve( sPath ), _sCommand ) - if fs.exists( sPath ) and not fs.isDir( sPath ) then + sPath = fs.combine(shell.resolve(sPath), _sCommand) + if fs.exists(sPath) and not fs.isDir(sPath) then return sPath else - local sPathLua = pathWithExtension( sPath, "lua" ) - if fs.exists( sPathLua ) and not fs.isDir( sPathLua ) then + local sPathLua = pathWithExtension(sPath, "lua") + if fs.exists(sPathLua) and not fs.isDir(sPathLua) then return sPathLua end end @@ -264,22 +264,22 @@ function shell.resolveProgram( _sCommand ) return nil end -function shell.programs( _bIncludeHidden ) +function shell.programs(_bIncludeHidden) local tItems = {} -- Add programs from the path for sPath in string.gmatch(sPath, "[^:]+") do - sPath = shell.resolve( sPath ) - if fs.isDir( sPath ) then - local tList = fs.list( sPath ) + sPath = shell.resolve(sPath) + if fs.isDir(sPath) then + local tList = fs.list(sPath) for n = 1, #tList do local sFile = tList[n] - if not fs.isDir( fs.combine( sPath, sFile ) ) and - (_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then + if not fs.isDir(fs.combine(sPath, sFile)) and + (_bIncludeHidden or string.sub(sFile, 1, 1) ~= ".") then if #sFile > 4 and sFile:sub(-4) == ".lua" then sFile = sFile:sub(1, -5) end - tItems[ sFile ] = true + tItems[sFile] = true end end end @@ -287,40 +287,40 @@ function shell.programs( _bIncludeHidden ) -- Sort and return local tItemList = {} - for sItem in pairs( tItems ) do - table.insert( tItemList, sItem ) + for sItem in pairs(tItems) do + table.insert(tItemList, sItem) end - table.sort( tItemList ) + table.sort(tItemList) return tItemList end -local function completeProgram( sLine ) +local function completeProgram(sLine) if #sLine > 0 and (sLine:find("/") or sLine:find("\\")) then -- Add programs from the root - return fs.complete( sLine, sDir, true, false ) + return fs.complete(sLine, sDir, true, false) else local tResults = {} local tSeen = {} -- Add aliases - for sAlias in pairs( tAliases ) do - if #sAlias > #sLine and string.sub( sAlias, 1, #sLine ) == sLine then - local sResult = string.sub( sAlias, #sLine + 1 ) - if not tSeen[ sResult ] then - table.insert( tResults, sResult ) - tSeen[ sResult ] = true + for sAlias in pairs(tAliases) do + if #sAlias > #sLine and string.sub(sAlias, 1, #sLine) == sLine then + local sResult = string.sub(sAlias, #sLine + 1) + if not tSeen[sResult] then + table.insert(tResults, sResult) + tSeen[sResult] = true end end end -- Add all subdirectories. We don't include files as they will be added in the block below - local tDirs = fs.complete( sLine, sDir, false, false ) + local tDirs = fs.complete(sLine, sDir, false, false) for i = 1, #tDirs do local sResult = tDirs[i] - if not tSeen[ sResult ] then - table.insert ( tResults, sResult ) - tSeen [ sResult ] = true + if not tSeen[sResult] then + table.insert (tResults, sResult) + tSeen [sResult] = true end end @@ -328,48 +328,48 @@ local function completeProgram( sLine ) local tPrograms = shell.programs() for n = 1, #tPrograms do local sProgram = tPrograms[n] - if #sProgram > #sLine and string.sub( sProgram, 1, #sLine ) == sLine then - local sResult = string.sub( sProgram, #sLine + 1 ) - if not tSeen[ sResult ] then - table.insert( tResults, sResult ) - tSeen[ sResult ] = true + if #sProgram > #sLine and string.sub(sProgram, 1, #sLine) == sLine then + local sResult = string.sub(sProgram, #sLine + 1) + if not tSeen[sResult] then + table.insert(tResults, sResult) + tSeen[sResult] = true end end end -- Sort and return - table.sort( tResults ) + table.sort(tResults) return tResults end end -local function completeProgramArgument( sProgram, nArgument, sPart, tPreviousParts ) - local tInfo = tCompletionInfo[ sProgram ] +local function completeProgramArgument(sProgram, nArgument, sPart, tPreviousParts) + local tInfo = tCompletionInfo[sProgram] if tInfo then - return tInfo.fnComplete( shell, nArgument, sPart, tPreviousParts ) + return tInfo.fnComplete(shell, nArgument, sPart, tPreviousParts) end return nil end -function shell.complete( sLine ) +function shell.complete(sLine) expect(1, sLine, "string") if #sLine > 0 then - local tWords = tokenise( sLine ) + local tWords = tokenise(sLine) local nIndex = #tWords - if string.sub( sLine, #sLine, #sLine ) == " " then + if string.sub(sLine, #sLine, #sLine) == " " then nIndex = nIndex + 1 end if nIndex == 1 then local sBit = tWords[1] or "" - local sPath = shell.resolveProgram( sBit ) - if tCompletionInfo[ sPath ] then + local sPath = shell.resolveProgram(sBit) + if tCompletionInfo[sPath] then return { " " } else - local tResults = completeProgram( sBit ) + local tResults = completeProgram(sBit) for n = 1, #tResults do local sResult = tResults[n] - local sPath = shell.resolveProgram( sBit .. sResult ) - if tCompletionInfo[ sPath ] then + local sPath = shell.resolveProgram(sBit .. sResult) + if tCompletionInfo[sPath] then tResults[n] = sResult .. " " end end @@ -377,26 +377,26 @@ function shell.complete( sLine ) end elseif nIndex > 1 then - local sPath = shell.resolveProgram( tWords[1] ) + local sPath = shell.resolveProgram(tWords[1]) local sPart = tWords[nIndex] or "" local tPreviousParts = tWords tPreviousParts[nIndex] = nil - return completeProgramArgument( sPath , nIndex - 1, sPart, tPreviousParts ) + return completeProgramArgument(sPath , nIndex - 1, sPart, tPreviousParts) end end return nil end -function shell.completeProgram( sProgram ) +function shell.completeProgram(sProgram) expect(1, sProgram, "string") - return completeProgram( sProgram ) + return completeProgram(sProgram) end -function shell.setCompletionFunction( sProgram, fnComplete ) +function shell.setCompletionFunction(sProgram, fnComplete) expect(1, sProgram, "string") expect(2, fnComplete, "function") - tCompletionInfo[ sProgram ] = { + tCompletionInfo[sProgram] = { fnComplete = fnComplete, } end @@ -412,45 +412,45 @@ function shell.getRunningProgram() return nil end -function shell.setAlias( _sCommand, _sProgram ) +function shell.setAlias(_sCommand, _sProgram) expect(1, _sCommand, "string") expect(2, _sProgram, "string") - tAliases[ _sCommand ] = _sProgram + tAliases[_sCommand] = _sProgram end -function shell.clearAlias( _sCommand ) +function shell.clearAlias(_sCommand) expect(1, _sCommand, "string") - tAliases[ _sCommand ] = nil + tAliases[_sCommand] = nil end function shell.aliases() -- Copy aliases local tCopy = {} - for sAlias, sCommand in pairs( tAliases ) do + for sAlias, sCommand in pairs(tAliases) do tCopy[sAlias] = sCommand end return tCopy end if multishell then - function shell.openTab( ... ) - local tWords = tokenise( ... ) + function shell.openTab(...) + local tWords = tokenise(...) local sCommand = tWords[1] if sCommand then - local sPath = shell.resolveProgram( sCommand ) + local sPath = shell.resolveProgram(sCommand) if sPath == "rom/programs/shell.lua" then - return multishell.launch( createShellEnv( "rom/programs" ), sPath, table.unpack( tWords, 2 ) ) + return multishell.launch(createShellEnv("rom/programs"), sPath, table.unpack(tWords, 2)) elseif sPath ~= nil then - return multishell.launch( createShellEnv( "rom/programs" ), "rom/programs/shell.lua", sCommand, table.unpack( tWords, 2 ) ) + return multishell.launch(createShellEnv("rom/programs"), "rom/programs/shell.lua", sCommand, table.unpack(tWords, 2)) else - printError( "No such program" ) + printError("No such program") end end end - function shell.switchTab( nID ) + function shell.switchTab(nID) expect(1, nID, "number") - multishell.setFocus( nID ) + multishell.setFocus(nID) end end @@ -458,40 +458,40 @@ local tArgs = { ... } if #tArgs > 0 then -- "shell x y z" -- Run the program specified on the commandline - shell.run( ... ) + shell.run(...) else -- "shell" -- Print the header - term.setBackgroundColor( bgColour ) - term.setTextColour( promptColour ) - print( os.version() ) - term.setTextColour( textColour ) + term.setBackgroundColor(bgColour) + term.setTextColour(promptColour) + print(os.version()) + term.setTextColour(textColour) -- Run the startup program if parentShell == nil then - shell.run( "/rom/startup.lua" ) + shell.run("/rom/startup.lua") end -- Read commands and execute them local tCommandHistory = {} while not bExit do - term.redirect( parentTerm ) - term.setBackgroundColor( bgColour ) - term.setTextColour( promptColour ) - write( shell.dir() .. "> " ) - term.setTextColour( textColour ) + term.redirect(parentTerm) + term.setBackgroundColor(bgColour) + term.setTextColour(promptColour) + write(shell.dir() .. "> ") + term.setTextColour(textColour) local sLine - if settings.get( "shell.autocomplete" ) then - sLine = read( nil, tCommandHistory, shell.complete ) + if settings.get("shell.autocomplete") then + sLine = read(nil, tCommandHistory, shell.complete) else - sLine = read( nil, tCommandHistory ) + sLine = read(nil, tCommandHistory) end if sLine:match("%S") and tCommandHistory[#tCommandHistory] ~= sLine then - table.insert( tCommandHistory, sLine ) + table.insert(tCommandHistory, sLine) end - shell.run( sLine ) + shell.run(sLine) end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/shutdown.lua b/src/main/resources/assets/computercraft/lua/rom/programs/shutdown.lua index 052409eeb..b895c571f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/shutdown.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/shutdown.lua @@ -1,8 +1,8 @@ if term.isColour() then - term.setTextColour( colours.yellow ) + term.setTextColour(colours.yellow) end -print( "Goodbye" ) -term.setTextColour( colours.white ) +print("Goodbye") +term.setTextColour(colours.white) -sleep( 1 ) +sleep(1) os.shutdown() diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/time.lua b/src/main/resources/assets/computercraft/lua/rom/programs/time.lua index ccd83d4ec..988846013 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/time.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/time.lua @@ -1,3 +1,3 @@ local nTime = os.time() local nDay = os.day() -print( "The time is " .. textutils.formatTime( nTime, false ) .. " on Day " .. nDay ) +print("The time is " .. textutils.formatTime(nTime, false) .. " on Day " .. nDay) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/craft.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/craft.lua index 8b8923e71..65f93104b 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/craft.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/craft.lua @@ -1,26 +1,26 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end if not turtle.craft then - print( "Requires a Crafty Turtle" ) + print("Requires a Crafty Turtle") return end local tArgs = { ... } local nLimit = nil if #tArgs < 1 then - print( "Usage: craft [number]" ) + print("Usage: craft [number]") return else - nLimit = tonumber( tArgs[1] ) + nLimit = tonumber(tArgs[1]) end local nCrafted = 0 -local nOldCount = turtle.getItemCount( turtle.getSelectedSlot() ) -if turtle.craft( nLimit ) then - local nNewCount = turtle.getItemCount( turtle.getSelectedSlot() ) +local nOldCount = turtle.getItemCount(turtle.getSelectedSlot()) +if turtle.craft(nLimit) then + local nNewCount = turtle.getItemCount(turtle.getSelectedSlot()) if nOldCount <= nLimit then nCrafted = nNewCount else @@ -29,9 +29,9 @@ if turtle.craft( nLimit ) then end if nCrafted > 1 then - print( nCrafted .. " items crafted" ) + print(nCrafted .. " items crafted") elseif nCrafted == 1 then - print( "1 item crafted" ) + print("1 item crafted") else - print( "No items crafted" ) + print("No items crafted") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/dance.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/dance.lua index 696c15747..9d539fde4 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/dance.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/dance.lua @@ -1,5 +1,5 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") end local tMoves = { @@ -77,20 +77,20 @@ local tMoves = { end, } -textutils.slowWrite( "Preparing to get down." ) -textutils.slowPrint( "..", 0.75 ) +textutils.slowWrite("Preparing to get down.") +textutils.slowPrint("..", 0.75) local sAudio = nil -for _, sName in pairs( peripheral.getNames() ) do - if disk.hasAudio( sName ) then - disk.playAudio( sName ) - print( "Jamming to " .. disk.getAudioTitle( sName ) ) +for _, sName in pairs(peripheral.getNames()) do + if disk.hasAudio(sName) then + disk.playAudio(sName) + print("Jamming to " .. disk.getAudioTitle(sName)) sAudio = sName break end end -print( "Press any key to stop the groove" ) +print("Press any key to stop the groove") parallel.waitForAny( function() @@ -110,5 +110,5 @@ parallel.waitForAny( ) if sAudio then - disk.stopAudio( sAudio ) + disk.stopAudio(sAudio) end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/equip.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/equip.lua index 0a8f2a480..b69ef4c45 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/equip.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/equip.lua @@ -1,11 +1,11 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } local function printUsage() - print( "Usage: equip " ) + print("Usage: equip ") end if #tArgs ~= 2 then @@ -13,29 +13,29 @@ if #tArgs ~= 2 then return end -local function equip( nSlot, fnEquipFunction ) - turtle.select( nSlot ) - local nOldCount = turtle.getItemCount( nSlot ) +local function equip(nSlot, fnEquipFunction) + turtle.select(nSlot) + local nOldCount = turtle.getItemCount(nSlot) if nOldCount == 0 then - print( "Nothing to equip" ) + print("Nothing to equip") elseif fnEquipFunction() then - local nNewCount = turtle.getItemCount( nSlot ) + local nNewCount = turtle.getItemCount(nSlot) if nNewCount > 0 then - print( "Items swapped" ) + print("Items swapped") else - print( "Item equipped" ) + print("Item equipped") end else - print( "Item not equippable" ) + print("Item not equippable") end end -local nSlot = tonumber( tArgs[1] ) +local nSlot = tonumber(tArgs[1]) local sSide = tArgs[2] if sSide == "left" then - equip( nSlot, turtle.equipLeft ) + equip(nSlot, turtle.equipLeft) elseif sSide == "right" then - equip( nSlot, turtle.equipRight ) + equip(nSlot, turtle.equipRight) else printUsage() return diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/excavate.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/excavate.lua index 2edc5b1fa..9d4313eb1 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/excavate.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/excavate.lua @@ -1,18 +1,18 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } if #tArgs ~= 1 then - print( "Usage: excavate " ) + print("Usage: excavate ") return end -- Mine in a quarry pattern until we hit something we can't dig -local size = tonumber( tArgs[1] ) +local size = tonumber(tArgs[1]) if size < 1 then - print( "Excavate diameter must be positive" ) + print("Excavate diameter must be positive") return end @@ -26,8 +26,8 @@ local xDir, zDir = 0, 1 local goTo -- Filled in further down local refuel -- Filled in further down -local function unload( _bKeepOneFuelStack ) - print( "Unloading items..." ) +local function unload(_bKeepOneFuelStack) + print("Unloading items...") for n = 1, 16 do local nCount = turtle.getItemCount(n) if nCount > 0 then @@ -49,22 +49,22 @@ end local function returnSupplies() local x, y, z, xd, zd = xPos, depth, zPos, xDir, zDir - print( "Returning to surface..." ) - goTo( 0, 0, 0, 0, -1 ) + print("Returning to surface...") + goTo(0, 0, 0, 0, -1) local fuelNeeded = 2 * (x + y + z) + 1 - if not refuel( fuelNeeded ) then - unload( true ) - print( "Waiting for fuel" ) - while not refuel( fuelNeeded ) do - os.pullEvent( "turtle_inventory" ) + if not refuel(fuelNeeded) then + unload(true) + print("Waiting for fuel") + while not refuel(fuelNeeded) do + os.pullEvent("turtle_inventory") end else - unload( true ) + unload(true) end - print( "Resuming mining..." ) - goTo( x, y, z, xd, zd ) + print("Resuming mining...") + goTo(x, y, z, xd, zd) end local function collect() @@ -81,18 +81,18 @@ local function collect() if nTotalItems > collected then collected = nTotalItems if math.fmod(collected + unloaded, 50) == 0 then - print( "Mined " .. collected + unloaded .. " items." ) + print("Mined " .. collected + unloaded .. " items.") end end if bFull then - print( "No empty slots left." ) + print("No empty slots left.") return false end return true end -function refuel( ammount ) +function refuel(ammount) local fuelLevel = turtle.getFuelLevel() if fuelLevel == "unlimited" then return true @@ -123,7 +123,7 @@ end local function tryForwards() if not refuel() then - print( "Not enough Fuel" ) + print("Not enough Fuel") returnSupplies() end @@ -141,7 +141,7 @@ local function tryForwards() returnSupplies() end else - sleep( 0.5 ) + sleep(0.5) end end @@ -152,7 +152,7 @@ end local function tryDown() if not refuel() then - print( "Not enough Fuel" ) + print("Not enough Fuel") returnSupplies() end @@ -170,13 +170,13 @@ local function tryDown() returnSupplies() end else - sleep( 0.5 ) + sleep(0.5) end end depth = depth + 1 - if math.fmod( depth, 10 ) == 0 then - print( "Descended " .. depth .. " metres." ) + if math.fmod(depth, 10) == 0 then + print("Descended " .. depth .. " metres.") end return true @@ -192,14 +192,14 @@ local function turnRight() xDir, zDir = zDir, -xDir end -function goTo( x, y, z, xd, zd ) +function goTo(x, y, z, xd, zd) while depth > y do if turtle.up() then depth = depth - 1 elseif turtle.digUp() or turtle.attackUp() then collect() else - sleep( 0.5 ) + sleep(0.5) end end @@ -213,7 +213,7 @@ function goTo( x, y, z, xd, zd ) elseif turtle.dig() or turtle.attack() then collect() else - sleep( 0.5 ) + sleep(0.5) end end elseif xPos < x then @@ -226,7 +226,7 @@ function goTo( x, y, z, xd, zd ) elseif turtle.dig() or turtle.attack() then collect() else - sleep( 0.5 ) + sleep(0.5) end end end @@ -241,7 +241,7 @@ function goTo( x, y, z, xd, zd ) elseif turtle.dig() or turtle.attack() then collect() else - sleep( 0.5 ) + sleep(0.5) end end elseif zPos < z then @@ -254,7 +254,7 @@ function goTo( x, y, z, xd, zd ) elseif turtle.dig() or turtle.attack() then collect() else - sleep( 0.5 ) + sleep(0.5) end end end @@ -265,7 +265,7 @@ function goTo( x, y, z, xd, zd ) elseif turtle.digDown() or turtle.attackDown() then collect() else - sleep( 0.5 ) + sleep(0.5) end end @@ -275,11 +275,11 @@ function goTo( x, y, z, xd, zd ) end if not refuel() then - print( "Out of Fuel" ) + print("Out of Fuel") return end -print( "Excavating..." ) +print("Excavating...") local reseal = false turtle.select(1) @@ -341,16 +341,16 @@ while not done do end end -print( "Returning to surface..." ) +print("Returning to surface...") -- Return to where we started -goTo( 0, 0, 0, 0, -1 ) -unload( false ) -goTo( 0, 0, 0, 0, 1 ) +goTo(0, 0, 0, 0, -1) +unload(false) +goTo(0, 0, 0, 0, 1) -- Seal the hole if reseal then turtle.placeDown() end -print( "Mined " .. collected + unloaded .. " items total." ) +print("Mined " .. collected + unloaded .. " items total.") diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/go.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/go.lua index c8b43b0e9..9c6de67ce 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/go.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/go.lua @@ -1,11 +1,11 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } if #tArgs < 1 then - print( "Usage: go " ) + print("Usage: go ") return end @@ -29,7 +29,7 @@ while nArg <= #tArgs do local sDirection = tArgs[nArg] local nDistance = 1 if nArg < #tArgs then - local num = tonumber( tArgs[nArg + 1] ) + local num = tonumber(tArgs[nArg + 1]) if num then nDistance = num nArg = nArg + 1 @@ -43,15 +43,15 @@ while nArg <= #tArgs do if fnHandler() then nDistance = nDistance - 1 elseif turtle.getFuelLevel() == 0 then - print( "Out of fuel" ) + print("Out of fuel") return else sleep(0.5) end end else - print( "No such direction: " .. sDirection ) - print( "Try: forward, back, up, down" ) + print("No such direction: " .. sDirection) + print("Try: forward, back, up, down") return end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/refuel.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/refuel.lua index 77f796196..d9dd5b002 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/refuel.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/refuel.lua @@ -1,18 +1,18 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } local nLimit = 1 if #tArgs > 1 then - print( "Usage: refuel [number]" ) + print("Usage: refuel [number]") return elseif #tArgs > 0 then if tArgs[1] == "all" then nLimit = nil else - nLimit = tonumber( tArgs[1] ) + nLimit = tonumber(tArgs[1]) if not nLimit then print("Invalid limit, expected a number or \"all\"") return @@ -29,17 +29,17 @@ if turtle.getFuelLevel() ~= "unlimited" then local nCount = turtle.getItemCount(n) if nCount > 0 then - turtle.select( n ) - if turtle.refuel( nLimit ) and nLimit then + turtle.select(n) + if turtle.refuel(nLimit) and nLimit then local nNewCount = turtle.getItemCount(n) nLimit = nLimit - (nCount - nNewCount) end end end - print( "Fuel level is " .. turtle.getFuelLevel() ) + print("Fuel level is " .. turtle.getFuelLevel()) if turtle.getFuelLevel() == turtle.getFuelLimit() then - print( "Fuel limit reached" ) + print("Fuel limit reached") end else - print( "Fuel level is unlimited" ) + print("Fuel level is unlimited") end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/tunnel.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/tunnel.lua index 50183d013..34c0807d9 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/tunnel.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/tunnel.lua @@ -1,18 +1,18 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } if #tArgs ~= 1 then - print( "Usage: tunnel " ) + print("Usage: tunnel ") return end -- Mine in a quarry pattern until we hit something we can't dig -local length = tonumber( tArgs[1] ) +local length = tonumber(tArgs[1]) if length < 1 then - print( "Tunnel length must be positive" ) + print("Tunnel length must be positive") return end local collected = 0 @@ -20,7 +20,7 @@ local collected = 0 local function collect() collected = collected + 1 if math.fmod(collected, 25) == 0 then - print( "Mined " .. collected .. " items." ) + print("Mined " .. collected .. " items.") end end @@ -81,11 +81,11 @@ local function refuel() end if not tryRefuel() then - print( "Add more fuel to continue." ) + print("Add more fuel to continue.") while not tryRefuel() do - os.pullEvent( "turtle_inventory" ) + os.pullEvent("turtle_inventory") end - print( "Resuming Tunnel." ) + print("Resuming Tunnel.") end end @@ -99,7 +99,7 @@ local function tryUp() elseif turtle.attackUp() then collect() else - sleep( 0.5 ) + sleep(0.5) end end return true @@ -115,7 +115,7 @@ local function tryDown() elseif turtle.attackDown() then collect() else - sleep( 0.5 ) + sleep(0.5) end end return true @@ -131,13 +131,13 @@ local function tryForward() elseif turtle.attack() then collect() else - sleep( 0.5 ) + sleep(0.5) end end return true end -print( "Tunnelling..." ) +print("Tunnelling...") for n = 1, length do turtle.placeDown() @@ -156,11 +156,11 @@ for n = 1, length do if n < length then tryDig() if not tryForward() then - print( "Aborting Tunnel." ) + print("Aborting Tunnel.") break end else - print( "Tunnel complete." ) + print("Tunnel complete.") end end @@ -182,5 +182,5 @@ turtle.turnRight() turtle.turnRight() ]] -print( "Tunnel complete." ) -print( "Mined " .. collected .. " items total." ) +print("Tunnel complete.") +print("Mined " .. collected .. " items total.") diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/turn.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/turn.lua index d34db5753..e3fbbcbd3 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/turn.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/turn.lua @@ -1,11 +1,11 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } if #tArgs < 1 then - print( "Usage: turn " ) + print("Usage: turn ") return end @@ -21,7 +21,7 @@ while nArg <= #tArgs do local sDirection = tArgs[nArg] local nDistance = 1 if nArg < #tArgs then - local num = tonumber( tArgs[nArg + 1] ) + local num = tonumber(tArgs[nArg + 1]) if num then nDistance = num nArg = nArg + 1 @@ -32,11 +32,11 @@ while nArg <= #tArgs do local fnHandler = tHandlers[string.lower(sDirection)] if fnHandler then for _ = 1, nDistance do - fnHandler( nArg ) + fnHandler(nArg) end else - print( "No such direction: " .. sDirection ) - print( "Try: left, right" ) + print("No such direction: " .. sDirection) + print("Try: left, right") return end end diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/unequip.lua b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/unequip.lua index e19672577..97501aa42 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/turtle/unequip.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/turtle/unequip.lua @@ -1,11 +1,11 @@ if not turtle then - printError( "Requires a Turtle" ) + printError("Requires a Turtle") return end local tArgs = { ... } local function printUsage() - print( "Usage: unequip " ) + print("Usage: unequip ") end if #tArgs ~= 1 then @@ -13,31 +13,31 @@ if #tArgs ~= 1 then return end -local function unequip( fnEquipFunction ) +local function unequip(fnEquipFunction) for nSlot = 1, 16 do - local nOldCount = turtle.getItemCount( nSlot ) + local nOldCount = turtle.getItemCount(nSlot) if nOldCount == 0 then - turtle.select( nSlot ) + turtle.select(nSlot) if fnEquipFunction() then - local nNewCount = turtle.getItemCount( nSlot ) + local nNewCount = turtle.getItemCount(nSlot) if nNewCount > 0 then - print( "Item unequipped" ) + print("Item unequipped") return else - print( "Nothing to unequip" ) + print("Nothing to unequip") return end end end end - print( "No space to unequip item" ) + print("No space to unequip item") end local sSide = tArgs[1] if sSide == "left" then - unequip( turtle.equipLeft ) + unequip(turtle.equipLeft) elseif sSide == "right" then - unequip( turtle.equipRight ) + unequip(turtle.equipRight) else printUsage() return diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/type.lua b/src/main/resources/assets/computercraft/lua/rom/programs/type.lua index c2fabbbaf..ec0ebc0c5 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/type.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/type.lua @@ -1,18 +1,18 @@ local tArgs = { ... } if #tArgs < 1 then - print( "Usage: type " ) + print("Usage: type ") return end -local sPath = shell.resolve( tArgs[1] ) -if fs.exists( sPath ) then - if fs.isDir( sPath ) then - print( "directory" ) +local sPath = shell.resolve(tArgs[1]) +if fs.exists(sPath) then + if fs.isDir(sPath) then + print("directory") else - print( "file" ) + print("file") end else - print( "No such path" ) + print("No such path") end diff --git a/src/main/resources/assets/computercraft/lua/rom/startup.lua b/src/main/resources/assets/computercraft/lua/rom/startup.lua index e19eb0317..1b3046092 100644 --- a/src/main/resources/assets/computercraft/lua/rom/startup.lua +++ b/src/main/resources/assets/computercraft/lua/rom/startup.lua @@ -22,130 +22,130 @@ end if http then sPath = sPath .. ":/rom/programs/http" end -shell.setPath( sPath ) -help.setPath( "/rom/help" ) +shell.setPath(sPath) +help.setPath("/rom/help") -- Setup aliases -shell.setAlias( "ls", "list" ) -shell.setAlias( "dir", "list" ) -shell.setAlias( "cp", "copy" ) -shell.setAlias( "mv", "move" ) -shell.setAlias( "rm", "delete" ) -shell.setAlias( "clr", "clear" ) -shell.setAlias( "rs", "redstone" ) -shell.setAlias( "sh", "shell" ) +shell.setAlias("ls", "list") +shell.setAlias("dir", "list") +shell.setAlias("cp", "copy") +shell.setAlias("mv", "move") +shell.setAlias("rm", "delete") +shell.setAlias("clr", "clear") +shell.setAlias("rs", "redstone") +shell.setAlias("sh", "shell") if term.isColor() then - shell.setAlias( "background", "bg" ) - shell.setAlias( "foreground", "fg" ) + shell.setAlias("background", "bg") + shell.setAlias("foreground", "fg") end -- Setup completion functions local function completePastebinPut(shell, text, previous) if previous[2] == "put" then - return fs.complete( text, shell.dir(), true, false ) + return fs.complete(text, shell.dir(), true, false) end end -shell.setCompletionFunction( "rom/programs/alias.lua", completion.build(nil, completion.program) ) -shell.setCompletionFunction( "rom/programs/cd.lua", completion.build(completion.dir) ) -shell.setCompletionFunction( "rom/programs/copy.lua", completion.build( +shell.setCompletionFunction("rom/programs/alias.lua", completion.build(nil, completion.program)) +shell.setCompletionFunction("rom/programs/cd.lua", completion.build(completion.dir)) +shell.setCompletionFunction("rom/programs/copy.lua", completion.build( { completion.dirOrFile, true }, completion.dirOrFile -) ) -shell.setCompletionFunction( "rom/programs/delete.lua", completion.build({ completion.dirOrFile, many = true }) ) -shell.setCompletionFunction( "rom/programs/drive.lua", completion.build(completion.dir) ) -shell.setCompletionFunction( "rom/programs/edit.lua", completion.build(completion.file) ) -shell.setCompletionFunction( "rom/programs/eject.lua", completion.build(completion.peripheral) ) -shell.setCompletionFunction( "rom/programs/gps.lua", completion.build({ completion.choice, { "host", "host ", "locate" } }) ) -shell.setCompletionFunction( "rom/programs/help.lua", completion.build(completion.help) ) -shell.setCompletionFunction( "rom/programs/id.lua", completion.build(completion.peripheral) ) -shell.setCompletionFunction( "rom/programs/label.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/delete.lua", completion.build({ completion.dirOrFile, many = true })) +shell.setCompletionFunction("rom/programs/drive.lua", completion.build(completion.dir)) +shell.setCompletionFunction("rom/programs/edit.lua", completion.build(completion.file)) +shell.setCompletionFunction("rom/programs/eject.lua", completion.build(completion.peripheral)) +shell.setCompletionFunction("rom/programs/gps.lua", completion.build({ completion.choice, { "host", "host ", "locate" } })) +shell.setCompletionFunction("rom/programs/help.lua", completion.build(completion.help)) +shell.setCompletionFunction("rom/programs/id.lua", completion.build(completion.peripheral)) +shell.setCompletionFunction("rom/programs/label.lua", completion.build( { completion.choice, { "get", "get ", "set ", "clear", "clear " } }, completion.peripheral -) ) -shell.setCompletionFunction( "rom/programs/list.lua", completion.build(completion.dir) ) -shell.setCompletionFunction( "rom/programs/mkdir.lua", completion.build({ completion.dir, many = true }) ) -shell.setCompletionFunction( "rom/programs/monitor.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/list.lua", completion.build(completion.dir)) +shell.setCompletionFunction("rom/programs/mkdir.lua", completion.build({ completion.dir, many = true })) +shell.setCompletionFunction("rom/programs/monitor.lua", completion.build( { completion.peripheral, true }, completion.program -) ) -shell.setCompletionFunction( "rom/programs/move.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/move.lua", completion.build( { completion.dirOrFile, true }, completion.dirOrFile -) ) -shell.setCompletionFunction( "rom/programs/redstone.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/redstone.lua", completion.build( { completion.choice, { "probe", "set ", "pulse " } }, completion.side -) ) -shell.setCompletionFunction( "rom/programs/rename.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/rename.lua", completion.build( { completion.dirOrFile, true }, completion.dirOrFile -) ) -shell.setCompletionFunction( "rom/programs/shell.lua", completion.build(completion.program) ) -shell.setCompletionFunction( "rom/programs/type.lua", completion.build(completion.dirOrFile) ) -shell.setCompletionFunction( "rom/programs/set.lua", completion.build({ completion.setting, true }) ) -shell.setCompletionFunction( "rom/programs/advanced/bg.lua", completion.build(completion.program) ) -shell.setCompletionFunction( "rom/programs/advanced/fg.lua", completion.build(completion.program) ) -shell.setCompletionFunction( "rom/programs/fun/dj.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/shell.lua", completion.build(completion.program)) +shell.setCompletionFunction("rom/programs/type.lua", completion.build(completion.dirOrFile)) +shell.setCompletionFunction("rom/programs/set.lua", completion.build({ completion.setting, true })) +shell.setCompletionFunction("rom/programs/advanced/bg.lua", completion.build(completion.program)) +shell.setCompletionFunction("rom/programs/advanced/fg.lua", completion.build(completion.program)) +shell.setCompletionFunction("rom/programs/fun/dj.lua", completion.build( { completion.choice, { "play", "play ", "stop " } }, completion.peripheral -) ) -shell.setCompletionFunction( "rom/programs/fun/advanced/paint.lua", completion.build(completion.file) ) -shell.setCompletionFunction( "rom/programs/http/pastebin.lua", completion.build( +)) +shell.setCompletionFunction("rom/programs/fun/advanced/paint.lua", completion.build(completion.file)) +shell.setCompletionFunction("rom/programs/http/pastebin.lua", completion.build( { completion.choice, { "put ", "get ", "run " } }, completePastebinPut -) ) -shell.setCompletionFunction( "rom/programs/rednet/chat.lua", completion.build({ completion.choice, { "host ", "join " } }) ) -shell.setCompletionFunction( "rom/programs/command/exec.lua", completion.build(completion.command) ) -shell.setCompletionFunction( "rom/programs/http/wget.lua", completion.build({ completion.choice, { "run " } }) ) +)) +shell.setCompletionFunction("rom/programs/rednet/chat.lua", completion.build({ completion.choice, { "host ", "join " } })) +shell.setCompletionFunction("rom/programs/command/exec.lua", completion.build(completion.command)) +shell.setCompletionFunction("rom/programs/http/wget.lua", completion.build({ completion.choice, { "run " } })) if turtle then - shell.setCompletionFunction( "rom/programs/turtle/go.lua", completion.build( + shell.setCompletionFunction("rom/programs/turtle/go.lua", completion.build( { completion.choice, { "left", "right", "forward", "back", "down", "up" }, true, many = true } - ) ) - shell.setCompletionFunction( "rom/programs/turtle/turn.lua", completion.build( + )) + shell.setCompletionFunction("rom/programs/turtle/turn.lua", completion.build( { completion.choice, { "left", "right" }, true, many = true } - ) ) - shell.setCompletionFunction( "rom/programs/turtle/equip.lua", completion.build( + )) + shell.setCompletionFunction("rom/programs/turtle/equip.lua", completion.build( nil, { completion.choice, { "left", "right" } } - ) ) - shell.setCompletionFunction( "rom/programs/turtle/unequip.lua", completion.build( + )) + shell.setCompletionFunction("rom/programs/turtle/unequip.lua", completion.build( { completion.choice, { "left", "right" } } - ) ) + )) end -- Run autorun files -if fs.exists( "/rom/autorun" ) and fs.isDir( "/rom/autorun" ) then - local tFiles = fs.list( "/rom/autorun" ) - for _, sFile in ipairs( tFiles ) do - if string.sub( sFile, 1, 1 ) ~= "." then +if fs.exists("/rom/autorun") and fs.isDir("/rom/autorun") then + local tFiles = fs.list("/rom/autorun") + for _, sFile in ipairs(tFiles) do + if string.sub(sFile, 1, 1) ~= "." then local sPath = "/rom/autorun/" .. sFile - if not fs.isDir( sPath ) then - shell.run( sPath ) + if not fs.isDir(sPath) then + shell.run(sPath) end end end end -local function findStartups( sBaseDir ) +local function findStartups(sBaseDir) local tStartups = nil - local sBasePath = "/" .. fs.combine( sBaseDir, "startup" ) - local sStartupNode = shell.resolveProgram( sBasePath ) + local sBasePath = "/" .. fs.combine(sBaseDir, "startup") + local sStartupNode = shell.resolveProgram(sBasePath) if sStartupNode then tStartups = { sStartupNode } end -- It's possible that there is a startup directory and a startup.lua file, so this has to be -- executed even if a file has already been found. - if fs.isDir( sBasePath ) then + if fs.isDir(sBasePath) then if tStartups == nil then tStartups = {} end - for _, v in pairs( fs.list( sBasePath ) ) do - local sPath = "/" .. fs.combine( sBasePath, v ) - if not fs.isDir( sPath ) then - tStartups[ #tStartups + 1 ] = sPath + for _, v in pairs(fs.list(sBasePath)) do + local sPath = "/" .. fs.combine(sBasePath, v) + if not fs.isDir(sPath) then + tStartups[#tStartups + 1] = sPath end end end @@ -153,19 +153,19 @@ local function findStartups( sBaseDir ) end -- Show MOTD -if settings.get( "motd.enable" ) then - shell.run( "motd" ) +if settings.get("motd.enable") then + shell.run("motd") end -- Run the user created startup, either from disk drives or the root local tUserStartups = nil -if settings.get( "shell.allow_startup" ) then - tUserStartups = findStartups( "/" ) +if settings.get("shell.allow_startup") then + tUserStartups = findStartups("/") end -if settings.get( "shell.allow_disk_startup" ) then - for _, sName in pairs( peripheral.getNames() ) do - if disk.isPresent( sName ) and disk.hasData( sName ) then - local startups = findStartups( disk.getMountPath( sName ) ) +if settings.get("shell.allow_disk_startup") then + for _, sName in pairs(peripheral.getNames()) do + if disk.isPresent(sName) and disk.hasData(sName) then + local startups = findStartups(disk.getMountPath(sName)) if startups then tUserStartups = startups break @@ -174,7 +174,7 @@ if settings.get( "shell.allow_disk_startup" ) then end end if tUserStartups then - for _, v in pairs( tUserStartups ) do - shell.run( v ) + for _, v in pairs(tUserStartups) do + shell.run(v) end end diff --git a/src/test/resources/test-rom/mcfly.lua b/src/test/resources/test-rom/mcfly.lua index ecb01eaac..81e5afb3c 100644 --- a/src/test/resources/test-rom/mcfly.lua +++ b/src/test/resources/test-rom/mcfly.lua @@ -238,8 +238,8 @@ local function matches(eq, exact, left, right) -- If we've already explored/are exploring the left and right then return if eq[left] and eq[left][right] then return true end - if not eq[left] then eq[left] = {[right] = true} else eq[left][right] = true end - if not eq[right] then eq[right] = {[left] = true} else eq[right][left] = true end + if not eq[left] then eq[left] = { [right] = true } else eq[left][right] = true end + if not eq[right] then eq[right] = { [left] = true } else eq[right][left] = true end -- Verify all pairs in left are equal to those in right for k, v in pairs(left) do diff --git a/src/test/resources/test-rom/spec/programs/command/exec_spec.lua b/src/test/resources/test-rom/spec/programs/command/exec_spec.lua index 0c12b18a7..ba714629f 100644 --- a/src/test/resources/test-rom/spec/programs/command/exec_spec.lua +++ b/src/test/resources/test-rom/spec/programs/command/exec_spec.lua @@ -15,7 +15,7 @@ describe("The exec program", function() it("runs a command", function() stub(_G, "commands", { - exec = function() return true, {"Hello World!"} end, + exec = function() return true, { "Hello World!" } end, }) expect(capture(stub, "/rom/programs/command/exec.lua computercraft")) @@ -24,7 +24,7 @@ describe("The exec program", function() it("reports command failures", function() stub(_G, "commands", { - exec = function() return false, {"Hello World!"} end, + exec = function() return false, { "Hello World!" } end, }) expect(capture(stub, "/rom/programs/command/exec.lua computercraft")) diff --git a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua index 61bfe833b..192ba7007 100644 --- a/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua +++ b/src/test/resources/test-rom/spec/programs/http/pastebin_spec.lua @@ -49,7 +49,7 @@ describe("The pastebin program", function() it("upload a program to pastebin", function() setup_request() - local file = fs.open( "testup", "w" ) + local file = fs.open("testup", "w") file.close() expect(capture(stub, "pastebin", "put", "testup")) From 10a27a7a250eea160a8c172e63d8153c792bb53b Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sat, 18 Apr 2020 14:38:20 +0100 Subject: [PATCH 25/35] Add utility to check table keys expect.field acts very similarly to expect.expect, though checks a specific table key rather than a function argument. --- .../lua/rom/modules/main/cc/expect.lua | 61 +++++++++++++----- .../test-rom/spec/modules/cc/expect_spec.lua | 62 +++++++++++++------ 2 files changed, 87 insertions(+), 36 deletions(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua index 2f4c648b6..24ca66d56 100644 --- a/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua +++ b/src/main/resources/assets/computercraft/lua/rom/modules/main/cc/expect.lua @@ -5,28 +5,29 @@ local native_select, native_type = select, type ---- Expect an argument to have a specific type. --- --- @tparam number index The 1-based argument index. --- @param value The argument's value. --- @tparam string ... The allowed types of the argument. --- @throws If the value is not one of the allowed types. -local function expect(index, value, ...) - local t = native_type(value) - for i = 1, native_select("#", ...) do - if t == native_select(i, ...) then return true end - end - +local function get_type_names(...) local types = table.pack(...) for i = types.n, 1, -1 do if types[i] == "nil" then table.remove(types, i) end end - local type_names if #types <= 1 then - type_names = tostring(...) + return tostring(...) else - type_names = table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types] + return table.concat(types, ", ", 1, #types - 1) .. " or " .. types[#types] + end +end +--- Expect an argument to have a specific type. +-- +-- @tparam number index The 1-based argument index. +-- @param value The argument's value. +-- @tparam string ... The allowed types of the argument. +-- @return The given `value`. +-- @throws If the value is not one of the allowed types. +local function expect(index, value, ...) + local t = native_type(value) + for i = 1, native_select("#", ...) do + if t == native_select(i, ...) then return value end end -- If we can determine the function name with a high level of confidence, try to include it. @@ -36,6 +37,7 @@ local function expect(index, value, ...) if ok and info.name and #info.name ~= "" and info.what ~= "C" then name = info.name end end + local type_names = get_type_names(...) if name then error(("bad argument #%d to '%s' (expected %s, got %s)"):format(index, name, type_names, t), 3) else @@ -43,4 +45,31 @@ local function expect(index, value, ...) end end -return { expect = expect } +--- Expect an field to have a specific type. +-- +-- @tparam table tbl The table to index. +-- @tparam string index The field name to check. +-- @tparam string ... The allowed types of the argument. +-- @return The contents of the given field. +-- @throws If the field is not one of the allowed types. +local function field(tbl, index, ...) + expect(1, tbl, "table") + expect(2, index, "string") + + local value = tbl[index] + local t = native_type(value) + for i = 1, native_select("#", ...) do + if t == native_select(i, ...) then return value end + end + + if value == nil then + error(("field '%s' missing from table"):format(index), 3) + else + error(("bad field '%s' (expected %s, got %s)"):format(index, get_type_names(...), t), 3) + end +end + +return { + expect = expect, + field = field, +} diff --git a/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua b/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua index 36f076f60..125e43640 100644 --- a/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua +++ b/src/test/resources/test-rom/spec/modules/cc/expect_spec.lua @@ -1,31 +1,53 @@ describe("cc.expect", function() local e = require("cc.expect") - it("checks a single type", function() - expect(e.expect(1, "test", "string")):eq(true) - expect(e.expect(1, 2, "number")):eq(true) + describe("expect", function() + it("checks a single type", function() + expect(e.expect(1, "test", "string")):eq("test") + expect(e.expect(1, 2, "number")):eq(2) - expect.error(e.expect, 1, nil, "string"):eq("bad argument #1 (expected string, got nil)") - expect.error(e.expect, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)") + expect.error(e.expect, 1, nil, "string"):eq("bad argument #1 (expected string, got nil)") + expect.error(e.expect, 2, 1, "nil"):eq("bad argument #2 (expected nil, got number)") + end) + + it("checks multiple types", function() + expect(e.expect(1, "test", "string", "number")):eq("test") + expect(e.expect(1, 2, "string", "number")):eq(2) + + expect.error(e.expect, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)") + expect.error(e.expect, 2, false, "string", "table", "number", "nil") + :eq("bad argument #2 (expected string, table or number, got boolean)") + end) + + it("includes the function name", function() + local function worker() + expect(e.expect(1, nil, "string")):eq(true) + end + local function trampoline() + worker() + end + + expect.error(trampoline):eq("expect_spec.lua:27: bad argument #1 to 'worker' (expected string, got nil)") + end) end) - it("checks multiple types", function() - expect(e.expect(1, "test", "string", "number")):eq(true) - expect(e.expect(1, 2, "string", "number")):eq(true) + describe("field", function() + it("checks a single type", function() + expect(e.field({ k = "test" }, "k", "string")):eq("test") + expect(e.field({ k = 2 }, "k", "number")):eq(2) - expect.error(e.expect, 1, nil, "string", "number"):eq("bad argument #1 (expected string or number, got nil)") - expect.error(e.expect, 2, false, "string", "table", "number", "nil") - :eq("bad argument #2 (expected string, table or number, got boolean)") - end) + expect.error(e.field, { k = nil }, "k", "string"):eq("field 'k' missing from table") + expect.error(e.field, { l = 1 }, "l", "nil"):eq("bad field 'l' (expected nil, got number)") + end) - it("includes the function name", function() - local function worker() - expect(e.expect(1, nil, "string")):eq(true) - end - local function trampoline() - worker() - end + it("checks multiple types", function() + expect(e.field({ k = "test" }, "k", "string", "number")):eq("test") + expect(e.field({ k = 2 }, "k", "string", "number")):eq(2) - expect.error(trampoline):eq("expect_spec.lua:26: bad argument #1 to 'worker' (expected string, got nil)") + expect.error(e.field, { k = nil }, "k", "string", "number") + :eq("field 'k' missing from table") + expect.error(e.field, { l = false }, "l", "string", "table", "number", "nil") + :eq("bad field 'l' (expected string, table or number, got boolean)") + end) end) end) From eead8b575569be841f535cc2e44c9f9f2e671c18 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sat, 18 Apr 2020 21:42:59 +0100 Subject: [PATCH 26/35] A small amount of Lua documentation I'd forgotten how tedious this was. I can't say any of these docs are especially good, but it's something. --- doc/stub/http.lua | 22 +- doc/stub/turtle.lua | 226 ++++++++++++++++-- .../computercraft/lua/rom/apis/peripheral.lua | 6 +- .../lua/rom/apis/turtle/turtle.lua | 2 +- .../computercraft/lua/rom/apis/window.lua | 95 +++++--- 5 files changed, 297 insertions(+), 54 deletions(-) diff --git a/doc/stub/http.lua b/doc/stub/http.lua index 8e7df608f..8e8a24c08 100644 --- a/doc/stub/http.lua +++ b/doc/stub/http.lua @@ -199,8 +199,6 @@ function websocket(url, headers) end -- of the initial websocket connection. function websocketAsync(url, headers) end - - --- A websocket, which can be used to send an receive messages with a web -- server. -- @@ -208,6 +206,24 @@ function websocketAsync(url, headers) end -- @see http.websocket On how to open a websocket. local Websocket = {} +--- Send a websocket message to the connected server. +-- +-- @tparam string message The message to send. +-- @tparam[opt] boolean binary Whether this message should be treated as a +-- binary string, rather than encoded text. +-- @throws If the websocket has been closed. function Websocket.send(message, binary) end -function Websocket.receive() end + +--- Wait for a message from the server. +-- +-- @tparam[opt] number timeout The number of seconds to wait if no message is +-- received. +-- @treturn[1] string The received message. +-- @treturn boolean If this was a binary message. +-- @treturn[2] nil If the websocket was closed while waiting, or if we timed out. +-- @throws If the websocket has been closed. +function Websocket.receive(timeout) end + +--- Close this websocket. This will terminate the connection, meaning messages +-- can no longer be sent or received along it. function Websocket.close() end diff --git a/doc/stub/turtle.lua b/doc/stub/turtle.lua index 0baa4c6fa..7fabd2a8a 100644 --- a/doc/stub/turtle.lua +++ b/doc/stub/turtle.lua @@ -1,42 +1,230 @@ +--- Move the turtle forward one block. +-- @treturn boolean Whether the turtle could successfully move. +-- @treturn string|nil The reason the turtle could not move. function forward() end + +--- Move the turtle backwards one block. +-- @treturn boolean Whether the turtle could successfully move. +-- @treturn string|nil The reason the turtle could not move. function back() end + +--- Move the turtle up one block. +-- @treturn boolean Whether the turtle could successfully move. +-- @treturn string|nil The reason the turtle could not move. function up() end + +--- Move the turtle down one block. +-- @treturn boolean Whether the turtle could successfully move. +-- @treturn string|nil The reason the turtle could not move. function down() end + +--- Rotate the turtle 90 degress to the left. function turnLeft() end + +--- Rotate the turtle 90 degress to the right. function turnRight() end + +--- Attempt to break the block in front of the turtle. +-- +-- This requires a turtle tool capable of breaking the block. Diamond pickaxes +-- (mining turtles) can break any vanilla block, but other tools (such as axes) +-- are more limited. +-- +-- @tparam[opt] "left"|"right" side The specific tool to use. +-- @treturn boolean Whether a block was broken. +-- @treturn string|nil The reason no block was broken. function dig(side) end + +--- Attempt to break the block above the turtle. See @{dig} for full details. +-- +-- @tparam[opt] "left"|"right" side The specific tool to use. +-- @treturn boolean Whether a block was broken. +-- @treturn string|nil The reason no block was broken. function digUp(side) end + +--- Attempt to break the block below the turtle. See @{dig} for full details. +-- +-- @tparam[opt] "left"|"right" side The specific tool to use. +-- @treturn boolean Whether a block was broken. +-- @treturn string|nil The reason no block was broken. function digDown(side) end + +--- Attack the entity in front of the turtle. +-- +-- @tparam[opt] "left"|"right" side The specific tool to use. +-- @treturn boolean Whether an entity was attacked. +-- @treturn string|nil The reason nothing was attacked. +function attack(side) end + +--- Attack the entity above the turtle. +-- +-- @tparam[opt] "left"|"right" side The specific tool to use. +-- @treturn boolean Whether an entity was attacked. +-- @treturn string|nil The reason nothing was attacked. +function attackUp(side) end + +--- Attack the entity below the turtle. +-- +-- @tparam[opt] "left"|"right" side The specific tool to use. +-- @treturn boolean Whether an entity was attacked. +-- @treturn string|nil The reason nothing was attacked. +function attackDown(side) end + +--- Place a block or item into the world in front of the turtle. +-- +-- @treturn boolean Whether the block could be placed. +-- @treturn string|nil The reason the block was not placed. function place() end + +--- Place a block or item into the world above the turtle. +-- +-- @treturn boolean Whether the block could be placed. +-- @treturn string|nil The reason the block was not placed. function placeUp() end + +--- Place a block or item into the world below the turtle. +-- +-- @treturn boolean Whether the block could be placed. +-- @treturn string|nil The reason the block was not placed. function placeDown() end + +--- Drop the currently selected stack into the inventory in front of the turtle, +-- or as an item into the world if there is no inventory. +-- +-- @tparam[opt] number count The number of items to drop. If not given, the +-- entire stack will be dropped. +-- @treturn boolean Whether items were dropped. +-- @treturn string|nil The reason the no items were dropped. +-- @see select function drop(count) end -function select(slot) end -function getItemCount(slot) end -function getItemSpace(slot) end + +--- Drop the currently selected stack into the inventory above the turtle, or as +-- an item into the world if there is no inventory. +-- +-- @tparam[opt] number count The number of items to drop. If not given, the +-- entire stack will be dropped. +-- @treturn boolean Whether items were dropped. +-- @treturn string|nil The reason the no items were dropped. +-- @see select +function dropUp(count) end + +--- Drop the currently selected stack into the inventory below the turtle, or as +-- an item into the world if there is no inventory. +-- +-- @tparam[opt] number count The number of items to drop. If not given, the +-- entire stack will be dropped. +-- @treturn boolean Whether items were dropped. +-- @treturn string|nil The reason the no items were dropped. +-- @see select +function dropDown(count) end + +--- Suck an item from the inventory in front of the turtle, or from an item +-- floating in the world. +-- +-- This will pull items into the first acceptable slot, starting at the +-- @{select|currently selected} one. +-- +-- @tparam[opt] number count The number of items to suck. If not given, up to a +-- stack of items will be picked up. +-- @treturn boolean Whether items were picked up. +-- @treturn string|nil The reason the no items were picked up. +function suck(count) end + +--- Suck an item from the inventory above the turtle, or from an item floating +-- in the world. +-- +-- @tparam[opt] number count The number of items to suck. If not given, up to a +-- stack of items will be picked up. +-- @treturn boolean Whether items were picked up. +-- @treturn string|nil The reason the no items were picked up. +function suckUp(count) end + +--- Suck an item from the inventory below the turtle, or from an item floating +-- in the world. +-- +-- @tparam[opt] number count The number of items to suck. If not given, up to a +-- stack of items will be picked up. +-- @treturn boolean Whether items were picked up. +-- @treturn string|nil The reason the no items were picked up. +function suckDown(count) end + +--- Check if there is a solid block in front of the turtle. In this case, solid +-- refers to any non-air or liquid block. +-- +-- @treturn boolean If there is a solid block in front. function detect() end + +--- Check if there is a solid block above the turtle. +-- +-- @treturn boolean If there is a solid block above. function detectUp() end + +--- Check if there is a solid block below the turtle. +-- +-- @treturn boolean If there is a solid block below. function detectDown() end + function compare() end function compareUp() end function compareDown() end -function attack(side) end -function attackUp(side) end -function attackDown(side) end -function dropUp(count) end -function dropDown(count) end -function suck(count) end -function suckUp(count) end -function suckDown(count) end -function getFuelLevel() end -function refuel(count) end -function compareTo(slot) end -function transferTo(slot, count) end -function getSelectedSlot() end -function getFuelLimit() end -function equipLeft() end -function equipRight() end + function inspect() end function inspectUp() end function inspectDown() end + + +--- Change the currently selected slot. +-- +-- The selected slot is determines what slot actions like @{drop} or +-- @{getItemCount} act on. +-- +-- @tparam number slot The slot to select. +-- @see getSelectedSlot +function select(slot) end + +--- Get the currently selected slot. +-- +-- @treturn number The current slot. +-- @see select +function getSelectedSlot() end + +--- Get the number of items in the given slot. +-- +-- @tparam[opt] number slot The slot we wish to check. Defaults to the @{turtle.select|selected slot}. +-- @treturn number The number of items in this slot. +function getItemCount(slot) end + +--- Get the remaining number of items which may be stored in this stack. +-- +-- For instance, if a slot contains 13 blocks of dirt, it has room for another 51. +-- +-- @tparam[opt] number slot The slot we wish to check. Defaults to the @{turtle.select|selected slot}. +-- @treturn number The space left in this slot. +function getItemSpace(slot) end + + +--- Get detailed information about the items in the given slot. +-- +-- @tparam[opt] number slot The slot to get information about. Defaults to the @{turtle.select|selected slot}. +-- @treturn nil|table Information about the given slot, or @{nil} if it is empty. +-- @usage Print the current slot, assuming it contains 13 dirt. +-- +-- print(textutils.serialize(turtle.getItemDetail())) +-- -- => { +-- -- name = "minecraft:dirt", +-- -- damage = 0, +-- -- count = 13, +-- -- } function getItemDetail(slot) end + +function getFuelLevel() end + +function refuel(count) end +function compareTo(slot) end +function transferTo(slot, count) end + +function getFuelLimit() end +function equipLeft() end +function equipRight() end + +function craft(limit) end diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua index c3c71ff65..b9039a52f 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua @@ -105,14 +105,16 @@ function getMethods(name) return nil end ---- Call a method on a peripheral with a given name +--- Call a method on the peripheral with the given name. -- -- @tparam string name The name of the peripheral to invoke the method on. -- @tparam string method The name of the method -- @param ... Additional arguments to pass to the method -- @return The return values of the peripheral method. -- --- @usage peripheral.call("top", "open", 1) +-- @usage Open the modem on the top of this computer. +-- +-- peripheral.call("top", "open", 1) function call(name, method, ...) expect(1, name, "string") expect(2, method, "string") diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua index f52645524..32852f718 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/turtle/turtle.lua @@ -5,7 +5,7 @@ if not turtle then error("Cannot load turtle API on computer", 2) end -native = turtle.native or turtle +native = turtle.native or turtle --- @local local function addCraftMethod(object) if peripheral.getType("left") == "workbench" then diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua index aa6e5aab6..57bd70c54 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua @@ -68,7 +68,7 @@ local string_sub = string.sub -- @tparam number nWidth The width of this window -- @tparam number nHeight The height of this window -- @tparam[opt] boolean bStartVisible Whether this window is visible by --- default. Defaults to `true`. +-- default. Defaults to `true`. -- @treturn Window The constructed window function create(parent, nX, nY, nWidth, nHeight, bStartVisible) expect(1, parent, "table") @@ -229,9 +229,11 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) end end - --- Terminal implementation + --- The window object. Refer to the @{window|module's documentation} for + -- a full description. -- -- @type Window + -- @see term.Redirect local window = {} function window.write(sText) @@ -437,6 +439,13 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) return nBackgroundColor end + --- Get the buffered contents of a line in this window. + --- + -- @tparam number y The y position of the line to get. + -- @treturn string The textual content of this line. + -- @treturn string The text colours of this line, suitable for use with @{term.blit}. + -- @treturn string The background colours of this line, suitable for use with @{term.blit}. + -- @throws If `y` is a valid line. function window.getLine(y) if type(y) ~= "number" then expect(1, y, "number") end @@ -448,16 +457,26 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) end -- Other functions - function window.setVisible(bVis) - if type(bVis) ~= "boolean" then expect(1, bVis, "boolean") end - if bVisible ~= bVis then - bVisible = bVis + + --- Set whether this window is visible. Invisible windows will not be drawn + -- to the screen until they are made visible again. + -- + -- Making an invisible window visible will immediately draw it. + -- + -- @tparam boolean visible Whether this window is visible. + function window.setVisible(visible) + if type(visible) ~= "boolean" then expect(1, visible, "boolean") end + if bVisible ~= visible then + bVisible = visible if bVisible then window.redraw() end end end + --- Draw this window. This does nothing if the window is not visible. + -- + -- @see Window:setVisible function window.redraw() if bVisible then redraw() @@ -468,6 +487,8 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) end end + --- Set the current terminal's cursor to where this window's cursor is. This + -- does nothing if the window is not visible. function window.restoreCursor() if bVisible then updateCursorBlink() @@ -476,31 +497,47 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) end end + --- Get the position of the top left corner of this window. + -- + -- @treturn number The x position of this window. + -- @treturn number The y position of this window. function window.getPosition() return nX, nY end - function window.reposition(nNewX, nNewY, nNewWidth, nNewHeight, newParent) - if type(nNewX) ~= "number" then expect(1, nNewX, "number") end - if type(nNewY) ~= "number" then expect(2, nNewY, "number") end - if nNewWidth ~= nil or nNewHeight ~= nil then - expect(3, nNewWidth, "number") - expect(4, nNewHeight, "number") + --- Reposition or resize the given window. + -- + -- This function also accepts arguments to change the size of this window. + -- It is recommended that you fire a `term_resize` event after changing a + -- window's, to allow programs to adjust their sizing. + -- + -- @tparam number new_x The new x position of this window. + -- @tparam number new_y The new y position of this window. + -- @tparam[opt] number new_width The new width of this window. + -- @tparam number new_height The new height of this window. + -- @tparam[opt] term.Redirect new_parent The new redirect object this + -- window should draw to. + function window.reposition(new_x, new_y, new_width, new_height, new_parent) + if type(new_x) ~= "number" then expect(1, new_x, "number") end + if type(new_y) ~= "number" then expect(2, new_y, "number") end + if new_width ~= nil or new_height ~= nil then + expect(3, new_width, "number") + expect(4, new_height, "number") end - if newParent ~= nil and type(newParent) ~= "table" then expect(5, newParent, "table") end + if new_parent ~= nil and type(new_parent) ~= "table" then expect(5, new_parent, "table") end - nX = nNewX - nY = nNewY + nX = new_x + nY = new_y - if newParent then parent = newParent end + if new_parent then parent = new_parent end - if nNewWidth and nNewHeight then + if new_width and new_height then local tNewLines = {} - createEmptyLines(nNewWidth) + createEmptyLines(new_width) local sEmptyText = sEmptySpaceLine local sEmptyTextColor = tEmptyColorLines[nTextColor] local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor] - for y = 1, nNewHeight do + for y = 1, new_height do if y > nHeight then tNewLines[y] = { text = sEmptyText, @@ -509,25 +546,25 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) } else local tOldLine = tLines[y] - if nNewWidth == nWidth then + if new_width == nWidth then tNewLines[y] = tOldLine - elseif nNewWidth < nWidth then + elseif new_width < nWidth then tNewLines[y] = { - text = string_sub(tOldLine.text, 1, nNewWidth), - textColor = string_sub(tOldLine.textColor, 1, nNewWidth), - backgroundColor = string_sub(tOldLine.backgroundColor, 1, nNewWidth), + text = string_sub(tOldLine.text, 1, new_width), + textColor = string_sub(tOldLine.textColor, 1, new_width), + backgroundColor = string_sub(tOldLine.backgroundColor, 1, new_width), } else tNewLines[y] = { - text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, nNewWidth), - textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, nNewWidth), - backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, nNewWidth), + text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, new_width), + textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, new_width), + backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width), } end end end - nWidth = nNewWidth - nHeight = nNewHeight + nWidth = new_width + nHeight = new_height tLines = tNewLines end if bVisible then From b14c7842fc06924d18fc65f0c43ef2acbb53e231 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 19 Apr 2020 15:08:46 +0100 Subject: [PATCH 27/35] Add textutils.unserialiseJSON (#407) This is relatively unoptimised right now, but should be efficient enough for most practical applications. - Add textutils.json_null. This will be serialized into a literal `null`. When deserializing, and parse_null is true, this will be returned instead of a nil. - Add textutils.unserializeJSON (and textutils.unserializeJSON). This is a standard compliant JSON parser (hopefully). - Passing in nbt_style to textutils.unserializeJSON will handle stringified NBT (no quotes around object keys, numeric suffices). We don't currently support byte/long/int arrays - something to add in a future commit. --- .../computercraft/lua/rom/apis/textutils.lua | 237 +++++++++++++++++- .../core/ComputerTestDelegate.java | 4 +- .../test-rom/data/json-parsing/LICENSE | 21 ++ .../test-rom/data/json-parsing/README.md | 9 + .../i_number_double_huge_neg_exp.json | 1 + .../data/json-parsing/i_number_huge_exp.json | 1 + .../i_number_neg_int_huge_exp.json | 1 + .../i_number_pos_double_huge_exp.json | 1 + .../i_number_real_neg_overflow.json | 1 + .../i_number_real_pos_overflow.json | 1 + .../json-parsing/i_number_real_underflow.json | 1 + .../i_number_too_big_neg_int.json | 1 + .../i_number_too_big_pos_int.json | 1 + .../i_number_very_big_negative_int.json | 1 + .../i_object_key_lone_2nd_surrogate.json | 1 + ..._string_1st_surrogate_but_2nd_missing.json | 1 + ...tring_1st_valid_surrogate_2nd_invalid.json | 1 + .../i_string_UTF-16LE_with_BOM.json | Bin 0 -> 12 bytes .../i_string_UTF-8_invalid_sequence.json | 1 + .../i_string_UTF8_surrogate_U+D800.json | 1 + ...incomplete_surrogate_and_escape_valid.json | 1 + .../i_string_incomplete_surrogate_pair.json | 1 + ...ng_incomplete_surrogates_escape_valid.json | 1 + .../i_string_invalid_lonely_surrogate.json | 1 + .../i_string_invalid_surrogate.json | 1 + .../json-parsing/i_string_invalid_utf-8.json | 1 + .../i_string_inverted_surrogates_U+1D11E.json | 1 + .../json-parsing/i_string_iso_latin_1.json | 1 + .../i_string_lone_second_surrogate.json | 1 + .../i_string_lone_utf8_continuation_byte.json | 1 + .../i_string_not_in_unicode_range.json | 1 + .../i_string_overlong_sequence_2_bytes.json | 1 + .../i_string_overlong_sequence_6_bytes.json | 1 + ...string_overlong_sequence_6_bytes_null.json | 1 + .../i_string_truncated-utf-8.json | 1 + .../json-parsing/i_string_utf16BE_no_BOM.json | Bin 0 -> 10 bytes .../json-parsing/i_string_utf16LE_no_BOM.json | Bin 0 -> 10 bytes .../i_structure_500_nested_arrays.json | 1 + .../i_structure_UTF-8_BOM_empty_object.json | 1 + .../n_array_1_true_without_comma.json | 1 + .../json-parsing/n_array_a_invalid_utf8.json | 1 + .../n_array_colon_instead_of_comma.json | 1 + .../n_array_comma_after_close.json | 1 + .../n_array_comma_and_number.json | 1 + .../json-parsing/n_array_double_comma.json | 1 + .../n_array_double_extra_comma.json | 1 + .../json-parsing/n_array_extra_close.json | 1 + .../json-parsing/n_array_extra_comma.json | 1 + .../data/json-parsing/n_array_incomplete.json | 1 + .../n_array_incomplete_invalid_value.json | 1 + .../n_array_inner_array_no_comma.json | 1 + .../json-parsing/n_array_invalid_utf8.json | 1 + .../n_array_items_separated_by_semicolon.json | 1 + .../data/json-parsing/n_array_just_comma.json | 1 + .../data/json-parsing/n_array_just_minus.json | 1 + .../json-parsing/n_array_missing_value.json | 1 + .../n_array_newlines_unclosed.json | 3 + .../n_array_number_and_comma.json | 1 + .../n_array_number_and_several_commas.json | 1 + .../n_array_spaces_vertical_tab_formfeed.json | 1 + .../json-parsing/n_array_star_inside.json | 1 + .../data/json-parsing/n_array_unclosed.json | 1 + .../n_array_unclosed_trailing_comma.json | 1 + .../n_array_unclosed_with_new_lines.json | 3 + .../n_array_unclosed_with_object_inside.json | 1 + .../data/json-parsing/n_incomplete_false.json | 1 + .../data/json-parsing/n_incomplete_null.json | 1 + .../data/json-parsing/n_incomplete_true.json | 1 + .../n_multidigit_number_then_00.json | Bin 0 -> 4 bytes .../data/json-parsing/n_number_++.json | 1 + .../data/json-parsing/n_number_+1.json | 1 + .../data/json-parsing/n_number_+Inf.json | 1 + .../data/json-parsing/n_number_-01.json | 1 + .../data/json-parsing/n_number_-1.0..json | 1 + .../data/json-parsing/n_number_-2..json | 1 + .../data/json-parsing/n_number_-NaN.json | 1 + .../data/json-parsing/n_number_.-1.json | 1 + .../data/json-parsing/n_number_.2e-3.json | 1 + .../data/json-parsing/n_number_0.1.2.json | 1 + .../data/json-parsing/n_number_0.3e+.json | 1 + .../data/json-parsing/n_number_0.3e.json | 1 + .../data/json-parsing/n_number_0.e1.json | 1 + .../json-parsing/n_number_0_capital_E+.json | 1 + .../json-parsing/n_number_0_capital_E.json | 1 + .../data/json-parsing/n_number_0e+.json | 1 + .../data/json-parsing/n_number_0e.json | 1 + .../data/json-parsing/n_number_1.0e+.json | 1 + .../data/json-parsing/n_number_1.0e-.json | 1 + .../data/json-parsing/n_number_1.0e.json | 1 + .../data/json-parsing/n_number_1_000.json | 1 + .../data/json-parsing/n_number_1eE2.json | 1 + .../data/json-parsing/n_number_2.e+3.json | 1 + .../data/json-parsing/n_number_2.e-3.json | 1 + .../data/json-parsing/n_number_2.e3.json | 1 + .../data/json-parsing/n_number_9.e+.json | 1 + .../data/json-parsing/n_number_Inf.json | 1 + .../data/json-parsing/n_number_NaN.json | 1 + .../n_number_U+FF11_fullwidth_digit_one.json | 1 + .../json-parsing/n_number_expression.json | 1 + .../json-parsing/n_number_hex_1_digit.json | 1 + .../json-parsing/n_number_hex_2_digits.json | 1 + .../data/json-parsing/n_number_infinity.json | 1 + .../data/json-parsing/n_number_invalid+-.json | 1 + .../n_number_invalid-negative-real.json | 1 + .../n_number_invalid-utf-8-in-bigger-int.json | 1 + .../n_number_invalid-utf-8-in-exponent.json | 1 + .../n_number_invalid-utf-8-in-int.json | 1 + .../json-parsing/n_number_minus_infinity.json | 1 + ...mber_minus_sign_with_trailing_garbage.json | 1 + .../json-parsing/n_number_minus_space_1.json | 1 + .../n_number_neg_int_starting_with_zero.json | 1 + .../n_number_neg_real_without_int_part.json | 1 + .../n_number_neg_with_garbage_at_end.json | 1 + .../n_number_real_garbage_after_e.json | 1 + ...number_real_with_invalid_utf8_after_e.json | 1 + ...n_number_real_without_fractional_part.json | 1 + .../n_number_starting_with_dot.json | 1 + .../json-parsing/n_number_with_alpha.json | 1 + .../n_number_with_alpha_char.json | 1 + .../n_number_with_leading_zero.json | 1 + .../data/json-parsing/n_object_bad_value.json | 1 + .../json-parsing/n_object_bracket_key.json | 1 + .../n_object_comma_instead_of_colon.json | 1 + .../json-parsing/n_object_double_colon.json | 1 + .../data/json-parsing/n_object_emoji.json | 1 + .../json-parsing/n_object_garbage_at_end.json | 1 + .../n_object_key_with_single_quotes.json | 1 + ...uation_byte_in_key_and_trailing_comma.json | 1 + .../json-parsing/n_object_missing_colon.json | 1 + .../json-parsing/n_object_missing_key.json | 1 + .../n_object_missing_semicolon.json | 1 + .../json-parsing/n_object_missing_value.json | 1 + .../data/json-parsing/n_object_no-colon.json | 1 + .../json-parsing/n_object_non_string_key.json | 1 + ...on_string_key_but_huge_number_instead.json | 1 + .../n_object_repeated_null_null.json | 1 + .../n_object_several_trailing_commas.json | 1 + .../json-parsing/n_object_single_quote.json | 1 + .../json-parsing/n_object_trailing_comma.json | 1 + .../n_object_trailing_comment.json | 1 + .../n_object_trailing_comment_open.json | 1 + .../n_object_trailing_comment_slash_open.json | 1 + ...railing_comment_slash_open_incomplete.json | 1 + .../n_object_two_commas_in_a_row.json | 1 + .../json-parsing/n_object_unquoted_key.json | 1 + .../n_object_unterminated-value.json | 1 + .../n_object_with_single_string.json | 1 + .../n_object_with_trailing_garbage.json | 1 + .../data/json-parsing/n_single_space.json | 1 + .../n_string_1_surrogate_then_escape.json | 1 + .../n_string_1_surrogate_then_escape_u.json | 1 + .../n_string_1_surrogate_then_escape_u1.json | 1 + .../n_string_1_surrogate_then_escape_u1x.json | 1 + .../n_string_accentuated_char_no_quotes.json | 1 + .../json-parsing/n_string_backslash_00.json | Bin 0 -> 6 bytes .../data/json-parsing/n_string_escape_x.json | 1 + .../n_string_escaped_backslash_bad.json | 1 + .../n_string_escaped_ctrl_char_tab.json | 1 + .../json-parsing/n_string_escaped_emoji.json | 1 + .../n_string_incomplete_escape.json | 1 + ...n_string_incomplete_escaped_character.json | 1 + .../n_string_incomplete_surrogate.json | 1 + ...g_incomplete_surrogate_escape_invalid.json | 1 + .../n_string_invalid-utf-8-in-escape.json | 1 + .../n_string_invalid_backslash_esc.json | 1 + .../n_string_invalid_unicode_escape.json | 1 + .../n_string_invalid_utf8_after_escape.json | 1 + .../n_string_leading_uescaped_thinspace.json | 1 + .../n_string_no_quotes_with_bad_escape.json | 1 + .../n_string_single_doublequote.json | 1 + .../json-parsing/n_string_single_quote.json | 1 + ...string_single_string_no_double_quotes.json | 1 + .../n_string_start_escape_unclosed.json | 1 + .../n_string_unescaped_crtl_char.json | Bin 0 -> 7 bytes .../n_string_unescaped_newline.json | 2 + .../json-parsing/n_string_unescaped_tab.json | 1 + .../n_string_unicode_CapitalU.json | 1 + .../n_string_with_trailing_garbage.json | 1 + .../n_structure_100000_opening_arrays.json | 1 + .../n_structure_U+2060_word_joined.json | 1 + .../n_structure_UTF8_BOM_no_data.json | 1 + .../n_structure_angle_bracket_..json | 1 + .../n_structure_angle_bracket_null.json | 1 + .../n_structure_array_trailing_garbage.json | 1 + ...tructure_array_with_extra_array_close.json | 1 + ..._structure_array_with_unclosed_string.json | 1 + .../n_structure_ascii-unicode-identifier.json | 1 + .../n_structure_capitalized_True.json | 1 + .../n_structure_close_unopened_array.json | 1 + ...ucture_comma_instead_of_closing_brace.json | 1 + .../n_structure_double_array.json | 1 + .../json-parsing/n_structure_end_array.json | 1 + .../n_structure_incomplete_UTF8_BOM.json | 1 + .../n_structure_lone-invalid-utf-8.json | 1 + .../n_structure_lone-open-bracket.json | 1 + .../json-parsing/n_structure_no_data.json | 0 .../n_structure_null-byte-outside-string.json | Bin 0 -> 3 bytes ...tructure_number_with_trailing_garbage.json | 1 + ...ure_object_followed_by_closing_object.json | 1 + .../n_structure_object_unclosed_no_value.json | 1 + .../n_structure_object_with_comment.json | 1 + ...tructure_object_with_trailing_garbage.json | 1 + .../n_structure_open_array_apostrophe.json | 1 + .../n_structure_open_array_comma.json | 1 + .../n_structure_open_array_object.json | 1 + .../n_structure_open_array_open_object.json | 1 + .../n_structure_open_array_open_string.json | 1 + .../n_structure_open_array_string.json | 1 + .../json-parsing/n_structure_open_object.json | 1 + .../n_structure_open_object_close_array.json | 1 + .../n_structure_open_object_comma.json | 1 + .../n_structure_open_object_open_array.json | 1 + .../n_structure_open_object_open_string.json | 1 + ...e_open_object_string_with_apostrophes.json | 1 + .../json-parsing/n_structure_open_open.json | 1 + .../n_structure_single_eacute.json | 1 + .../json-parsing/n_structure_single_star.json | 1 + .../json-parsing/n_structure_trailing_#.json | 1 + ...n_structure_uescaped_LF_before_string.json | 1 + .../n_structure_unclosed_array.json | 1 + ...structure_unclosed_array_partial_null.json | 1 + ...cture_unclosed_array_unfinished_false.json | 1 + ...ucture_unclosed_array_unfinished_true.json | 1 + .../n_structure_unclosed_object.json | 1 + .../n_structure_unicode-identifier.json | 1 + ...ructure_whitespace_U+2060_word_joiner.json | 1 + .../n_structure_whitespace_formfeed.json | 1 + .../test-rom/data/json-parsing/skip.lua | 21 ++ .../y_array_arraysWithSpaces.json | 1 + .../json-parsing/y_array_empty-string.json | 1 + .../data/json-parsing/y_array_empty.json | 1 + .../y_array_ending_with_newline.json | 1 + .../data/json-parsing/y_array_false.json | 1 + .../json-parsing/y_array_heterogeneous.json | 1 + .../data/json-parsing/y_array_null.json | 1 + .../y_array_with_1_and_newline.json | 2 + .../y_array_with_leading_space.json | 1 + .../y_array_with_several_null.json | 1 + .../y_array_with_trailing_space.json | 1 + .../test-rom/data/json-parsing/y_number.json | 1 + .../data/json-parsing/y_number_0e+1.json | 1 + .../data/json-parsing/y_number_0e1.json | 1 + .../json-parsing/y_number_after_space.json | 1 + .../y_number_double_close_to_zero.json | 1 + .../json-parsing/y_number_int_with_exp.json | 1 + .../json-parsing/y_number_minus_zero.json | 1 + .../json-parsing/y_number_negative_int.json | 1 + .../json-parsing/y_number_negative_one.json | 1 + .../json-parsing/y_number_negative_zero.json | 1 + .../json-parsing/y_number_real_capital_e.json | 1 + .../y_number_real_capital_e_neg_exp.json | 1 + .../y_number_real_capital_e_pos_exp.json | 1 + .../json-parsing/y_number_real_exponent.json | 1 + .../y_number_real_fraction_exponent.json | 1 + .../json-parsing/y_number_real_neg_exp.json | 1 + .../y_number_real_pos_exponent.json | 1 + .../json-parsing/y_number_simple_int.json | 1 + .../json-parsing/y_number_simple_real.json | 1 + .../test-rom/data/json-parsing/y_object.json | 1 + .../data/json-parsing/y_object_basic.json | 1 + .../json-parsing/y_object_duplicated_key.json | 1 + .../y_object_duplicated_key_and_value.json | 1 + .../data/json-parsing/y_object_empty.json | 1 + .../data/json-parsing/y_object_empty_key.json | 1 + .../y_object_escaped_null_in_key.json | 1 + .../y_object_extreme_numbers.json | 1 + .../json-parsing/y_object_long_strings.json | 1 + .../data/json-parsing/y_object_simple.json | 1 + .../json-parsing/y_object_string_unicode.json | 1 + .../json-parsing/y_object_with_newlines.json | 3 + .../y_string_1_2_3_bytes_UTF-8_sequences.json | 1 + .../y_string_accepted_surrogate_pair.json | 1 + .../y_string_accepted_surrogate_pairs.json | 1 + .../y_string_allowed_escapes.json | 1 + ...y_string_backslash_and_u_escaped_zero.json | 1 + .../y_string_backslash_doublequotes.json | 1 + .../data/json-parsing/y_string_comments.json | 1 + .../y_string_double_escape_a.json | 1 + .../y_string_double_escape_n.json | 1 + .../y_string_escaped_control_character.json | 1 + .../y_string_escaped_noncharacter.json | 1 + .../data/json-parsing/y_string_in_array.json | 1 + .../y_string_in_array_with_leading_space.json | 1 + .../y_string_last_surrogates_1_and_2.json | 1 + .../json-parsing/y_string_nbsp_uescaped.json | 1 + ...y_string_nonCharacterInUTF-8_U+10FFFF.json | 1 + .../y_string_nonCharacterInUTF-8_U+FFFF.json | 1 + .../json-parsing/y_string_null_escape.json | 1 + .../json-parsing/y_string_one-byte-utf-8.json | 1 + .../data/json-parsing/y_string_pi.json | 1 + ...ring_reservedCharacterInUTF-8_U+1BFFF.json | 1 + .../json-parsing/y_string_simple_ascii.json | 1 + .../data/json-parsing/y_string_space.json | 1 + ...rogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json | 1 + .../y_string_three-byte-utf-8.json | 1 + .../json-parsing/y_string_two-byte-utf-8.json | 1 + .../y_string_u+2028_line_sep.json | 1 + .../json-parsing/y_string_u+2029_par_sep.json | 1 + .../data/json-parsing/y_string_uEscape.json | 1 + .../y_string_uescaped_newline.json | 1 + .../y_string_unescaped_char_delete.json | 1 + .../data/json-parsing/y_string_unicode.json | 1 + .../y_string_unicodeEscapedBackslash.json | 1 + .../data/json-parsing/y_string_unicode_2.json | 1 + .../y_string_unicode_U+10FFFE_nonchar.json | 1 + .../y_string_unicode_U+1FFFE_nonchar.json | 1 + ...tring_unicode_U+200B_ZERO_WIDTH_SPACE.json | 1 + ..._string_unicode_U+2064_invisible_plus.json | 1 + .../y_string_unicode_U+FDD0_nonchar.json | 1 + .../y_string_unicode_U+FFFE_nonchar.json | 1 + ...y_string_unicode_escaped_double_quote.json | 1 + .../data/json-parsing/y_string_utf8.json | 1 + .../y_string_with_del_character.json | 1 + .../y_structure_lonely_false.json | 1 + .../json-parsing/y_structure_lonely_int.json | 1 + .../y_structure_lonely_negative_real.json | 1 + .../json-parsing/y_structure_lonely_null.json | 1 + .../y_structure_lonely_string.json | 1 + .../json-parsing/y_structure_lonely_true.json | 1 + .../y_structure_string_empty.json | 1 + .../y_structure_trailing_newline.json | 1 + .../y_structure_true_in_array.json | 1 + .../y_structure_whitespace_array.json | 1 + .../test-rom/spec/apis/textutils_spec.lua | 63 +++++ tools/check-lines.py | 6 +- 325 files changed, 666 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/test-rom/data/json-parsing/LICENSE create mode 100644 src/test/resources/test-rom/data/json-parsing/README.md create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_UTF-16LE_with_BOM.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_utf16BE_no_BOM.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_string_utf16LE_no_BOM.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json create mode 100644 src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_multidigit_number_then_00.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_++.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_+1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_-01.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_-2..json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_.-1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0e+.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_0e.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_1_000.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_Inf.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_NaN.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_expression.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_infinity.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_emoji.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_single_space.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_backslash_00.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_unescaped_crtl_char.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_no_data.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_null-byte-outside-string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json create mode 100644 src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json create mode 100644 src/test/resources/test-rom/data/json-parsing/skip.lua create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_empty.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_false.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_0e1.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_after_space.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_basic.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_empty.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_simple.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_comments.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_in_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_pi.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_space.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_utf8.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json create mode 100644 src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua index 1ef4fb1ae..d087c570c 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/textutils.lua @@ -3,7 +3,8 @@ -- -- @module textutils -local expect = dofile("rom/modules/main/cc/expect.lua").expect +local expect = dofile("rom/modules/main/cc/expect.lua") +local expect, field = expect.expect, expect.field --- Slowly writes string text at current cursor position, -- character-by-character. @@ -307,6 +308,14 @@ local function serializeImpl(t, tTracking, sIndent) end end +local function mk_tbl(str, name) + local msg = "attempt to mutate textutils." .. name + return setmetatable({}, { + __newindex = function() error(msg, 2) end, + __tostring = function() return str end, + }) +end + --- A table representing an empty JSON array, in order to distinguish it from an -- empty JSON object. -- @@ -314,16 +323,22 @@ end -- -- @usage textutils.serialiseJSON(textutils.empty_json_array) -- @see textutils.serialiseJSON -empty_json_array = setmetatable({}, { - __newindex = function() - error("attempt to mutate textutils.empty_json_array", 2) - end, -}) +-- @see textutils.unserialiseJSON +empty_json_array = mk_tbl("[]", "empty_json_array") + +--- A table representing the JSON null value. +-- +-- The contents of this table should not be modified. +-- +-- @usage textutils.serialiseJSON(textutils.json_null) +-- @see textutils.serialiseJSON +-- @see textutils.unserialiseJSON +json_null = mk_tbl("null", "json_null") local function serializeJSONImpl(t, tTracking, bNBTStyle) local sType = type(t) - if t == empty_json_array then - return "[]" + if t == empty_json_array then return "[]" + elseif t == json_null then return "null" elseif sType == "table" then if tTracking[t] ~= nil then @@ -386,6 +401,209 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle) end end +local unserialise_json +do + local sub, find, match, concat, tonumber = string.sub, string.find, string.match, table.concat, tonumber + + --- Skip any whitespace + local function skip(str, pos) + local _, last = find(str, "^[ \n\r\v]+", pos) + if last then return last + 1 else return pos end + end + + local escapes = { + ["b"] = '\b', ["f"] = '\f', ["n"] = '\n', ["r"] = '\r', ["t"] = '\t', + ["\""] = "\"", ["/"] = "/", ["\\"] = "\\", + } + + local mt = {} + + local function error_at(pos, msg, ...) + if select('#', ...) > 0 then msg = msg:format(...) end + error(setmetatable({ pos = pos, msg = msg }, mt)) + end + + local function expected(pos, actual, exp) + if actual == "" then actual = "end of input" else actual = ("%q"):format(actual) end + error_at(pos, "Unexpected %s, expected %s.", actual, exp) + end + + local function parse_string(str, pos) + local buf, n = {}, 1 + + while true do + local c = sub(str, pos, pos) + if c == "" then error_at(pos, "Unexpected end of input, expected '\"'.") end + if c == '"' then break end + + if c == '\\' then + -- Handle the various escapes + c = sub(str, pos + 1, pos + 1) + if c == "" then error_at(pos, "Unexpected end of input, expected escape sequence.") end + + if c == "u" then + local num_str = match(str, "^%x%x%x%x", pos + 2) + if not num_str then error_at(pos, "Malformed unicode escape %q.", sub(str, pos + 2, pos + 5)) end + buf[n], n, pos = utf8.char(tonumber(num_str, 16)), n + 1, pos + 6 + else + local unesc = escapes[c] + if not unesc then error_at(pos + 1, "Unknown escape character %q.", unesc) end + buf[n], n, pos = unesc, n + 1, pos + 2 + end + elseif c >= '\x20' then + buf[n], n, pos = c, n + 1, pos + 1 + else + error_at(pos + 1, "Unescaped whitespace %q.", c) + end + end + + return concat(buf, "", 1, n - 1), pos + 1 + end + + local valid = { b = true, B = true, s = true, S = true, l = true, L = true, f = true, F = true, d = true, D = true } + local function parse_number(str, pos, opts) + local _, last, num_str = find(str, '^(-?%d+%.?%d*[eE]?[+-]?%d*)', pos) + local val = tonumber(num_str) + if not val then error_at(pos, "Malformed number %q.", num_str) end + + if opts.nbt_style and valid[sub(str, pos + 1, pos + 1)] then return val, last + 2 end + + return val, last + 1 + end + + local function parse_ident(str, pos) + local _, last, val = find(str, '^([%a][%w_]*)', pos) + return val, last + 1 + end + + local function decode_impl(str, pos, opts) + local c = sub(str, pos, pos) + if c == '"' then return parse_string(str, pos + 1) + elseif c == "-" or c >= "0" and c <= "9" then return parse_number(str, pos, opts) + elseif c == "t" then + if sub(str, pos + 1, pos + 3) == "rue" then return true, pos + 4 end + elseif c == 'f' then + if sub(str, pos + 1, pos + 4) == "alse" then return false, pos + 5 end + elseif c == 'n' then + if sub(str, pos + 1, pos + 3) == "ull" then + if opts.parse_null then + return json_null, pos + 4 + else + return nil, pos + 4 + end + end + elseif c == "{" then + local obj = {} + + pos = skip(str, pos + 1) + c = sub(str, pos, pos) + + if c == "" then return error_at(pos, "Unexpected end of input, expected '}'.") end + if c == "}" then return obj, pos + 1 end + + while true do + local key, value + if c == "\"" then key, pos = parse_string(str, pos + 1) + elseif opts.nbt_style then key, pos = parse_ident(str, pos) + else return expected(pos, c, "object key") + end + + pos = skip(str, pos) + + c = sub(str, pos, pos) + if c ~= ":" then return expected(pos, c, "':'") end + + value, pos = decode_impl(str, skip(str, pos + 1), opts) + obj[key] = value + + -- Consume the next delimiter + pos = skip(str, pos) + c = sub(str, pos, pos) + if c == "}" then break + elseif c == "," then pos = skip(str, pos + 1) + else return expected(pos, c, "',' or '}'") + end + + c = sub(str, pos, pos) + end + + return obj, pos + 1 + + elseif c == "[" then + local arr, n = {}, 1 + + pos = skip(str, pos + 1) + c = sub(str, pos, pos) + + if c == "" then return expected(pos, c, "']'") end + if c == "]" then return empty_json_array, pos + 1 end + + while true do + n, arr[n], pos = n + 1, decode_impl(str, pos, opts) + + -- Consume the next delimiter + pos = skip(str, pos) + c = sub(str, pos, pos) + if c == "]" then break + elseif c == "," then pos = skip(str, pos + 1) + else return expected(pos, c, "',' or ']'") + end + end + + return arr, pos + 1 + elseif c == "" then error_at(pos, 'Unexpected end of input.') + end + + error_at(pos, "Unexpected character %q.", c) + end + + --- Converts a serialised JSON string back into a reassembled Lua object. + -- + -- This may be used with @{textutils.serializeJSON}, or when communicating + -- with command blocks or web APIs. + -- + -- @tparam string s The serialised string to deserialise. + -- @tparam[opt] { nbt_style? = boolean, parse_null? = boolean } options + -- Options which control how this JSON object is parsed. + -- + -- - `nbt_style`: When true, this will accept [stringified NBT][nbt] strings, + -- as produced by many commands. + -- - `parse_null`: When true, `null` will be parsed as @{json_null}, rather + -- than `nil`. + -- + -- [nbt]: https://minecraft.gamepedia.com/NBT_format + -- @return[1] The deserialised object + -- @treturn[2] nil If the object could not be deserialised. + -- @treturn string A message describing why the JSON string is invalid. + unserialise_json = function(s, options) + expect(1, s, "string") + expect(2, options, "table", "nil") + + if options then + field(options, "nbt_style", "boolean", "nil") + field(options, "nbt_style", "boolean", "nil") + else + options = {} + end + + local ok, res, pos = pcall(decode_impl, s, skip(s, 1), options) + if not ok then + if type(res) == "table" and getmetatable(res) == mt then + return nil, ("Malformed JSON at position %d: %s"):format(res.pos, res.msg) + end + + error(res, 0) + end + + pos = skip(s, pos) + if pos <= #s then + return nil, ("Malformed JSON at position %d: Unexpected trailing character %q."):format(pos, sub(s, pos, pos)) + end + return res + + end +end + --- Convert a Lua object into a textual representation, suitable for -- saving in a file or pretty-printing. -- @@ -449,6 +667,9 @@ end serialiseJSON = serializeJSON -- GB version +unserializeJSON = unserialise_json +unserialiseJSON = unserialise_json + --- Replaces certain characters in a string to make it safe for use in URLs or POST data. -- -- @tparam string str The string to encode diff --git a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java index fec45a318..a3410f04e 100644 --- a/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java +++ b/src/test/java/dan200/computercraft/core/ComputerTestDelegate.java @@ -96,7 +96,7 @@ public class ComputerTestDelegate try( WritableByteChannel channel = mount.openChannelForWrite( "startup.lua" ); Writer writer = Channels.newWriter( channel, StandardCharsets.UTF_8.newEncoder(), -1 ) ) { - writer.write( "loadfile('test/mcfly.lua', nil, _ENV)('test/spec') cct_test.finish()" ); + writer.write( "loadfile('test-rom/mcfly.lua', nil, _ENV)('test-rom/spec') cct_test.finish()" ); } computer = new Computer( new BasicEnvironment( mount ), term, 0 ); @@ -122,7 +122,7 @@ public class ComputerTestDelegate try { computer.getAPIEnvironment().getFileSystem().mount( - "test-rom", "test", + "test-rom", "test-rom", BasicEnvironment.createMount( ComputerTestDelegate.class, "test-rom", "test" ) ); } diff --git a/src/test/resources/test-rom/data/json-parsing/LICENSE b/src/test/resources/test-rom/data/json-parsing/LICENSE new file mode 100644 index 000000000..c4b3621d5 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Nicolas Seriot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/test/resources/test-rom/data/json-parsing/README.md b/src/test/resources/test-rom/data/json-parsing/README.md new file mode 100644 index 000000000..d0d801887 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/README.md @@ -0,0 +1,9 @@ +# JSON Parsing Test Suite + +This is a collection of JSON test cases from [nst/JSONTestSuite][gh]. We simply +determine whether an object is succesfully parsed or not, and do not check the +contents. + +See `LICENSE` for copyright information. + +[gh]: https://github.com/nst/JSONTestSuite diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json new file mode 100644 index 000000000..ae4c7b71f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_double_huge_neg_exp.json @@ -0,0 +1 @@ +[123.456e-789] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json new file mode 100644 index 000000000..9b5efa236 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_huge_exp.json @@ -0,0 +1 @@ +[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json new file mode 100644 index 000000000..3abd58a5c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_neg_int_huge_exp.json @@ -0,0 +1 @@ +[-1e+9999] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json b/src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json new file mode 100644 index 000000000..e10a7eb62 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_pos_double_huge_exp.json @@ -0,0 +1 @@ +[1.5e+9999] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json b/src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json new file mode 100644 index 000000000..3d628a994 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_real_neg_overflow.json @@ -0,0 +1 @@ +[-123123e100000] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json b/src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json new file mode 100644 index 000000000..54d7d3dcd --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_real_pos_overflow.json @@ -0,0 +1 @@ +[123123e100000] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json b/src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json new file mode 100644 index 000000000..c5236eb26 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_real_underflow.json @@ -0,0 +1 @@ +[123e-10000000] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json new file mode 100644 index 000000000..dfa384619 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_neg_int.json @@ -0,0 +1 @@ +[-123123123123123123123123123123] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json new file mode 100644 index 000000000..338a8c3c0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_too_big_pos_int.json @@ -0,0 +1 @@ +[100000000000000000000] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json b/src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json new file mode 100644 index 000000000..e2d9738c2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_number_very_big_negative_int.json @@ -0,0 +1 @@ +[-237462374673276894279832749832423479823246327846] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json new file mode 100644 index 000000000..5be7ebaf9 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_object_key_lone_2nd_surrogate.json @@ -0,0 +1 @@ +{"\uDFAA":0} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json b/src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json new file mode 100644 index 000000000..3b9e37c67 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_1st_surrogate_but_2nd_missing.json @@ -0,0 +1 @@ +["\uDADA"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json b/src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json new file mode 100644 index 000000000..487592832 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_1st_valid_surrogate_2nd_invalid.json @@ -0,0 +1 @@ +["\uD888\u1234"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_UTF-16LE_with_BOM.json b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-16LE_with_BOM.json new file mode 100644 index 0000000000000000000000000000000000000000..2a79c0629a49133d8f715bddbef19cfb3ef025bd GIT binary patch literal 12 ScmezWFPcG#;Uy5qG5`P~Km+3d literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json new file mode 100644 index 000000000..e2a968a15 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_UTF-8_invalid_sequence.json @@ -0,0 +1 @@ +["日шú"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json b/src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json new file mode 100644 index 000000000..916bff920 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_UTF8_surrogate_U+D800.json @@ -0,0 +1 @@ +["í €"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json new file mode 100644 index 000000000..3cb11d229 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_and_escape_valid.json @@ -0,0 +1 @@ +["\uD800\n"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json new file mode 100644 index 000000000..38ec23bb0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogate_pair.json @@ -0,0 +1 @@ +["\uDd1ea"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json new file mode 100644 index 000000000..c9cd6f6c3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_incomplete_surrogates_escape_valid.json @@ -0,0 +1 @@ +["\uD800\uD800\n"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json new file mode 100644 index 000000000..3abbd8d8d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_lonely_surrogate.json @@ -0,0 +1 @@ +["\ud800"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json new file mode 100644 index 000000000..ffddc04f5 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_surrogate.json @@ -0,0 +1 @@ +["\ud800abc"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json new file mode 100644 index 000000000..8e45a7eca --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_invalid_utf-8.json @@ -0,0 +1 @@ +["ÿ"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json b/src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json new file mode 100644 index 000000000..0d5456cc3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_inverted_surrogates_U+1D11E.json @@ -0,0 +1 @@ +["\uDd1e\uD834"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json b/src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json new file mode 100644 index 000000000..9389c9823 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_iso_latin_1.json @@ -0,0 +1 @@ +["é"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json b/src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json new file mode 100644 index 000000000..1dbd397f3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_lone_second_surrogate.json @@ -0,0 +1 @@ +["\uDFAA"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json b/src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json new file mode 100644 index 000000000..729337c0a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_lone_utf8_continuation_byte.json @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json b/src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json new file mode 100644 index 000000000..df90a2916 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_not_in_unicode_range.json @@ -0,0 +1 @@ +["ô¿¿¿"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json new file mode 100644 index 000000000..c8cee5e0a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_2_bytes.json @@ -0,0 +1 @@ +["À¯"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json new file mode 100644 index 000000000..9a91da791 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes.json @@ -0,0 +1 @@ +["üƒ¿¿¿¿"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json new file mode 100644 index 000000000..d24fffdd9 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_overlong_sequence_6_bytes_null.json @@ -0,0 +1 @@ +["ü€€€€€"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json b/src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json new file mode 100644 index 000000000..63c7777fb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_string_truncated-utf-8.json @@ -0,0 +1 @@ +["àÿ"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_utf16BE_no_BOM.json b/src/test/resources/test-rom/data/json-parsing/i_string_utf16BE_no_BOM.json new file mode 100644 index 0000000000000000000000000000000000000000..57e5392ff6309134268c7e3ec2a9289b99ff7148 GIT binary patch literal 10 PcmZRGW>8{y3B<7g33~zN literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/i_string_utf16LE_no_BOM.json b/src/test/resources/test-rom/data/json-parsing/i_string_utf16LE_no_BOM.json new file mode 100644 index 0000000000000000000000000000000000000000..c49c1b25d8e5819591993d4d45e4649a46c1b3e0 GIT binary patch literal 10 Pcma!MP-1uq#IXzj3t$1} literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json b/src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json new file mode 100644 index 000000000..711840589 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_structure_500_nested_arrays.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json b/src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json new file mode 100644 index 000000000..22fdca1b2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/i_structure_UTF-8_BOM_empty_object.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json new file mode 100644 index 000000000..c14e3f6b1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_1_true_without_comma.json @@ -0,0 +1 @@ +[1 true] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json b/src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json new file mode 100644 index 000000000..38a86e2e6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_a_invalid_utf8.json @@ -0,0 +1 @@ +[aå] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json new file mode 100644 index 000000000..0d02ad448 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_colon_instead_of_comma.json @@ -0,0 +1 @@ +["": 1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json b/src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json new file mode 100644 index 000000000..2ccba8d95 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_comma_after_close.json @@ -0,0 +1 @@ +[""], \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json b/src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json new file mode 100644 index 000000000..d2c84e374 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_comma_and_number.json @@ -0,0 +1 @@ +[,1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json new file mode 100644 index 000000000..0431712bc --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_double_comma.json @@ -0,0 +1 @@ +[1,,2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json new file mode 100644 index 000000000..3f01d3129 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_double_extra_comma.json @@ -0,0 +1 @@ +["x",,] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json b/src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json new file mode 100644 index 000000000..c12f9fae1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_extra_close.json @@ -0,0 +1 @@ +["x"]] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json new file mode 100644 index 000000000..5f8ce18e4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_extra_comma.json @@ -0,0 +1 @@ +["",] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json new file mode 100644 index 000000000..cc65b0b51 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete.json @@ -0,0 +1 @@ +["x" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json new file mode 100644 index 000000000..c21a8f6cf --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_incomplete_invalid_value.json @@ -0,0 +1 @@ +[x \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json new file mode 100644 index 000000000..c70b71647 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_inner_array_no_comma.json @@ -0,0 +1 @@ +[3[4]] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json b/src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json new file mode 100644 index 000000000..6099d3441 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_invalid_utf8.json @@ -0,0 +1 @@ +[ÿ] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json b/src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json new file mode 100644 index 000000000..d4bd7314c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_items_separated_by_semicolon.json @@ -0,0 +1 @@ +[1:2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json new file mode 100644 index 000000000..9d7077c68 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_just_comma.json @@ -0,0 +1 @@ +[,] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json b/src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json new file mode 100644 index 000000000..29501c6ca --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_just_minus.json @@ -0,0 +1 @@ +[-] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json b/src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json new file mode 100644 index 000000000..3a6ba86f3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_missing_value.json @@ -0,0 +1 @@ +[ , ""] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json b/src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json new file mode 100644 index 000000000..646680065 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_newlines_unclosed.json @@ -0,0 +1,3 @@ +["a", +4 +,1, \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json new file mode 100644 index 000000000..13f6f1d18 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_comma.json @@ -0,0 +1 @@ +[1,] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json new file mode 100644 index 000000000..0ac408cb8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_number_and_several_commas.json @@ -0,0 +1 @@ +[1,,] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json b/src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json new file mode 100644 index 000000000..6cd7cf585 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_spaces_vertical_tab_formfeed.json @@ -0,0 +1 @@ +[" a"\f] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json b/src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json new file mode 100644 index 000000000..5a5194647 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_star_inside.json @@ -0,0 +1 @@ +[*] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json new file mode 100644 index 000000000..060733059 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed.json @@ -0,0 +1 @@ +["" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json new file mode 100644 index 000000000..6604698ff --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_trailing_comma.json @@ -0,0 +1 @@ +[1, \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json new file mode 100644 index 000000000..4f61de3fb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_new_lines.json @@ -0,0 +1,3 @@ +[1, +1 +,1 \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json new file mode 100644 index 000000000..043a87e2d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_array_unclosed_with_object_inside.json @@ -0,0 +1 @@ +[{} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json b/src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json new file mode 100644 index 000000000..eb18c6a14 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_incomplete_false.json @@ -0,0 +1 @@ +[fals] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json b/src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json new file mode 100644 index 000000000..c18ef5385 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_incomplete_null.json @@ -0,0 +1 @@ +[nul] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json b/src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json new file mode 100644 index 000000000..f451ac6d2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_incomplete_true.json @@ -0,0 +1 @@ +[tru] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_multidigit_number_then_00.json b/src/test/resources/test-rom/data/json-parsing/n_multidigit_number_then_00.json new file mode 100644 index 0000000000000000000000000000000000000000..c22507b864f7f089c91c1eb85b1b15cc63b943a6 GIT binary patch literal 4 LcmXpsGG+h(0mJ~8 literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_++.json b/src/test/resources/test-rom/data/json-parsing/n_number_++.json new file mode 100644 index 000000000..bdb62aaf4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_++.json @@ -0,0 +1 @@ +[++1234] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_+1.json b/src/test/resources/test-rom/data/json-parsing/n_number_+1.json new file mode 100644 index 000000000..3cbe58c92 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_+1.json @@ -0,0 +1 @@ +[+1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json b/src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json new file mode 100644 index 000000000..871ae14d5 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_+Inf.json @@ -0,0 +1 @@ +[+Inf] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-01.json b/src/test/resources/test-rom/data/json-parsing/n_number_-01.json new file mode 100644 index 000000000..0df32bac8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_-01.json @@ -0,0 +1 @@ +[-01] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json b/src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json new file mode 100644 index 000000000..7cf55a85a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_-1.0..json @@ -0,0 +1 @@ +[-1.0.] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-2..json b/src/test/resources/test-rom/data/json-parsing/n_number_-2..json new file mode 100644 index 000000000..9be84365d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_-2..json @@ -0,0 +1 @@ +[-2.] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json b/src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json new file mode 100644 index 000000000..f61615d40 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_-NaN.json @@ -0,0 +1 @@ +[-NaN] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_.-1.json b/src/test/resources/test-rom/data/json-parsing/n_number_.-1.json new file mode 100644 index 000000000..1c9f2dd1b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_.-1.json @@ -0,0 +1 @@ +[.-1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json b/src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json new file mode 100644 index 000000000..c6c976f25 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_.2e-3.json @@ -0,0 +1 @@ +[.2e-3] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json new file mode 100644 index 000000000..c83a25621 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.1.2.json @@ -0,0 +1 @@ +[0.1.2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json new file mode 100644 index 000000000..a55a1bfef --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e+.json @@ -0,0 +1 @@ +[0.3e+] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json new file mode 100644 index 000000000..3dd5df4b3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.3e.json @@ -0,0 +1 @@ +[0.3e] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json b/src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json new file mode 100644 index 000000000..c92c71ccb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0.e1.json @@ -0,0 +1 @@ +[0.e1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json new file mode 100644 index 000000000..3ba2c7d6d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E+.json @@ -0,0 +1 @@ +[0E+] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json new file mode 100644 index 000000000..5301840d1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0_capital_E.json @@ -0,0 +1 @@ +[0E] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_0e+.json new file mode 100644 index 000000000..8ab0bc4b8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0e+.json @@ -0,0 +1 @@ +[0e+] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_0e.json b/src/test/resources/test-rom/data/json-parsing/n_number_0e.json new file mode 100644 index 000000000..47ec421bb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_0e.json @@ -0,0 +1 @@ +[0e] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json new file mode 100644 index 000000000..cd84b9f69 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e+.json @@ -0,0 +1 @@ +[1.0e+] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json new file mode 100644 index 000000000..4eb7afa0f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e-.json @@ -0,0 +1 @@ +[1.0e-] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json new file mode 100644 index 000000000..21753f4c7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_1.0e.json @@ -0,0 +1 @@ +[1.0e] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1_000.json b/src/test/resources/test-rom/data/json-parsing/n_number_1_000.json new file mode 100644 index 000000000..7b18b66b3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_1_000.json @@ -0,0 +1 @@ +[1 000.0] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json b/src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json new file mode 100644 index 000000000..4318a341d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_1eE2.json @@ -0,0 +1 @@ +[1eE2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json b/src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json new file mode 100644 index 000000000..4442f394d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_2.e+3.json @@ -0,0 +1 @@ +[2.e+3] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json b/src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json new file mode 100644 index 000000000..a65060edf --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_2.e-3.json @@ -0,0 +1 @@ +[2.e-3] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json b/src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json new file mode 100644 index 000000000..66f7cf701 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_2.e3.json @@ -0,0 +1 @@ +[2.e3] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json b/src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json new file mode 100644 index 000000000..732a7b11c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_9.e+.json @@ -0,0 +1 @@ +[9.e+] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_Inf.json b/src/test/resources/test-rom/data/json-parsing/n_number_Inf.json new file mode 100644 index 000000000..c40c734c3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_Inf.json @@ -0,0 +1 @@ +[Inf] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_NaN.json b/src/test/resources/test-rom/data/json-parsing/n_number_NaN.json new file mode 100644 index 000000000..499231790 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_NaN.json @@ -0,0 +1 @@ +[NaN] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json b/src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json new file mode 100644 index 000000000..b14587e5e --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_U+FF11_fullwidth_digit_one.json @@ -0,0 +1 @@ +[1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_expression.json b/src/test/resources/test-rom/data/json-parsing/n_number_expression.json new file mode 100644 index 000000000..76fdbc8a4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_expression.json @@ -0,0 +1 @@ +[1+2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json b/src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json new file mode 100644 index 000000000..3b214880c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_hex_1_digit.json @@ -0,0 +1 @@ +[0x1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json b/src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json new file mode 100644 index 000000000..83e516ab0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_hex_2_digits.json @@ -0,0 +1 @@ +[0x42] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_infinity.json b/src/test/resources/test-rom/data/json-parsing/n_number_infinity.json new file mode 100644 index 000000000..8c2baf783 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_infinity.json @@ -0,0 +1 @@ +[Infinity] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json new file mode 100644 index 000000000..1cce602b5 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid+-.json @@ -0,0 +1 @@ +[0e+-1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json new file mode 100644 index 000000000..5fc3c1efb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-negative-real.json @@ -0,0 +1 @@ +[-123.123foo] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json new file mode 100644 index 000000000..3b97e580e --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-bigger-int.json @@ -0,0 +1 @@ +[123å] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json new file mode 100644 index 000000000..ea35d723c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-exponent.json @@ -0,0 +1 @@ +[1e1å] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json new file mode 100644 index 000000000..371226e4c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_invalid-utf-8-in-int.json @@ -0,0 +1 @@ +[0å] diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json b/src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json new file mode 100644 index 000000000..cf4133d22 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_minus_infinity.json @@ -0,0 +1 @@ +[-Infinity] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json new file mode 100644 index 000000000..a6d8e78e7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_minus_sign_with_trailing_garbage.json @@ -0,0 +1 @@ +[-foo] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json b/src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json new file mode 100644 index 000000000..9a5ebedf6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_minus_space_1.json @@ -0,0 +1 @@ +[- 1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json b/src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json new file mode 100644 index 000000000..67af0960a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_neg_int_starting_with_zero.json @@ -0,0 +1 @@ +[-012] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json b/src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json new file mode 100644 index 000000000..1f2a43496 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_neg_real_without_int_part.json @@ -0,0 +1 @@ +[-.123] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json b/src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json new file mode 100644 index 000000000..2aa73119f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_neg_with_garbage_at_end.json @@ -0,0 +1 @@ +[-1x] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json b/src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json new file mode 100644 index 000000000..9213dfca8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_real_garbage_after_e.json @@ -0,0 +1 @@ +[1ea] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json b/src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json new file mode 100644 index 000000000..1e52ef964 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_real_with_invalid_utf8_after_e.json @@ -0,0 +1 @@ +[1eå] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json b/src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json new file mode 100644 index 000000000..1de287cf8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_real_without_fractional_part.json @@ -0,0 +1 @@ +[1.] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json b/src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json new file mode 100644 index 000000000..f682dbdce --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_starting_with_dot.json @@ -0,0 +1 @@ +[.123] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json new file mode 100644 index 000000000..1e42d8182 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha.json @@ -0,0 +1 @@ +[1.2a-3] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json new file mode 100644 index 000000000..b79daccb8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_with_alpha_char.json @@ -0,0 +1 @@ +[1.8011670033376514H-308] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json b/src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json new file mode 100644 index 000000000..7106da1f3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_number_with_leading_zero.json @@ -0,0 +1 @@ +[012] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json b/src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json new file mode 100644 index 000000000..a03a8c03b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_bad_value.json @@ -0,0 +1 @@ +["x", truth] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json new file mode 100644 index 000000000..cc443b483 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_bracket_key.json @@ -0,0 +1 @@ +{[: "x"} diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json new file mode 100644 index 000000000..8d5637708 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_comma_instead_of_colon.json @@ -0,0 +1 @@ +{"x", null} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json new file mode 100644 index 000000000..80e8c7b89 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_double_colon.json @@ -0,0 +1 @@ +{"x"::"b"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_emoji.json b/src/test/resources/test-rom/data/json-parsing/n_object_emoji.json new file mode 100644 index 000000000..cb4078eaa --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_emoji.json @@ -0,0 +1 @@ +{🇨🇭} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json b/src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json new file mode 100644 index 000000000..80c42cbad --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_garbage_at_end.json @@ -0,0 +1 @@ +{"a":"a" 123} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json b/src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json new file mode 100644 index 000000000..77c327599 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_key_with_single_quotes.json @@ -0,0 +1 @@ +{key: 'value'} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json b/src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json new file mode 100644 index 000000000..aa2cb637c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json @@ -0,0 +1 @@ +{"¹":"0",} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json new file mode 100644 index 000000000..b98eff62d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_colon.json @@ -0,0 +1 @@ +{"a" b} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json new file mode 100644 index 000000000..b4fb0f528 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_key.json @@ -0,0 +1 @@ +{:"b"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json new file mode 100644 index 000000000..e3451384f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_semicolon.json @@ -0,0 +1 @@ +{"a" "b"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json b/src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json new file mode 100644 index 000000000..3ef538a60 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_missing_value.json @@ -0,0 +1 @@ +{"a": \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json b/src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json new file mode 100644 index 000000000..f3797b357 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_no-colon.json @@ -0,0 +1 @@ +{"a" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json new file mode 100644 index 000000000..b9945b34b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key.json @@ -0,0 +1 @@ +{1:1} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json new file mode 100644 index 000000000..b37fa86c0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_non_string_key_but_huge_number_instead.json @@ -0,0 +1 @@ +{9999E9999:1} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json b/src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json new file mode 100644 index 000000000..f7d2959d0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_repeated_null_null.json @@ -0,0 +1 @@ +{null:null,null:null} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json b/src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json new file mode 100644 index 000000000..3c9afe8dc --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_several_trailing_commas.json @@ -0,0 +1 @@ +{"id":0,,,,,} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json b/src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json new file mode 100644 index 000000000..e5cdf976a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_single_quote.json @@ -0,0 +1 @@ +{'a':0} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json new file mode 100644 index 000000000..a4b025094 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comma.json @@ -0,0 +1 @@ +{"id":0,} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json new file mode 100644 index 000000000..a372c6553 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment.json @@ -0,0 +1 @@ +{"a":"b"}/**/ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json new file mode 100644 index 000000000..d557f41ca --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_open.json @@ -0,0 +1 @@ +{"a":"b"}/**// \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json new file mode 100644 index 000000000..e335136c0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open.json @@ -0,0 +1 @@ +{"a":"b"}// \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json new file mode 100644 index 000000000..d892e49f1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_trailing_comment_slash_open_incomplete.json @@ -0,0 +1 @@ +{"a":"b"}/ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json b/src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json new file mode 100644 index 000000000..7c639ae64 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_two_commas_in_a_row.json @@ -0,0 +1 @@ +{"a":"b",,"c":"d"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json b/src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json new file mode 100644 index 000000000..8ba137293 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_unquoted_key.json @@ -0,0 +1 @@ +{a: "b"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json b/src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json new file mode 100644 index 000000000..7fe699a6a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_unterminated-value.json @@ -0,0 +1 @@ +{"a":"a \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json b/src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json new file mode 100644 index 000000000..d63f7fbb7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_with_single_string.json @@ -0,0 +1 @@ +{ "foo" : "bar", "a" } \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json new file mode 100644 index 000000000..787c8f0a8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_object_with_trailing_garbage.json @@ -0,0 +1 @@ +{"a":"b"}# \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_single_space.json b/src/test/resources/test-rom/data/json-parsing/n_single_space.json new file mode 100644 index 000000000..0519ecba6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_single_space.json @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json new file mode 100644 index 000000000..acec66d8f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape.json @@ -0,0 +1 @@ +["\uD800\"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json new file mode 100644 index 000000000..e834b05e9 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u.json @@ -0,0 +1 @@ +["\uD800\u"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json new file mode 100644 index 000000000..a04cd3489 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1.json @@ -0,0 +1 @@ +["\uD800\u1"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json new file mode 100644 index 000000000..bfbd23409 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_1_surrogate_then_escape_u1x.json @@ -0,0 +1 @@ +["\uD800\u1x"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json b/src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json new file mode 100644 index 000000000..fd6895693 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_accentuated_char_no_quotes.json @@ -0,0 +1 @@ +[é] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_backslash_00.json b/src/test/resources/test-rom/data/json-parsing/n_string_backslash_00.json new file mode 100644 index 0000000000000000000000000000000000000000..b5bf267b5d4ee922d20cec1543b66d1530ff76cf GIT binary patch literal 6 Ncma!6ieXTS1pox&0a*Y5 literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json b/src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json new file mode 100644 index 000000000..fae291938 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_escape_x.json @@ -0,0 +1 @@ +["\x00"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json new file mode 100644 index 000000000..016fcb47e --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_backslash_bad.json @@ -0,0 +1 @@ +["\\\"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json new file mode 100644 index 000000000..f35ea382b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_ctrl_char_tab.json @@ -0,0 +1 @@ +["\ "] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json new file mode 100644 index 000000000..a27775421 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_escaped_emoji.json @@ -0,0 +1 @@ +["\🌀"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json new file mode 100644 index 000000000..3415c33ca --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escape.json @@ -0,0 +1 @@ +["\"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json new file mode 100644 index 000000000..0f2197ea2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_escaped_character.json @@ -0,0 +1 @@ +["\u00A"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json new file mode 100644 index 000000000..75504a656 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate.json @@ -0,0 +1 @@ +["\uD834\uDd"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json new file mode 100644 index 000000000..bd9656060 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_incomplete_surrogate_escape_invalid.json @@ -0,0 +1 @@ +["\uD800\uD800\x"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json new file mode 100644 index 000000000..0c4300643 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid-utf-8-in-escape.json @@ -0,0 +1 @@ +["\uå"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json new file mode 100644 index 000000000..d1eb60921 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_backslash_esc.json @@ -0,0 +1 @@ +["\a"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json new file mode 100644 index 000000000..7608cb6ba --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_unicode_escape.json @@ -0,0 +1 @@ +["\uqqqq"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json new file mode 100644 index 000000000..2f757a25b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_invalid_utf8_after_escape.json @@ -0,0 +1 @@ +["\å"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json b/src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json new file mode 100644 index 000000000..7b297c636 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_leading_uescaped_thinspace.json @@ -0,0 +1 @@ +[\u0020"asd"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json b/src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json new file mode 100644 index 000000000..01bc70aba --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_no_quotes_with_bad_escape.json @@ -0,0 +1 @@ +[\n] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json b/src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json new file mode 100644 index 000000000..9d68933c4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_single_doublequote.json @@ -0,0 +1 @@ +" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json b/src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json new file mode 100644 index 000000000..caff239bf --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_single_quote.json @@ -0,0 +1 @@ +['single quote'] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json b/src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json new file mode 100644 index 000000000..f2ba8f84a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_single_string_no_double_quotes.json @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json b/src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json new file mode 100644 index 000000000..db62a46fc --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_start_escape_unclosed.json @@ -0,0 +1 @@ +["\ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_crtl_char.json b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_crtl_char.json new file mode 100644 index 0000000000000000000000000000000000000000..9f21348071d3d736bdd469f159cea674c09237af GIT binary patch literal 7 Ocma!6N@Pe>iUj}$`2oKG literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json new file mode 100644 index 000000000..700d36086 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_newline.json @@ -0,0 +1,2 @@ +["new +line"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json new file mode 100644 index 000000000..160264a2d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_unescaped_tab.json @@ -0,0 +1 @@ +[" "] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json b/src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json new file mode 100644 index 000000000..17332bb17 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_unicode_CapitalU.json @@ -0,0 +1 @@ +"\UA66D" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json new file mode 100644 index 000000000..efe3bd272 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_string_with_trailing_garbage.json @@ -0,0 +1 @@ +""x \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json b/src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json new file mode 100644 index 000000000..a4823eecc --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_100000_opening_arrays.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json b/src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json new file mode 100644 index 000000000..81156a699 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_U+2060_word_joined.json @@ -0,0 +1 @@ +[â ] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json b/src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_UTF8_BOM_no_data.json @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json new file mode 100644 index 000000000..a56fef0b0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_..json @@ -0,0 +1 @@ +<.> \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json new file mode 100644 index 000000000..617f26254 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_angle_bracket_null.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json new file mode 100644 index 000000000..5a745e6f3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_array_trailing_garbage.json @@ -0,0 +1 @@ +[1]x \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json new file mode 100644 index 000000000..6cfb1398d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_extra_array_close.json @@ -0,0 +1 @@ +[1]] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json new file mode 100644 index 000000000..ba6b1788b --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_array_with_unclosed_string.json @@ -0,0 +1 @@ +["asd] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json b/src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json new file mode 100644 index 000000000..ef2ab62fe --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_ascii-unicode-identifier.json @@ -0,0 +1 @@ +aÃ¥ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json b/src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json new file mode 100644 index 000000000..7cd88469a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_capitalized_True.json @@ -0,0 +1 @@ +[True] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json new file mode 100644 index 000000000..d2af0c646 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_close_unopened_array.json @@ -0,0 +1 @@ +1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json b/src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json new file mode 100644 index 000000000..ac61b8200 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_comma_instead_of_closing_brace.json @@ -0,0 +1 @@ +{"x": true, \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json new file mode 100644 index 000000000..058d1626e --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_double_array.json @@ -0,0 +1 @@ +[][] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json new file mode 100644 index 000000000..54caf60b1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_end_array.json @@ -0,0 +1 @@ +] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json b/src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json new file mode 100644 index 000000000..bfcdd514f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_incomplete_UTF8_BOM.json @@ -0,0 +1 @@ +ï»{} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json new file mode 100644 index 000000000..8b1296cad --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-invalid-utf-8.json @@ -0,0 +1 @@ +å \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json new file mode 100644 index 000000000..8e2f0bef1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_lone-open-bracket.json @@ -0,0 +1 @@ +[ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_no_data.json b/src/test/resources/test-rom/data/json-parsing/n_structure_no_data.json new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_null-byte-outside-string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_null-byte-outside-string.json new file mode 100644 index 0000000000000000000000000000000000000000..326db14422a756e9bd39221edf37b844cb8471c7 GIT binary patch literal 3 Kcma!Mhy?%vaR9jh literal 0 HcmV?d00001 diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json new file mode 100644 index 000000000..0746539d2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_number_with_trailing_garbage.json @@ -0,0 +1 @@ +2@ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json new file mode 100644 index 000000000..aa9ebaec5 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_followed_by_closing_object.json @@ -0,0 +1 @@ +{}} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json new file mode 100644 index 000000000..17d045147 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_unclosed_no_value.json @@ -0,0 +1 @@ +{"": \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json new file mode 100644 index 000000000..ed1b569b7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_comment.json @@ -0,0 +1 @@ +{"a":/*comment*/"b"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json new file mode 100644 index 000000000..9ca2336d7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_object_with_trailing_garbage.json @@ -0,0 +1 @@ +{"a": true} "x" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json new file mode 100644 index 000000000..8bebe3af0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_apostrophe.json @@ -0,0 +1 @@ +[' \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json new file mode 100644 index 000000000..6295fdc36 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_comma.json @@ -0,0 +1 @@ +[, \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json new file mode 100644 index 000000000..e870445b2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_object.json @@ -0,0 +1 @@ +[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"": diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json new file mode 100644 index 000000000..7a63c8c57 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_object.json @@ -0,0 +1 @@ +[{ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json new file mode 100644 index 000000000..9822a6baf --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_open_string.json @@ -0,0 +1 @@ +["a \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json new file mode 100644 index 000000000..42a619362 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_array_string.json @@ -0,0 +1 @@ +["a" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json new file mode 100644 index 000000000..81750b96f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object.json @@ -0,0 +1 @@ +{ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json new file mode 100644 index 000000000..eebc700a1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_close_array.json @@ -0,0 +1 @@ +{] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json new file mode 100644 index 000000000..47bc9106f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_comma.json @@ -0,0 +1 @@ +{, \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json new file mode 100644 index 000000000..381ede5de --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_array.json @@ -0,0 +1 @@ +{[ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json new file mode 100644 index 000000000..328c30cd7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_open_string.json @@ -0,0 +1 @@ +{"a \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json new file mode 100644 index 000000000..9dba17090 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_object_string_with_apostrophes.json @@ -0,0 +1 @@ +{'a' \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json b/src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json new file mode 100644 index 000000000..841fd5f86 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_open_open.json @@ -0,0 +1 @@ +["\{["\{["\{["\{ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json b/src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json new file mode 100644 index 000000000..92a39f398 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_single_eacute.json @@ -0,0 +1 @@ +é \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json b/src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json new file mode 100644 index 000000000..f59ec20aa --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_single_star.json @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json b/src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json new file mode 100644 index 000000000..898611087 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_trailing_#.json @@ -0,0 +1 @@ +{"a":"b"}#{} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json b/src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json new file mode 100644 index 000000000..df2f0f242 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_uescaped_LF_before_string.json @@ -0,0 +1 @@ +[\u000A""] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json new file mode 100644 index 000000000..11209515c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array.json @@ -0,0 +1 @@ +[1 \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json new file mode 100644 index 000000000..0d591762c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_partial_null.json @@ -0,0 +1 @@ +[ false, nul \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json new file mode 100644 index 000000000..a2ff8504a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_false.json @@ -0,0 +1 @@ +[ true, fals \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json new file mode 100644 index 000000000..3149e8f5a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_array_unfinished_true.json @@ -0,0 +1 @@ +[ false, tru \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json new file mode 100644 index 000000000..694d69dbd --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unclosed_object.json @@ -0,0 +1 @@ +{"asd":"asd" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json b/src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json new file mode 100644 index 000000000..7284aea33 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_unicode-identifier.json @@ -0,0 +1 @@ +Ã¥ \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json new file mode 100644 index 000000000..81156a699 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_U+2060_word_joiner.json @@ -0,0 +1 @@ +[â ] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json new file mode 100644 index 000000000..a9ea535d1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/n_structure_whitespace_formfeed.json @@ -0,0 +1 @@ +[ ] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/skip.lua b/src/test/resources/test-rom/data/json-parsing/skip.lua new file mode 100644 index 000000000..3b147c7b4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/skip.lua @@ -0,0 +1,21 @@ +local skip = { + -- Should fail, but pass. + -- We're too flexible on number formatting, but it's not a major concern. + "n_number_-01", + "n_number_-2.", + "n_number_0.e1", + "n_number_2.e3", + "n_number_2.e+3", + "n_number_2.e-3", + "n_number_neg_int_starting_with_zero", + "n_number_real_without_fractional_part", + "n_number_with_leading_zero", + + -- Should pass, but fail. + -- These two are due to stack overflows within the parser. + "n_structure_open_array_object", + "n_structure_100000_opening_arrays", +} + +for _, k in pairs(skip) do skip[k] = true end +return skip diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json b/src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json new file mode 100644 index 000000000..582290798 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_arraysWithSpaces.json @@ -0,0 +1 @@ +[[] ] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json b/src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json new file mode 100644 index 000000000..93b6be2bc --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_empty-string.json @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_empty.json b/src/test/resources/test-rom/data/json-parsing/y_array_empty.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json b/src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json new file mode 100644 index 000000000..eac5f7b46 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_ending_with_newline.json @@ -0,0 +1 @@ +["a"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_false.json b/src/test/resources/test-rom/data/json-parsing/y_array_false.json new file mode 100644 index 000000000..67b2f0760 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_false.json @@ -0,0 +1 @@ +[false] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json b/src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json new file mode 100644 index 000000000..d3c1e2648 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_heterogeneous.json @@ -0,0 +1 @@ +[null, 1, "1", {}] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_null.json b/src/test/resources/test-rom/data/json-parsing/y_array_null.json new file mode 100644 index 000000000..500db4a86 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_null.json @@ -0,0 +1 @@ +[null] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json new file mode 100644 index 000000000..994825500 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_1_and_newline.json @@ -0,0 +1,2 @@ +[1 +] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json new file mode 100644 index 000000000..18bfe6422 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_leading_space.json @@ -0,0 +1 @@ + [1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json new file mode 100644 index 000000000..99f6c5d1d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_several_null.json @@ -0,0 +1 @@ +[1,null,null,null,2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json b/src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json new file mode 100644 index 000000000..de9e7a944 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_array_with_trailing_space.json @@ -0,0 +1 @@ +[2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number.json b/src/test/resources/test-rom/data/json-parsing/y_number.json new file mode 100644 index 000000000..e5f5cc334 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number.json @@ -0,0 +1 @@ +[123e65] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json b/src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json new file mode 100644 index 000000000..d1d396706 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_0e+1.json @@ -0,0 +1 @@ +[0e+1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_0e1.json b/src/test/resources/test-rom/data/json-parsing/y_number_0e1.json new file mode 100644 index 000000000..3283a7936 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_0e1.json @@ -0,0 +1 @@ +[0e1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_after_space.json b/src/test/resources/test-rom/data/json-parsing/y_number_after_space.json new file mode 100644 index 000000000..623570d96 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_after_space.json @@ -0,0 +1 @@ +[ 4] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json b/src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json new file mode 100644 index 000000000..96555ff78 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_double_close_to_zero.json @@ -0,0 +1 @@ +[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json new file mode 100644 index 000000000..a4ca9e754 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_int_with_exp.json @@ -0,0 +1 @@ +[20e1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json b/src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json new file mode 100644 index 000000000..37af1312a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_minus_zero.json @@ -0,0 +1 @@ +[-0] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json b/src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json new file mode 100644 index 000000000..8e30f8bd9 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_negative_int.json @@ -0,0 +1 @@ +[-123] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json b/src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json new file mode 100644 index 000000000..99d21a2a0 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_negative_one.json @@ -0,0 +1 @@ +[-1] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json b/src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json new file mode 100644 index 000000000..37af1312a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_negative_zero.json @@ -0,0 +1 @@ +[-0] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json new file mode 100644 index 000000000..6edbdfcb1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e.json @@ -0,0 +1 @@ +[1E22] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json new file mode 100644 index 000000000..0a01bd3ef --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_neg_exp.json @@ -0,0 +1 @@ +[1E-2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json new file mode 100644 index 000000000..5a8fc0972 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_capital_e_pos_exp.json @@ -0,0 +1 @@ +[1E+2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json new file mode 100644 index 000000000..da2522d61 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_exponent.json @@ -0,0 +1 @@ +[123e45] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json new file mode 100644 index 000000000..3944a7a45 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_fraction_exponent.json @@ -0,0 +1 @@ +[123.456e78] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json new file mode 100644 index 000000000..ca40d3c25 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_neg_exp.json @@ -0,0 +1 @@ +[1e-2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json b/src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json new file mode 100644 index 000000000..343601d51 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_real_pos_exponent.json @@ -0,0 +1 @@ +[1e+2] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json b/src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json new file mode 100644 index 000000000..e47f69afc --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_simple_int.json @@ -0,0 +1 @@ +[123] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json b/src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json new file mode 100644 index 000000000..b02878e5f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_number_simple_real.json @@ -0,0 +1 @@ +[123.456789] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object.json b/src/test/resources/test-rom/data/json-parsing/y_object.json new file mode 100644 index 000000000..78262eda3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object.json @@ -0,0 +1 @@ +{"asd":"sdf", "dfg":"fgh"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_basic.json b/src/test/resources/test-rom/data/json-parsing/y_object_basic.json new file mode 100644 index 000000000..646bbe7bb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_basic.json @@ -0,0 +1 @@ +{"asd":"sdf"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json new file mode 100644 index 000000000..bbc2e1ce4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key.json @@ -0,0 +1 @@ +{"a":"b","a":"c"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json new file mode 100644 index 000000000..211581c20 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_duplicated_key_and_value.json @@ -0,0 +1 @@ +{"a":"b","a":"b"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_empty.json b/src/test/resources/test-rom/data/json-parsing/y_object_empty.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json b/src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json new file mode 100644 index 000000000..c0013d3b8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_empty_key.json @@ -0,0 +1 @@ +{"":0} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json b/src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json new file mode 100644 index 000000000..593f0f67f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_escaped_null_in_key.json @@ -0,0 +1 @@ +{"foo\u0000bar": 42} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json b/src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json new file mode 100644 index 000000000..a0d3531c3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_extreme_numbers.json @@ -0,0 +1 @@ +{ "min": -1.0e+28, "max": 1.0e+28 } \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json b/src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json new file mode 100644 index 000000000..bdc4a0871 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_long_strings.json @@ -0,0 +1 @@ +{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_simple.json b/src/test/resources/test-rom/data/json-parsing/y_object_simple.json new file mode 100644 index 000000000..dacac917f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_simple.json @@ -0,0 +1 @@ +{"a":[]} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json b/src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json new file mode 100644 index 000000000..8effdb297 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_string_unicode.json @@ -0,0 +1 @@ +{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json b/src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json new file mode 100644 index 000000000..246ec6b34 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_object_with_newlines.json @@ -0,0 +1,3 @@ +{ +"a": "b" +} \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json b/src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json new file mode 100644 index 000000000..9967ddeb8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_1_2_3_bytes_UTF-8_sequences.json @@ -0,0 +1 @@ +["\u0060\u012a\u12AB"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json new file mode 100644 index 000000000..996875cc8 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pair.json @@ -0,0 +1 @@ +["\uD801\udc37"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json new file mode 100644 index 000000000..3401021ec --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_accepted_surrogate_pairs.json @@ -0,0 +1 @@ +["\ud83d\ude39\ud83d\udc8d"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json b/src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json new file mode 100644 index 000000000..7f495532f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_allowed_escapes.json @@ -0,0 +1 @@ +["\"\\\/\b\f\n\r\t"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json new file mode 100644 index 000000000..d4439eda7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_and_u_escaped_zero.json @@ -0,0 +1 @@ +["\\u0000"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json new file mode 100644 index 000000000..ae03243b6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_backslash_doublequotes.json @@ -0,0 +1 @@ +["\""] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_comments.json b/src/test/resources/test-rom/data/json-parsing/y_string_comments.json new file mode 100644 index 000000000..2260c20c2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_comments.json @@ -0,0 +1 @@ +["a/*b*/c/*d//e"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json new file mode 100644 index 000000000..6715d6f40 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_a.json @@ -0,0 +1 @@ +["\\a"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json new file mode 100644 index 000000000..44ca56c4d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_double_escape_n.json @@ -0,0 +1 @@ +["\\n"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json new file mode 100644 index 000000000..5b014a9c2 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_control_character.json @@ -0,0 +1 @@ +["\u0012"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json new file mode 100644 index 000000000..2ff52e2c5 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_escaped_noncharacter.json @@ -0,0 +1 @@ +["\uFFFF"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_in_array.json b/src/test/resources/test-rom/data/json-parsing/y_string_in_array.json new file mode 100644 index 000000000..21d7ae4cd --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_in_array.json @@ -0,0 +1 @@ +["asd"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json b/src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json new file mode 100644 index 000000000..9e1887c1e --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_in_array_with_leading_space.json @@ -0,0 +1 @@ +[ "asd"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json b/src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json new file mode 100644 index 000000000..3919cef76 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_last_surrogates_1_and_2.json @@ -0,0 +1 @@ +["\uDBFF\uDFFF"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json b/src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json new file mode 100644 index 000000000..2085ab1a1 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_nbsp_uescaped.json @@ -0,0 +1 @@ +["new\u00A0line"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json new file mode 100644 index 000000000..059e4d9dd --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json @@ -0,0 +1 @@ +["ô¿¿"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json new file mode 100644 index 000000000..4c913bd41 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_nonCharacterInUTF-8_U+FFFF.json @@ -0,0 +1 @@ +["ï¿¿"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json b/src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json new file mode 100644 index 000000000..c1ad84404 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_null_escape.json @@ -0,0 +1 @@ +["\u0000"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json b/src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json new file mode 100644 index 000000000..157185923 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_one-byte-utf-8.json @@ -0,0 +1 @@ +["\u002c"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_pi.json b/src/test/resources/test-rom/data/json-parsing/y_string_pi.json new file mode 100644 index 000000000..9df11ae88 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_pi.json @@ -0,0 +1 @@ +["Ï€"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json b/src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json new file mode 100644 index 000000000..10a33a171 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json @@ -0,0 +1 @@ +["𛿿"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json b/src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json new file mode 100644 index 000000000..8cadf7d05 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_simple_ascii.json @@ -0,0 +1 @@ +["asd "] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_space.json b/src/test/resources/test-rom/data/json-parsing/y_string_space.json new file mode 100644 index 000000000..efd782cc3 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_space.json @@ -0,0 +1 @@ +" " \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json b/src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json new file mode 100644 index 000000000..7620b6655 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json @@ -0,0 +1 @@ +["\uD834\uDd1e"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json b/src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json new file mode 100644 index 000000000..108f1d67d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_three-byte-utf-8.json @@ -0,0 +1 @@ +["\u0821"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json b/src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json new file mode 100644 index 000000000..461503c31 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_two-byte-utf-8.json @@ -0,0 +1 @@ +["\u0123"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json b/src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json new file mode 100644 index 000000000..897b6021a --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_u+2028_line_sep.json @@ -0,0 +1 @@ +["
"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json b/src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json new file mode 100644 index 000000000..8cd998c89 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_u+2029_par_sep.json @@ -0,0 +1 @@ +["
"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json b/src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json new file mode 100644 index 000000000..f7b41a02f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_uEscape.json @@ -0,0 +1 @@ +["\u0061\u30af\u30EA\u30b9"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json b/src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json new file mode 100644 index 000000000..3a5a220b6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_uescaped_newline.json @@ -0,0 +1 @@ +["new\u000Aline"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json b/src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json new file mode 100644 index 000000000..7d064f498 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unescaped_char_delete.json @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode.json new file mode 100644 index 000000000..3598095b7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode.json @@ -0,0 +1 @@ +["\uA66D"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json new file mode 100644 index 000000000..0bb3b51e7 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicodeEscapedBackslash.json @@ -0,0 +1 @@ +["\u005C"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json new file mode 100644 index 000000000..a7dcb9768 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_2.json @@ -0,0 +1 @@ +["â‚㈴â‚"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json new file mode 100644 index 000000000..9a8370b96 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+10FFFE_nonchar.json @@ -0,0 +1 @@ +["\uDBFF\uDFFE"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json new file mode 100644 index 000000000..c51f8ae45 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+1FFFE_nonchar.json @@ -0,0 +1 @@ +["\uD83F\uDFFE"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json new file mode 100644 index 000000000..626d5f815 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json @@ -0,0 +1 @@ +["\u200B"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json new file mode 100644 index 000000000..1e23972c6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+2064_invisible_plus.json @@ -0,0 +1 @@ +["\u2064"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json new file mode 100644 index 000000000..18ef151b4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FDD0_nonchar.json @@ -0,0 +1 @@ +["\uFDD0"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json new file mode 100644 index 000000000..13d261fda --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_U+FFFE_nonchar.json @@ -0,0 +1 @@ +["\uFFFE"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json new file mode 100644 index 000000000..4e6257856 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_unicode_escaped_double_quote.json @@ -0,0 +1 @@ +["\u0022"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_utf8.json b/src/test/resources/test-rom/data/json-parsing/y_string_utf8.json new file mode 100644 index 000000000..40878435f --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_utf8.json @@ -0,0 +1 @@ +["€ð„ž"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json b/src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json new file mode 100644 index 000000000..8bd24907d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_string_with_del_character.json @@ -0,0 +1 @@ +["aa"] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json new file mode 100644 index 000000000..02e4a84d6 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_false.json @@ -0,0 +1 @@ +false \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json new file mode 100644 index 000000000..f70d7bba4 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_int.json @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json new file mode 100644 index 000000000..b5135a207 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_negative_real.json @@ -0,0 +1 @@ +-0.1 \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_null.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json new file mode 100644 index 000000000..b6e982ca9 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_string.json @@ -0,0 +1 @@ +"asd" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json new file mode 100644 index 000000000..f32a5804e --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_lonely_true.json @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json b/src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json new file mode 100644 index 000000000..3cc762b55 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_string_empty.json @@ -0,0 +1 @@ +"" \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json b/src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json new file mode 100644 index 000000000..0c3426d4c --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_trailing_newline.json @@ -0,0 +1 @@ +["a"] diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json b/src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json new file mode 100644 index 000000000..de601e305 --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_true_in_array.json @@ -0,0 +1 @@ +[true] \ No newline at end of file diff --git a/src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json b/src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json new file mode 100644 index 000000000..2bedf7f2d --- /dev/null +++ b/src/test/resources/test-rom/data/json-parsing/y_structure_whitespace_array.json @@ -0,0 +1 @@ + [] \ No newline at end of file diff --git a/src/test/resources/test-rom/spec/apis/textutils_spec.lua b/src/test/resources/test-rom/spec/apis/textutils_spec.lua index ab881d60b..bdef2dbd2 100644 --- a/src/test/resources/test-rom/spec/apis/textutils_spec.lua +++ b/src/test/resources/test-rom/spec/apis/textutils_spec.lua @@ -70,6 +70,69 @@ describe("The textutils library", function() expect.error(textutils.serialiseJSON, nil):eq("bad argument #1 (expected table, string, number or boolean, got nil)") expect.error(textutils.serialiseJSON, "", 1):eq("bad argument #2 (expected boolean, got number)") end) + + it("serializes empty arrays", function() + expect(textutils.serializeJSON(textutils.empty_json_array)):eq("[]") + end) + + it("serializes null", function() + expect(textutils.serializeJSON(textutils.json_null)):eq("null") + end) + end) + + describe("textutils.unserializeJSON", function() + describe("parses", function() + it("a list of primitives", function() + expect(textutils.unserializeJSON('[1, true, false, "hello"]')):same { 1, true, false, "hello" } + end) + + it("null when parse_null is true", function() + expect(textutils.unserializeJSON("null", { parse_null = true })):eq(textutils.json_null) + end) + + it("null when parse_null is false", function() + expect(textutils.unserializeJSON("null", { parse_null = false })):eq(nil) + end) + + it("an empty array", function() + expect(textutils.unserializeJSON("[]", { parse_null = false })):eq(textutils.empty_json_array) + end) + + it("basic objects", function() + expect(textutils.unserializeJSON([[{ "a": 1, "b":2 }]])):same { a = 1, b = 2 } + end) + end) + + describe("parses using NBT-style syntax", function() + it("basic objects", function() + expect(textutils.unserializeJSON([[{ a: 1, b:2 }]], { nbt_style = true })):same { a = 1, b = 2 } + end) + + it("suffixed numbers", function() + expect(textutils.unserializeJSON("1b", { nbt_style = true })):eq(1) + end) + end) + + describe("passes nst/JSONTestSuite", function() + local search_path = "test-rom/data/json-parsing" + local skip = dofile(search_path .. "/skip.lua") + for _, file in pairs(fs.find(search_path .. "/*.json")) do + local name = fs.getName(file):sub(1, -6); + (skip[name] and pending or it)(name, function() + local h = io.open(file, "r") + local contents = h:read("*a") + h:close() + + local res, err = textutils.unserializeJSON(contents) + local kind = fs.getName(file):sub(1, 1) + if kind == "n" then + expect(res):eq(nil) + elseif kind == "y" then + if err ~= nil then fail("Expected test to pass, but failed with " .. err) end + end + end) + end + end) end) describe("textutils.urlEncode", function() diff --git a/tools/check-lines.py b/tools/check-lines.py index e1fcba620..d50d4610d 100644 --- a/tools/check-lines.py +++ b/tools/check-lines.py @@ -3,10 +3,10 @@ import pathlib, sys problems = False # Skip images and files without extensions -exclude = [ ".png", "" ] +exclude = [ "*.png", "**/data/json-parsing/*.json" ] -for path in pathlib.Path(".").glob("src/**/*"): - if path.is_dir() or path.suffix in exclude: +for path in pathlib.Path("src").glob("**/*"): + if path.is_dir() or path.suffix == "" or any(path.match(x) for x in exclude): continue with path.open(encoding="utf-8") as file: From c5f918ad9519db6040dd69daaff5eb34116d9b46 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Sun, 19 Apr 2020 20:11:49 +0100 Subject: [PATCH 28/35] Increase the maximum limit for websocket messages The constructor for the websocket handshaker has a default maxFramePayloadLength of 64kb, hence me being confused. Closes #376 --- src/main/java/dan200/computercraft/ComputerCraft.java | 3 +-- .../computercraft/core/apis/http/websocket/Websocket.java | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 626d387f0..430208196 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -22,7 +22,6 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.event.TurtleAction; import dan200.computercraft.core.apis.AddressPredicate; import dan200.computercraft.core.apis.ApiFactories; -import dan200.computercraft.core.apis.http.websocket.Websocket; import dan200.computercraft.core.computer.MainThread; import dan200.computercraft.core.filesystem.ComboMount; import dan200.computercraft.core.filesystem.FileMount; @@ -128,7 +127,7 @@ public class ComputerCraft public static long httpMaxDownload = 16 * 1024 * 1024; public static long httpMaxUpload = 4 * 1024 * 1024; public static int httpMaxWebsockets = 4; - public static int httpMaxWebsocketMessage = Websocket.MAX_MESSAGE_SIZE; + public static int httpMaxWebsocketMessage = 128 * 1024; public static boolean enableCommandBlock = false; public static int modem_range = 64; diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java index 67a15e963..c1d91a078 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/Websocket.java @@ -40,7 +40,11 @@ import java.util.concurrent.Future; */ public class Websocket extends Resource { - public static final int MAX_MESSAGE_SIZE = 64 * 1024; + /** + * We declare the maximum size to be 2^30 bytes. While messages can be much longer, we set an arbitrary limit as + * working with larger messages (especially within a Lua VM) is absurd. + */ + public static final int MAX_MESSAGE_SIZE = 1 << 30; static final String SUCCESS_EVENT = "websocket_success"; static final String FAILURE_EVENT = "websocket_failure"; From cea8be7efab3ac40c7acbf3a92fa4c1fd384f92d Mon Sep 17 00:00:00 2001 From: SquidDev Date: Mon, 20 Apr 2020 12:01:44 +0100 Subject: [PATCH 29/35] Fix terrible grammar in the README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 78a9cb32e..67bfd5644 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ several features have been included, such as full block modems, the Cobalt runti computers. ## Contributing -Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to developing the -mod, [check out the instructions here](CONTRIBUTING.md#developing). +Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to get started +developing the mod, [check out the instructions here](CONTRIBUTING.md#developing). ## Community If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about From 7c1154ddfc88ba93c6830b94a83c8c44da18403c Mon Sep 17 00:00:00 2001 From: SquidDev Date: Tue, 21 Apr 2020 08:51:49 +0100 Subject: [PATCH 30/35] Avoid shadowing of names in peripheral.isPresent Fixes #415 --- .../assets/computercraft/lua/rom/apis/peripheral.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua index b9039a52f..53b849adc 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua @@ -54,9 +54,9 @@ function isPresent(name) end for n = 1, #sides do - local name = sides[n] - if native.getType(name) == "modem" and not native.call(name, "isWireless") and - native.call(name, "isPresentRemote", name) + local side = sides[n] + if native.getType(side) == "modem" and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", name) then return true end From 11bf601db905e7880c413fdf5b58fb01e812f9c0 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 21 Apr 2020 10:43:26 +0100 Subject: [PATCH 31/35] Backport 1.15's terminal rendering code (#412) This is a backport of 1.15's terminal rendering code with some further improvements. This duplicates a fair bit of code, and is much more efficient. I expect the work done in #409 will supersede this, but that's unlikely to make its way into the next release so it's worth getting this in for now. - Refactor a lot of common terminal code into `FixedWithFontRenderer`. This shouldn't change any behaviour, but makes a lot of our terminal renderers (printed pages, terminals, monitors) a lot cleaner. - Terminal rendering is done using a single mode/vertex format. Rather than drawing an untextured quad for the background colours, we use an entirely white piece of the terminal font. This allows us to batch draws together more elegantly. - Some minor optimisations: - Skip rendering `"\0"` and `" "` characters. These characters occur pretty often, especially on blank monitors and, as the font is empty here, it is safe to skip them. - Batch together adjacent background cells of the same colour. Again, most terminals will have large runs of the same colour, so this is a worthwhile optimisation. These optimisations do mean that terminal performance is no longer consistent as "noisy" terminals will have worse performance. This is annoying, but still worthwhile. - Switch monitor rendering over to use VBOs. We also add a config option to switch between rendering backends. By default we'll choose the best one compatible with your GPU, but there is a config option to switch between VBOS (reasonable performance) and display lists (bad). When benchmarking 30 full-sized monitors rendering a static image, this improves my FPS[^1] from 7 to 95. This is obviously an extreme case - monitor updates are still slow, and so more frequently updating screens will still be less than stellar. [^1]: My graphics card is an Intel HD Graphics 520. Obviously numbers will vary. --- .../dan200/computercraft/ComputerCraft.java | 2 + .../client/gui/FixedWidthFontRenderer.java | 359 +++++++++++------ .../client/gui/widgets/WidgetTerminal.java | 101 +---- .../client/render/ItemPocketRenderer.java | 67 +--- .../client/render/PrintoutRenderer.java | 15 +- .../render/TileEntityMonitorRenderer.java | 361 +++++++----------- .../dan200/computercraft/shared/Config.java | 11 +- .../peripheral/monitor/ClientMonitor.java | 86 +++-- .../peripheral/monitor/MonitorRenderer.java | 105 +++++ .../computercraft/shared/util/Palette.java | 4 +- .../assets/computercraft/lang/en_us.lang | 4 + .../textures/gui/term_background.png | Bin 123 -> 0 bytes .../computercraft/textures/gui/term_font.png | Bin 1245 -> 3904 bytes 13 files changed, 594 insertions(+), 521 deletions(-) create mode 100644 src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java delete mode 100644 src/main/resources/assets/computercraft/textures/gui/term_background.png diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 430208196..2f3e85b66 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -46,6 +46,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.ItemCable; import dan200.computercraft.shared.peripheral.modem.wireless.BlockAdvancedModem; import dan200.computercraft.shared.peripheral.modem.wireless.ItemAdvancedModem; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; +import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.peripherals.PocketModem; import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; @@ -135,6 +136,7 @@ public class ComputerCraft public static int modem_rangeDuringStorm = 64; public static int modem_highAltitudeRangeDuringStorm = 384; public static int maxNotesPerTick = 8; + public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST; public static boolean turtlesNeedFuel = true; public static int turtleFuelLimit = 20000; diff --git a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java index 57760da3f..c214505d7 100644 --- a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java +++ b/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java @@ -5,195 +5,324 @@ */ package dan200.computercraft.client.gui; +import dan200.computercraft.client.FrameInfo; +import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; +import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Palette; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.texture.TextureManager; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL11; -import java.util.Arrays; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; public final class FixedWidthFontRenderer { private static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" ); - public static final ResourceLocation BACKGROUND = new ResourceLocation( "computercraft", "textures/gui/term_background.png" ); + + /** + * Like {@link DefaultVertexFormats#POSITION_TEX_COLOR}, but flipped. This is backported from 1.15, hence the + * custom format. + */ + public static final VertexFormat POSITION_COLOR_TEX = new VertexFormat(); + + static + { + POSITION_COLOR_TEX.addElement( DefaultVertexFormats.POSITION_3F ); + POSITION_COLOR_TEX.addElement( DefaultVertexFormats.COLOR_4UB ); + POSITION_COLOR_TEX.addElement( DefaultVertexFormats.TEX_2F ); + } public static final int FONT_HEIGHT = 9; public static final int FONT_WIDTH = 6; + public static final float WIDTH = 256.0f; - private static FixedWidthFontRenderer instance; - - public static FixedWidthFontRenderer instance() - { - if( instance != null ) return instance; - return instance = new FixedWidthFontRenderer(); - } - - private final TextureManager m_textureManager; + public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; + public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; private FixedWidthFontRenderer() { - m_textureManager = Minecraft.getMinecraft().getTextureManager(); } - private static void greyscaleify( double[] rgb ) + private static float toGreyscale( double[] rgb ) { - Arrays.fill( rgb, (rgb[0] + rgb[1] + rgb[2]) / 3.0f ); + return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); } - private void drawChar( BufferBuilder renderer, double x, double y, int index, int color, Palette p, boolean greyscale ) + private static int getColour( char c ) { + int i = "0123456789abcdef".indexOf( c ); + return i < 0 ? 0 : 15 - i; + } + + private static void drawChar( BufferBuilder buffer, float x, float y, int index, float r, float g, float b ) + { + // Short circuit to avoid the common case - the texture should be blank here after all. + if( index == '\0' || index == ' ' ) return; + int column = index % 16; int row = index / 16; - double[] colour = p.getColour( 15 - color ); - if( greyscale ) - { - greyscaleify( colour ); - } - float r = (float) colour[0]; - float g = (float) colour[1]; - float b = (float) colour[2]; - int xStart = 1 + column * (FONT_WIDTH + 2); int yStart = 1 + row * (FONT_HEIGHT + 2); - renderer.pos( x, y, 0.0 ).tex( xStart / 256.0, yStart / 256.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x, y + FONT_HEIGHT, 0.0 ).tex( xStart / 256.0, (yStart + FONT_HEIGHT) / 256.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x + FONT_WIDTH, y, 0.0 ).tex( (xStart + FONT_WIDTH) / 256.0, yStart / 256.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x + FONT_WIDTH, y, 0.0 ).tex( (xStart + FONT_WIDTH) / 256.0, yStart / 256.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x, y + FONT_HEIGHT, 0.0 ).tex( xStart / 256.0, (yStart + FONT_HEIGHT) / 256.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x + FONT_WIDTH, y + FONT_HEIGHT, 0.0 ).tex( (xStart + FONT_WIDTH) / 256.0, (yStart + FONT_HEIGHT) / 256.0 ).color( r, g, b, 1.0f ).endVertex(); + buffer.pos( x, y, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, yStart / WIDTH ).endVertex(); + buffer.pos( x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex(); + buffer.pos( x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex(); + buffer.pos( x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).endVertex(); + buffer.pos( x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex(); + buffer.pos( x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).tex( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).endVertex(); } - private void drawQuad( BufferBuilder renderer, double x, double y, int color, double width, Palette p, boolean greyscale ) + private static void drawQuad( BufferBuilder buffer, float x, float y, float width, float height, float r, float g, float b ) { - double[] colour = p.getColour( 15 - color ); + buffer.pos( x, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_START ).endVertex(); + buffer.pos( x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex(); + buffer.pos( x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex(); + buffer.pos( x + width, y, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_START ).endVertex(); + buffer.pos( x, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_START, BACKGROUND_END ).endVertex(); + buffer.pos( x + width, y + height, 0 ).color( r, g, b, 1.0f ).tex( BACKGROUND_END, BACKGROUND_END ).endVertex(); + } + + private static void drawQuad( BufferBuilder buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex ) + { + double[] colour = palette.getColour( getColour( colourIndex ) ); + float r, g, b; if( greyscale ) { - greyscaleify( colour ); + r = g = b = toGreyscale( colour ); + } + else + { + r = (float) colour[0]; + g = (float) colour[1]; + b = (float) colour[2]; } - float r = (float) colour[0]; - float g = (float) colour[1]; - float b = (float) colour[2]; - renderer.pos( x, y, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x, y + FONT_HEIGHT, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x + width, y, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x + width, y, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x, y + FONT_HEIGHT, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( x + width, y + FONT_HEIGHT, 0.0 ).color( r, g, b, 1.0f ).endVertex(); + drawQuad( buffer, x, y, width, height, r, g, b ); } - private boolean isGreyScale( int colour ) + private static void drawBackground( + @Nonnull BufferBuilder renderer, float x, float y, + @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, + float leftMarginSize, float rightMarginSize, float height + ) { - return colour == 0 || colour == 15 || colour == 7 || colour == 8; - } + if( leftMarginSize > 0 ) + { + drawQuad( renderer, x - leftMarginSize, y, leftMarginSize, height, palette, greyscale, backgroundColour.charAt( 0 ) ); + } - public void drawStringBackgroundPart( int x, int y, TextBuffer backgroundColour, double leftMarginSize, double rightMarginSize, boolean greyScale, Palette p ) - { - // Draw the quads - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder renderer = tessellator.getBuffer(); - renderer.begin( GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION_COLOR ); - if( leftMarginSize > 0.0 ) + if( rightMarginSize > 0 ) { - int colour1 = "0123456789abcdef".indexOf( backgroundColour.charAt( 0 ) ); - if( colour1 < 0 || (greyScale && !isGreyScale( colour1 )) ) - { - colour1 = 15; - } - drawQuad( renderer, x - leftMarginSize, y, colour1, leftMarginSize, p, greyScale ); - } - if( rightMarginSize > 0.0 ) - { - int colour2 = "0123456789abcdef".indexOf( backgroundColour.charAt( backgroundColour.length() - 1 ) ); - if( colour2 < 0 || (greyScale && !isGreyScale( colour2 )) ) - { - colour2 = 15; - } - drawQuad( renderer, x + backgroundColour.length() * FONT_WIDTH, y, colour2, rightMarginSize, p, greyScale ); + drawQuad( renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); } + + // Batch together runs of identical background cells. + int blockStart = 0; + char blockColour = '\0'; for( int i = 0; i < backgroundColour.length(); i++ ) { - int colour = "0123456789abcdef".indexOf( backgroundColour.charAt( i ) ); - if( colour < 0 || (greyScale && !isGreyScale( colour )) ) + char colourIndex = backgroundColour.charAt( i ); + if( colourIndex == blockColour ) continue; + + if( blockColour != '\0' ) { - colour = 15; + drawQuad( renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (i - blockStart), height, palette, greyscale, blockColour ); } - drawQuad( renderer, x + i * FONT_WIDTH, y, colour, FONT_WIDTH, p, greyScale ); + + blockColour = colourIndex; + blockStart = i; + } + + if( blockColour != '\0' ) + { + drawQuad( renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour ); } - GlStateManager.disableTexture2D(); - tessellator.draw(); - GlStateManager.enableTexture2D(); } - public void drawStringTextPart( int x, int y, TextBuffer s, TextBuffer textColour, boolean greyScale, Palette p ) + public static void drawString( + @Nonnull BufferBuilder renderer, float x, float y, + @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, + @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize + ) { - // Draw the quads - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder renderer = tessellator.getBuffer(); - renderer.begin( GL11.GL_TRIANGLES, DefaultVertexFormats.POSITION_TEX_COLOR ); - for( int i = 0; i < s.length(); i++ ) + if( backgroundColour != null ) { - // Switch colour - int colour = "0123456789abcdef".indexOf( textColour.charAt( i ) ); - if( colour < 0 || (greyScale && !isGreyScale( colour )) ) + drawBackground( renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT ); + } + + for( int i = 0; i < text.length(); i++ ) + { + double[] colour = palette.getColour( getColour( textColour.charAt( i ) ) ); + float r, g, b; + if( greyscale ) { - colour = 0; + r = g = b = toGreyscale( colour ); + } + else + { + r = (float) colour[0]; + g = (float) colour[1]; + b = (float) colour[2]; } // Draw char - int index = s.charAt( i ); - if( index < 0 || index > 255 ) - { - index = '?'; - } - drawChar( renderer, x + i * FONT_WIDTH, y, index, colour, p, greyScale ); + int index = text.charAt( i ); + if( index > 255 ) index = '?'; + drawChar( renderer, x + i * FONT_WIDTH, y, index, r, g, b ); } + + } + + public static void drawString( + float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, + @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize + ) + { + bindFont(); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + begin( buffer ); + drawString( buffer, x, y, text, textColour, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize ); tessellator.draw(); } - public void drawString( TextBuffer s, int x, int y, TextBuffer textColour, TextBuffer backgroundColour, double leftMarginSize, double rightMarginSize, boolean greyScale, Palette p ) + public static void drawTerminalWithoutCursor( + @Nonnull BufferBuilder buffer, float x, float y, + @Nonnull Terminal terminal, boolean greyscale, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) { - // Draw background - if( backgroundColour != null ) + Palette palette = terminal.getPalette(); + int height = terminal.getHeight(); + + // Top and bottom margins + drawBackground( + buffer, x, y - topMarginSize, + terminal.getBackgroundColourLine( 0 ), palette, greyscale, + leftMarginSize, rightMarginSize, topMarginSize + ); + + drawBackground( + buffer, x, y + height * FONT_HEIGHT, + terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, + leftMarginSize, rightMarginSize, bottomMarginSize + ); + + // The main text + for( int i = 0; i < height; i++ ) { - // Bind the background texture - m_textureManager.bindTexture( BACKGROUND ); - - // Draw the quads - drawStringBackgroundPart( x, y, backgroundColour, leftMarginSize, rightMarginSize, greyScale, p ); - } - - // Draw text - if( s != null && textColour != null ) - { - // Bind the font texture - bindFont(); - - // Draw the quads - drawStringTextPart( x, y, s, textColour, greyScale, p ); + drawString( + buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i, + terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ), + palette, greyscale, leftMarginSize, rightMarginSize + ); } } - public int getStringWidth( String s ) + public static void drawCursor( + @Nonnull BufferBuilder buffer, float x, float y, + @Nonnull Terminal terminal, boolean greyscale + ) { - if( s == null ) + Palette palette = terminal.getPalette(); + int width = terminal.getWidth(); + int height = terminal.getHeight(); + + int cursorX = terminal.getCursorX(); + int cursorY = terminal.getCursorY(); + if( terminal.getCursorBlink() && cursorX >= 0 && cursorX < width && cursorY >= 0 && cursorY < height && FrameInfo.getGlobalCursorBlink() ) { - return 0; + double[] colour = palette.getColour( 15 - terminal.getTextColour() ); + float r, g, b; + if( greyscale ) + { + r = g = b = toGreyscale( colour ); + } + else + { + r = (float) colour[0]; + g = (float) colour[1]; + b = (float) colour[2]; + } + + drawChar( buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b ); } - return s.length() * FONT_WIDTH; } - public void bindFont() + public static void drawTerminal( + @Nonnull BufferBuilder buffer, float x, float y, + @Nonnull Terminal terminal, boolean greyscale, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) { - m_textureManager.bindTexture( FONT ); + drawTerminalWithoutCursor( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); + drawCursor( buffer, x, y, terminal, greyscale ); + } + + public static void drawTerminal( + float x, float y, @Nonnull Terminal terminal, boolean greyscale, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) + { + bindFont(); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + begin( buffer ); + drawTerminal( buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); + tessellator.draw(); + } + + public static void drawEmptyTerminal( float x, float y, float width, float height ) + { + bindFont(); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + begin( buffer ); + + Colour colour = Colour.Black; + drawQuad( buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); + + tessellator.draw(); + } + + public static void drawBlocker( @Nonnull BufferBuilder buffer, float x, float y, float width, float height ) + { + Colour colour = Colour.Black; + drawQuad( buffer, x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); + } + + public static void drawBlocker( float x, float y, float width, float height ) + { + bindFont(); + + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + begin( buffer ); + drawBlocker( buffer, x, y, width, height ); + tessellator.draw(); + } + + public static void bindFont() + { + Minecraft.getMinecraft().getTextureManager().bindTexture( FONT ); GlStateManager.glTexParameteri( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP ); + + GlStateManager.enableTexture2D(); + } + + public static void begin( BufferBuilder buffer ) + { + buffer.begin( GL11.GL_TRIANGLES, POSITION_COLOR_TEX ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java index 87d5e2f80..691f632ea 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java @@ -5,25 +5,18 @@ */ package dan200.computercraft.client.gui.widgets; -import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.computer.core.IComputer; import dan200.computercraft.shared.computer.core.IComputerContainer; -import dan200.computercraft.shared.util.Colour; -import dan200.computercraft.shared.util.Palette; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.ChatAllowedCharacters; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import java.util.ArrayList; -import static dan200.computercraft.client.gui.FixedWidthFontRenderer.BACKGROUND; - public class WidgetTerminal extends Widget { private static final float TERMINATE_TIME = 0.5f; @@ -41,10 +34,10 @@ public class WidgetTerminal extends Widget private boolean m_focus = false; private boolean m_allowFocusLoss = true; - private int m_leftMargin; - private int m_rightMargin; - private int m_topMargin; - private int m_bottomMargin; + private final int leftMargin; + private final int rightMargin; + private final int topMargin; + private final int bottomMargin; private final ArrayList m_keysDown = new ArrayList<>(); @@ -58,10 +51,10 @@ public class WidgetTerminal extends Widget m_computer = computer; - m_leftMargin = leftMargin; - m_rightMargin = rightMargin; - m_topMargin = topMargin; - m_bottomMargin = bottomMargin; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + this.topMargin = topMargin; + this.bottomMargin = bottomMargin; } public void setAllowFocusLoss( boolean allowFocusLoss ) @@ -166,8 +159,8 @@ public class WidgetTerminal extends Widget Terminal term = computer.getTerminal(); if( term != null ) { - int charX = (mouseX - (getXPosition() + m_leftMargin)) / FixedWidthFontRenderer.FONT_WIDTH; - int charY = (mouseY - (getYPosition() + m_topMargin)) / FixedWidthFontRenderer.FONT_HEIGHT; + int charX = (mouseX - (getXPosition() + leftMargin)) / FixedWidthFontRenderer.FONT_WIDTH; + int charY = (mouseY - (getYPosition() + topMargin)) / FixedWidthFontRenderer.FONT_HEIGHT; charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); @@ -223,8 +216,8 @@ public class WidgetTerminal extends Widget Terminal term = computer.getTerminal(); if( term != null ) { - int charX = (mouseX - (getXPosition() + m_leftMargin)) / FixedWidthFontRenderer.FONT_WIDTH; - int charY = (mouseY - (getYPosition() + m_topMargin)) / FixedWidthFontRenderer.FONT_HEIGHT; + int charX = (mouseX - (getXPosition() + leftMargin)) / FixedWidthFontRenderer.FONT_WIDTH; + int charY = (mouseY - (getYPosition() + topMargin)) / FixedWidthFontRenderer.FONT_HEIGHT; charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); @@ -339,74 +332,14 @@ public class WidgetTerminal extends Widget Terminal terminal = computer != null ? computer.getTerminal() : null; if( terminal != null ) { - // Draw the terminal - boolean greyscale = !computer.isColour(); - - Palette palette = terminal.getPalette(); - - // Get the data from the terminal first - // Unfortunately we have to keep the lock for the whole of drawing, so the text doesn't change under us. - FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance(); - boolean tblink = m_focus && terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink(); - int tw = terminal.getWidth(); - int th = terminal.getHeight(); - int tx = terminal.getCursorX(); - int ty = terminal.getCursorY(); - - int x = startX + m_leftMargin; - int y = startY + m_topMargin; - - // Draw margins - TextBuffer emptyLine = new TextBuffer( ' ', tw ); - if( m_topMargin > 0 ) - { - fontRenderer.drawString( emptyLine, x, startY, terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ), m_leftMargin, m_rightMargin, greyscale, palette ); - } - if( m_bottomMargin > 0 ) - { - fontRenderer.drawString( emptyLine, x, startY + 2 * m_bottomMargin + (th - 1) * FixedWidthFontRenderer.FONT_HEIGHT, terminal.getTextColourLine( th - 1 ), terminal.getBackgroundColourLine( th - 1 ), m_leftMargin, m_rightMargin, greyscale, palette ); - } - - // Draw lines - for( int line = 0; line < th; line++ ) - { - TextBuffer text = terminal.getLine( line ); - TextBuffer colour = terminal.getTextColourLine( line ); - TextBuffer backgroundColour = terminal.getBackgroundColourLine( line ); - fontRenderer.drawString( text, x, y, colour, backgroundColour, m_leftMargin, m_rightMargin, greyscale, palette ); - y += FixedWidthFontRenderer.FONT_HEIGHT; - } - - if( tblink && tx >= 0 && ty >= 0 && tx < tw && ty < th ) - { - TextBuffer cursor = new TextBuffer( '_', 1 ); - TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 ); - - fontRenderer.drawString( - cursor, - x + FixedWidthFontRenderer.FONT_WIDTH * tx, - startY + m_topMargin + FixedWidthFontRenderer.FONT_HEIGHT * ty, - cursorColour, null, - 0, 0, - greyscale, - palette - ); - } + FixedWidthFontRenderer.drawTerminal( + startX + topMargin, startY + bottomMargin, + terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, rightMargin + ); } else { - // Draw a black background - mc.getTextureManager().bindTexture( BACKGROUND ); - Colour black = Colour.Black; - GlStateManager.color( black.getR(), black.getG(), black.getB(), 1.0f ); - try - { - drawTexturedModalRect( startX, startY, 0, 0, getWidth(), getHeight() ); - } - finally - { - GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f ); - } + FixedWidthFontRenderer.drawEmptyTerminal( startX, startY, getWidth(), getHeight() ); } } } diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java index 300c65b6a..c01ef3f01 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java @@ -6,15 +6,12 @@ package dan200.computercraft.client.render; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.computer.core.ClientComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.util.Colour; -import dan200.computercraft.shared.util.Palette; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; @@ -27,7 +24,8 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.relauncher.Side; import org.lwjgl.opengl.GL11; -import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*; +import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; +import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; import static dan200.computercraft.client.gui.GuiComputer.*; /** @@ -104,21 +102,11 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer if( computer != null && terminal != null ) { - // If we've a computer and terminal then attempt to render it. - renderTerminal( terminal, !computer.isColour(), width, height ); + FixedWidthFontRenderer.drawTerminal( MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); } else { - // Otherwise render a plain background - Minecraft.getMinecraft().getTextureManager().bindTexture( BACKGROUND ); - - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder buffer = tessellator.getBuffer(); - - Colour black = Colour.Black; - buffer.begin( GL11.GL_QUADS, DefaultVertexFormats.POSITION ); - renderTexture( buffer, 0, 0, 0, 0, width, height, black.getR(), black.getG(), black.getB() ); - tessellator.draw(); + FixedWidthFontRenderer.drawEmptyTerminal( 0, 0, width, height ); } GlStateManager.enableDepth(); @@ -189,53 +177,6 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer GlStateManager.enableTexture2D(); } - private static void renderTerminal( Terminal terminal, boolean greyscale, int width, int height ) - { - synchronized( terminal ) - { - int termWidth = terminal.getWidth(); - int termHeight = terminal.getHeight(); - - FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance(); - Palette palette = terminal.getPalette(); - - // Render top/bottom borders - TextBuffer emptyLine = new TextBuffer( ' ', termWidth ); - fontRenderer.drawString( - emptyLine, MARGIN, 0, - terminal.getTextColourLine( 0 ), terminal.getBackgroundColourLine( 0 ), MARGIN, MARGIN, greyscale, palette - ); - fontRenderer.drawString( - emptyLine, MARGIN, 2 * MARGIN + (termHeight - 1) * FixedWidthFontRenderer.FONT_HEIGHT, - terminal.getTextColourLine( termHeight - 1 ), terminal.getBackgroundColourLine( termHeight - 1 ), MARGIN, MARGIN, greyscale, palette - ); - - // Render the actual text - for( int line = 0; line < termWidth; line++ ) - { - TextBuffer text = terminal.getLine( line ); - TextBuffer colour = terminal.getTextColourLine( line ); - TextBuffer backgroundColour = terminal.getBackgroundColourLine( line ); - fontRenderer.drawString( - text, MARGIN, MARGIN + line * FONT_HEIGHT, - colour, backgroundColour, MARGIN, MARGIN, greyscale, palette - ); - } - - // And render the cursor; - int tx = terminal.getCursorX(), ty = terminal.getCursorY(); - if( terminal.getCursorBlink() && FrameInfo.getGlobalCursorBlink() && - tx >= 0 && ty >= 0 && tx < termWidth && ty < termHeight ) - { - TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 ); - fontRenderer.drawString( - new TextBuffer( '_', 1 ), MARGIN + FONT_WIDTH * tx, MARGIN + FONT_HEIGHT * ty, - cursorColour, null, 0, 0, greyscale, palette - ); - } - } - } - private static void renderTexture( BufferBuilder builder, int x, int y, int textureX, int textureY, int width, int height, float r, float g, float b ) { renderTexture( builder, x, y, textureX, textureY, width, height, width, height, r, g, b ); diff --git a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java index be07d5039..f068e9b0a 100644 --- a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java @@ -63,11 +63,12 @@ public final class PrintoutRenderer public static void drawText( int x, int y, int start, TextBuffer[] text, TextBuffer[] colours ) { - FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance(); - for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) { - fontRenderer.drawString( text[start + line], x, y + line * FONT_HEIGHT, colours[start + line], null, 0, 0, false, Palette.DEFAULT ); + FixedWidthFontRenderer.drawString( + x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT, + false, 0, 0 + ); } } @@ -78,11 +79,13 @@ public final class PrintoutRenderer GlStateManager.enableTexture2D(); GlStateManager.tryBlendFuncSeparate( SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ZERO ); - FixedWidthFontRenderer fontRenderer = FixedWidthFontRenderer.instance(); - for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) { - fontRenderer.drawString( new TextBuffer( text[start + line] ), x, y + line * FONT_HEIGHT, new TextBuffer( colours[start + line] ), null, 0, 0, false, Palette.DEFAULT ); + FixedWidthFontRenderer.drawString( + x, y + line * FONT_HEIGHT, + new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ), + null, Palette.DEFAULT, false, 0, 0 + ); } } diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java index 96646ff4c..db7f7b785 100644 --- a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java @@ -8,32 +8,33 @@ package dan200.computercraft.client.render; import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; -import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; +import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import dan200.computercraft.shared.peripheral.monitor.TileMonitor; -import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.DirectionUtil; -import dan200.computercraft.shared.util.Palette; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import org.lwjgl.opengl.GL11; +import javax.annotation.Nonnull; + +import static dan200.computercraft.shared.peripheral.monitor.TileMonitor.RENDER_MARGIN; + public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer { + private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1); + @Override - public void render( TileMonitor tileEntity, double posX, double posY, double posZ, float f, int i, float f2 ) + public void render( @Nonnull TileMonitor tileEntity, double posX, double posY, double posZ, float f, int i, float f2 ) { - if( tileEntity != null ) - { - renderMonitorAt( tileEntity, posX, posY, posZ, f, i ); - } + renderMonitorAt( tileEntity, posX, posY, posZ, f, i ); } private static void renderMonitorAt( TileMonitor monitor, double posX, double posY, double posZ, float f, int i ) @@ -69,223 +70,141 @@ public class TileEntityMonitorRenderer extends TileEntitySpecialRenderer= 0 && cursorX < width && cursorY >= 0 && cursorY < height ) - { - TextBuffer cursor = new TextBuffer( "_" ); - TextBuffer cursorColour = new TextBuffer( "0123456789abcdef".charAt( terminal.getTextColour() ), 1 ); - fontRenderer.drawString( - cursor, - FixedWidthFontRenderer.FONT_WIDTH * cursorX, - FixedWidthFontRenderer.FONT_HEIGHT * cursorY, - cursorColour, null, - 0, 0, - greyscale, - palette - ); - } - } - finally - { - GlStateManager.glEndList(); - } - } - if( FrameInfo.getGlobalCursorBlink() ) - { - GlStateManager.callList( originTerminal.renderDisplayLists[2] ); - GlStateManager.resetColor(); - } - } - finally - { - GlStateManager.popMatrix(); - } - } - else - { - // Draw a big black quad - mc.getTextureManager().bindTexture( FixedWidthFontRenderer.BACKGROUND ); - final Colour colour = Colour.Black; - - final float r = colour.getR(); - final float g = colour.getG(); - final float b = colour.getB(); - - renderer.begin( GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION_TEX_COLOR ); - renderer.pos( -TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0D ).tex( 0.0, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( -TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).tex( 0.0, 1.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( xSize + TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0D ).tex( 1.0, 0.0 ).color( r, g, b, 1.0f ).endVertex(); - renderer.pos( xSize + TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).tex( 1.0, 1.0 ).color( r, g, b, 1.0f ).endVertex(); - tessellator.draw(); - } - } - finally - { - GlStateManager.depthMask( true ); - mc.entityRenderer.enableLightmap(); - GlStateManager.enableLighting(); - } - - // Draw the depth blocker - GlStateManager.colorMask( false, false, false, false ); - try - { - mc.getTextureManager().bindTexture( FixedWidthFontRenderer.BACKGROUND ); - renderer.begin( GL11.GL_TRIANGLE_STRIP, DefaultVertexFormats.POSITION ); - renderer.pos( -TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0 ).endVertex(); - renderer.pos( -TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).endVertex(); - renderer.pos( xSize + TileMonitor.RENDER_MARGIN, TileMonitor.RENDER_MARGIN, 0.0 ).endVertex(); - renderer.pos( xSize + TileMonitor.RENDER_MARGIN, -ySize - TileMonitor.RENDER_MARGIN, 0.0 ).endVertex(); - tessellator.draw(); - } - finally - { - GlStateManager.colorMask( true, true, true, true ); - } - } - finally - { - GlStateManager.color( 1.0f, 1.0f, 1.0f, 1.0f ); GlStateManager.popMatrix(); } + else + { + FixedWidthFontRenderer.drawEmptyTerminal( + -MARGIN, MARGIN, + (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) + ); + } + + // Tear down render state for monitors. + GlStateManager.depthMask( true ); + mc.entityRenderer.enableLightmap(); + GlStateManager.enableLighting(); + + // Draw the depth blocker + GlStateManager.colorMask( false, false, false, false ); + FixedWidthFontRenderer.drawBlocker( + (float) -TileMonitor.RENDER_MARGIN, (float) TileMonitor.RENDER_MARGIN, + (float) (xSize + 2 * TileMonitor.RENDER_MARGIN), (float) -(ySize + TileMonitor.RENDER_MARGIN * 2) + ); + GlStateManager.colorMask( true, true, true, true ); + + GlStateManager.popMatrix(); + } + + private static void renderTerminal( ClientMonitor monitor, float xMargin, float yMargin ) + { + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + + boolean redraw = monitor.pollTerminalChanged(); + + // Setup the buffers if needed. We get the renderer here, to avoid the (unlikely) race condition between + // creating the buffers and rendering. + MonitorRenderer renderer = MonitorRenderer.current(); + if( monitor.createBuffer( renderer ) ) redraw = true; + + FixedWidthFontRenderer.bindFont(); + + switch( renderer ) + { + case VBO: + { + VertexBuffer vbo = monitor.buffer; + if( redraw ) + { + renderTerminalTo( monitor, buffer, xMargin, yMargin ); + buffer.finishDrawing(); + buffer.reset(); + vbo.bufferData( buffer.getByteBuffer() ); + } + + vbo.bindBuffer(); + setupBufferFormat(); + vbo.drawArrays( GL11.GL_TRIANGLES ); + vbo.unbindBuffer(); + + break; + } + + case DISPLAY_LIST: + if( redraw ) + { + GlStateManager.glNewList( monitor.displayList, GL11.GL_COMPILE ); + renderTerminalTo( monitor, buffer, xMargin, yMargin ); + tessellator.draw(); + GlStateManager.glEndList(); + } + + GlStateManager.callList( monitor.displayList ); + break; + } + + // We don't draw the cursor with a buffer, as it's dynamic and so we'll end up refreshing far more than is + // reasonable. + FixedWidthFontRenderer.begin( buffer ); + FixedWidthFontRenderer.drawCursor( buffer, 0, 0, monitor.getTerminal(), !monitor.isColour() ); + tessellator.draw(); + } + + private static void renderTerminalTo( ClientMonitor monitor, BufferBuilder buffer, float xMargin, float yMargin ) + { + FixedWidthFontRenderer.begin( buffer ); + FixedWidthFontRenderer.drawTerminalWithoutCursor( + buffer, 0, 0, + monitor.getTerminal(), !monitor.isColour(), yMargin, yMargin, xMargin, xMargin + ); + } + + public static void setupBufferFormat() + { + int stride = FixedWidthFontRenderer.POSITION_COLOR_TEX.getSize(); + GlStateManager.glVertexPointer( 3, GL11.GL_FLOAT, stride, 0 ); + GlStateManager.glEnableClientState( GL11.GL_VERTEX_ARRAY ); + + GlStateManager.glColorPointer( 4, GL11.GL_UNSIGNED_BYTE, stride, 12 ); + GlStateManager.glEnableClientState( GL11.GL_COLOR_ARRAY ); + + GlStateManager.glTexCoordPointer( 2, GL11.GL_FLOAT, stride, 16 ); + GlStateManager.glEnableClientState( GL11.GL_TEXTURE_COORD_ARRAY ); } } diff --git a/src/main/java/dan200/computercraft/shared/Config.java b/src/main/java/dan200/computercraft/shared/Config.java index 65819e69e..92f2b75c3 100644 --- a/src/main/java/dan200/computercraft/shared/Config.java +++ b/src/main/java/dan200/computercraft/shared/Config.java @@ -11,6 +11,7 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.turtle.event.TurtleAction; import dan200.computercraft.core.apis.AddressPredicate; import dan200.computercraft.core.apis.http.websocket.Websocket; +import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.ConfigElement; import net.minecraftforge.common.config.Configuration; @@ -69,6 +70,7 @@ public final class Config private static Property modemRangeDuringStorm; private static Property modemHighAltitudeRangeDuringStorm; private static Property maxNotesPerTick; + private static Property monitorRenderer; private static Property turtlesNeedFuel; private static Property turtleFuelLimit; @@ -264,9 +266,15 @@ public final class Config maxNotesPerTick.setComment( "Maximum amount of notes a speaker can play at once" ); maxNotesPerTick.setMinValue( 1 ); + monitorRenderer = config.get( CATEGORY_PERIPHERAL, "monitor_renderer", ComputerCraft.monitorRenderer.displayName() ); + monitorRenderer.setComment( "The renderer to use for monitors. Generally this should be kept at \"best\" - if " + + "monitors have performance issues, you may wish to experiment with alternative renderers." ); + monitorRenderer.setValidValues( MonitorRenderer.NAMES ); + setOrder( CATEGORY_PERIPHERAL, - commandBlockEnabled, modemRange, modemHighAltitudeRange, modemRangeDuringStorm, modemHighAltitudeRangeDuringStorm, maxNotesPerTick + commandBlockEnabled, modemRange, modemHighAltitudeRange, modemRangeDuringStorm, modemHighAltitudeRangeDuringStorm, maxNotesPerTick, + monitorRenderer ); } @@ -459,6 +467,7 @@ public final class Config ComputerCraft.modem_highAltitudeRange = Math.min( modemHighAltitudeRange.getInt(), MODEM_MAX_RANGE ); ComputerCraft.modem_rangeDuringStorm = Math.min( modemRangeDuringStorm.getInt(), MODEM_MAX_RANGE ); ComputerCraft.modem_highAltitudeRangeDuringStorm = Math.min( modemHighAltitudeRangeDuringStorm.getInt(), MODEM_MAX_RANGE ); + ComputerCraft.monitorRenderer = MonitorRenderer.ofString( monitorRenderer.getString() ); // Turtles ComputerCraft.turtlesNeedFuel = turtlesNeedFuel.getBoolean(); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java index c3ccc3dae..2bcd45181 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java @@ -5,8 +5,10 @@ */ package dan200.computercraft.shared.peripheral.monitor; +import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.shared.common.ClientTerminal; -import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.GLAllocation; +import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.util.math.BlockPos; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -15,7 +17,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; -public class ClientMonitor extends ClientTerminal +public final class ClientMonitor extends ClientTerminal { private static final Set allMonitors = new HashSet<>(); @@ -23,7 +25,9 @@ public class ClientMonitor extends ClientTerminal public long lastRenderFrame = -1; public BlockPos lastRenderPos = null; - public int[] renderDisplayLists = null; + + public VertexBuffer buffer; + public int displayList = 0; public ClientMonitor( boolean colour, TileMonitor origin ) { @@ -36,41 +40,72 @@ public class ClientMonitor extends ClientTerminal return origin; } + /** + * Create the appropriate buffer if needed. + * + * @param renderer The renderer to use. This can be fetched from {@link #renderer()}. + * @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer, + * or this mode does not require one. + */ @SideOnly( Side.CLIENT ) - public void createLists() + public boolean createBuffer( MonitorRenderer renderer ) { - if( renderDisplayLists == null ) + switch( renderer ) { - renderDisplayLists = new int[3]; + case VBO: + if( buffer != null ) return false; - for( int i = 0; i < renderDisplayLists.length; i++ ) - { - renderDisplayLists[i] = GlStateManager.glGenLists( 1 ); - } + deleteBuffers(); + buffer = new VertexBuffer( FixedWidthFontRenderer.POSITION_COLOR_TEX ); + addMonitor(); + return true; + case DISPLAY_LIST: + if( displayList != 0 ) return false; - synchronized( allMonitors ) - { - allMonitors.add( this ); - } + deleteBuffers(); + displayList = GLAllocation.generateDisplayLists( 1 ); + addMonitor(); + return true; + + default: + return false; + } + } + + private void addMonitor() + { + synchronized( allMonitors ) + { + allMonitors.add( this ); + } + } + + private void deleteBuffers() + { + if( buffer != null ) + { + buffer.deleteGlBuffers(); + buffer = null; + } + + if( displayList != 0 ) + { + GLAllocation.deleteDisplayLists( displayList ); + displayList = 0; } } @SideOnly( Side.CLIENT ) public void destroy() { - if( renderDisplayLists != null ) + if( buffer != null || displayList != 0 ) { synchronized( allMonitors ) { allMonitors.remove( this ); } - for( int list : renderDisplayLists ) - { - GlStateManager.glDeleteLists( list, 1 ); - } - - renderDisplayLists = null; + deleteBuffers(); } } @@ -82,14 +117,7 @@ public class ClientMonitor extends ClientTerminal for( Iterator iterator = allMonitors.iterator(); iterator.hasNext(); ) { ClientMonitor monitor = iterator.next(); - if( monitor.renderDisplayLists != null ) - { - for( int list : monitor.renderDisplayLists ) - { - GlStateManager.glDeleteLists( list, 1 ); - } - monitor.renderDisplayLists = null; - } + monitor.deleteBuffers(); iterator.remove(); } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java new file mode 100644 index 000000000..c60ea91ed --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java @@ -0,0 +1,105 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ + +package dan200.computercraft.shared.peripheral.monitor; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.render.TileEntityMonitorRenderer; +import net.minecraft.client.renderer.OpenGlHelper; + +import javax.annotation.Nonnull; +import java.util.Locale; + +/** + * The render type to use for monitors. + * + * @see TileEntityMonitorRenderer + * @see ClientMonitor + */ +public enum MonitorRenderer +{ + /** + * Determine the best monitor backend. + */ + BEST, + + /** + * Render using VBOs. This is the default when supported. + * + * @see net.minecraft.client.renderer.vertex.VertexBuffer + */ + VBO, + + /** + * Render using display lists. + * + * @see net.minecraft.client.renderer.GLAllocation#generateDisplayLists(int) + */ + DISPLAY_LIST; + + private static final MonitorRenderer[] VALUES = values(); + public static final String[] NAMES; + + private final String displayName = "gui.computercraft:config.peripheral.monitor_renderer." + name().toLowerCase( Locale.ROOT ); + + static + { + NAMES = new String[VALUES.length]; + for( int i = 0; i < VALUES.length; i++ ) NAMES[i] = VALUES[i].displayName(); + } + + public String displayName() + { + return displayName; + } + + @Nonnull + public static MonitorRenderer ofString( String name ) + { + for( MonitorRenderer backend : VALUES ) + { + if( backend.displayName.equalsIgnoreCase( name ) || backend.name().equalsIgnoreCase( name ) ) + { + return backend; + } + } + + ComputerCraft.log.warn( "Unknown monitor renderer {}. Falling back to default.", name ); + return BEST; + } + + /** + * Get the current renderer to use. + * + * @return The current renderer. Will not return {@link MonitorRenderer#BEST}. + */ + @Nonnull + public static MonitorRenderer current() + { + MonitorRenderer current = ComputerCraft.monitorRenderer; + switch( current ) + { + case BEST: + return best(); + case VBO: + if( !OpenGlHelper.vboSupported ) + { + ComputerCraft.log.warn( "VBOs are not supported on your graphics card. Falling back to default." ); + ComputerCraft.monitorRenderer = BEST; + return best(); + } + + return VBO; + default: + return current; + } + } + + private static MonitorRenderer best() + { + return OpenGlHelper.vboSupported ? VBO : DISPLAY_LIST; + } +} diff --git a/src/main/java/dan200/computercraft/shared/util/Palette.java b/src/main/java/dan200/computercraft/shared/util/Palette.java index 18c8040c2..e177940c2 100644 --- a/src/main/java/dan200/computercraft/shared/util/Palette.java +++ b/src/main/java/dan200/computercraft/shared/util/Palette.java @@ -48,13 +48,13 @@ public class Palette { if( i >= 0 && i < colours.length ) { - setColour( i, Colour.values()[i] ); + setColour( i, Colour.VALUES[i] ); } } public void resetColours() { - for( int i = 0; i < Colour.values().length; i++ ) + for( int i = 0; i < Colour.VALUES.length; i++ ) { resetColour( i ); } diff --git a/src/main/resources/assets/computercraft/lang/en_us.lang b/src/main/resources/assets/computercraft/lang/en_us.lang index 99cadd868..254fb0e21 100644 --- a/src/main/resources/assets/computercraft/lang/en_us.lang +++ b/src/main/resources/assets/computercraft/lang/en_us.lang @@ -185,6 +185,10 @@ gui.computercraft:config.peripheral.modem_high_altitude_range=Modem range (high- gui.computercraft:config.peripheral.modem_range_during_storm=Modem range (bad weather) gui.computercraft:config.peripheral.modem_high_altitude_range_during_storm=Modem range (high-altitude, bad weather) gui.computercraft:config.peripheral.max_notes_per_tick=Maximum notes that a computer can play at once +gui.computercraft:config.peripheral.monitor_renderer=Monitor renderer +gui.computercraft:config.peripheral.monitor_renderer.best=Best +gui.computercraft:config.peripheral.monitor_renderer.vbo=Vertex Buffers +gui.computercraft:config.peripheral.monitor_renderer.display_list=Display Lists gui.computercraft:config.turtle=Turtles gui.computercraft:config.turtle.need_fuel=Enable fuel diff --git a/src/main/resources/assets/computercraft/textures/gui/term_background.png b/src/main/resources/assets/computercraft/textures/gui/term_background.png deleted file mode 100644 index 694d2b25b96a186fffedf045db42cc4fd488de4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 123 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|oCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#J`WQ=AD@v@S_)7|%G1R$MB{w&pa1{unKc_a8(j=mC59ZnaFKx_G?!U+zMkkJ PpehDWS3j3^P6SzjiQolLup14BEunJ(pZXYQ<_oA zT9(N^)=3#A$~I#+w!}BS>-_!w|6S)i*Y&>d^E`jN%YEJV^IUJdy)8;qSXvkW08wiz z3kLuI@mvrf$j4j!OI@9Iq96y989?ronc@ZfzNTkQ0ic`!-SmL~03Tp)<8+Fb-`?Kl z&HviJ2>b&P@D1glcT^MXVC%>e!avyn$JJd*JPHWiv~mjqfZd;V21xSgX*>W3*H~Ma zI$^zLhzaRGi$yI8x3}{pjEWl@=%Kw~NgOQ$J|!WWWX}DX2qoOs>aAk%oybU?2ydD6 zsb1B@ITS?&Licn>8((P8_+%kpKmP6z%jYe=*EyCEnN|$^^*UfcoAA3ySLHi8V*muD zK=kFl?tZR`19$d`NKz`;$>)+zHgLK5Y~wSY_|S&(It7e7n-pbcmcdQFw%J_@TOE)+v^o!)Q)nUl3BRy*SH#mf@%>IpVIy9&WE}p zKq|;<4P{?4g2%t%Y}9P@2-B3a-*tPhS7y@MZk2-*CB(GOfed zrzs=*A|y8o5D&%7F(;Q)>m=C_t@d#$rs71BFVZ^NV)Wny)+V8QAht&_VjX)p=A%c> zS2@xCH*>~OULL_4*dMSvOVWnl$KyRO;{;7Fw5bD5DzTYvKj#Uxt^%SaP?&8r>B0gj zC>PMjKjYT%O|z0Wf;$HU0aD=9~WBdsI4UEGf)hw##`Wid2P9dyGUXCv;g8Y*r~{XY7=r#IG{4Le$*2^QsPAr z-1RUe7KWb-xuLvrwb(Q9N`fJF|E6D#&t8iwdcPYcFT}i09edl?S1pB*E1P8=`+6%* zLG#&fpJMHtX?DS~7U9WonXMV~XiVao?9l+wjpWwKo;9&Ti=o4#&UsDZpRVS)ZFbDz zJq}}=xAxwvP4@)Wi$?dUj+zX2x;Wo&1?hIWOzYi`b=5Hu!-@LQ9z`@ntBFwKlR%#DDZK8%(zRa3^B8W7+=CVC{+`U=6NLb!$BrV16I!#Yp z3#$1rqe<1^fChf{>dRQ3dc%f+ZHS<|_Eh&+iS8K)$|a%fcd1!5W<6 zVl7{u{d>-yLzDi4kmP|aaa3KK`oLyr0@LmL040ES8q_&)Kua?_a2H3JWzd+L$hf^W zy@8DHb$PVzoM+rAxba4WkNlqMi=VZ6ciBB_Car9?%-V$}NlCuBpNky%t4Mfn7?pW` zO=`lbvHOcE#wT=tO*qJs$R_fYsJb$VAy{y;TINRy)+Yqb zGq(jcqhF%BjKr&2BS2eXp`e^sKTm_waXmA+E%~O-XRQ!83Q(<1K54G68dJQ{GI=2y zo4=MTUV9*|rUVD zGf?z*iVB8isO4E&2#@=A3ZVz{e*D*}&TLBEK6!#inm10!jHv8-9sfDFI{xE%>8^`# zK>wS(!i3PWuIXg3VS*W&H6D3_NFqUZ_Llo#3B|(PLhraIb1g#Ck<0?s2z7JwNke zDp};p9*v_yTt&52tEVE*fLTIS^s8UwUCU^Ohl3O zyXQ?N$XBi#Dz;q5Z21CZ>8jSgX}WF36ONb8}&`8uIHQl%PoJZ zzU`mUXjZsSwwQ0N)<@WXc;6SBGZ%L?WNPpzCRkh6cs({;f!=)5fP@BN($3P#WF3!* z`B_d-l!AWiT&YVwlIop-YB}jn)Pp`gqk=!O#!MPVhkZt8${B*_xI{K&4K)avPi+ZI zMVW^95g&M)#jP~|h_KBO`xIr9WQU(^{f5Q8P#=!KEU*s7OQ;}+%KhEp^-k-S;Y_EX zh3&R>g0$`pCZqfhcv=WH`LY1ii)Sy*&}^a)N}@x$JoFNnBo zsbGQwUmDR&{^(QPdv&zWDJdX|J){sdvPcAPO7OICApr)|U6z^Cd!MG)KGe{N6{X7T zAVXqjYI{sHe34-N%Hp8%hc^}$Hodc0Eg_^+!gz*rxWA_p_(Ii(6m)eqsl7FJZ`P*%HdbO&3GQI6DDFF3BB1<@1w7ThQ(%l?{HyQVInf*N%Hm*F{wrjCrFtbv#| zhf9m^=kUe5E$5UM-h+qIb~HE$!SA%I|K*^&>q>t=@B~ViT$5>tD!_H3T%K?_6!f|i zZF;bc1NP3Q-7`!dgw*R3AmPU;P<+Ti=%OnI4zLYjKg!$Hfr0SjJd*w&Z_oyv=W=nQ XA!+xD{uor-`QTWevb7+adEfgF>y~f^ delta 1210 zcmV;r1V#J69^DC$7zqIb0001HygDV3As>GL32;bRa{vGi!vFvd!vV){sAK>D02y>e zSaefwW^{L9a%BK;VQFr3E^cLXAT%y8E-^MlX|MGF000J1OjJbx00960|E1ZV-2eap z0d!JMQvg8b*k%9#1TINLK~!ko?U*r(;53ft$gNw*CaPLibaxg1dWu2W;vU2DweZ)(~qIZj9vm6R;(YpK7xp z%y0+vRt3K34ghg3vCA3cQUfJeHynRqMiQXUfZMm;4F|Y$-J|1tz1-Pt1d{H!Gpkf! zx7xtz>%MfxN)L`zn~g>mc&%0IR%0d!_`)=S#cMyx=~bfGv^@YIRDrxr4I2mwYJ;{12$rX3d?u%jX03m(-3$nb z^T?@}RwYUz8&ES~#}K-)1c{_K^TistUMq}^WgsHV*W$z&JctSa(afz7r65ra+0;U9 z?iS2wJsA-B8l6VA(MIUWYicd2h=PTU+1SB0 zmh*w36RCe5mmafzoZSF` zmi;f?QvliQwzG?e%^};gFUSUO=C|VAY1$WRA_KaLk;Nzt6v+njA;w$+m4=jKeTWju z*kon}Z`oIdDB;m;pfbc)dh;DN-e3KV`>Tile&+xH+~42dUtL{YJv=yw&$#=^Vhf+&zER5OYEg)XxicN<1Pr0CMfRMROkT-d4cJ8lX{x@L`+Wt3Ziz zZt%hO0l!r%M3F7tBL%#x_TWa2$XL~z1feshzDGGmWFP|4X@2l+8Dqf!91YuT%C-L};@{R3fV{Ajc!LTH{u$>+ZVL4-2gTSh{~s*9)Hgxk3Z%wW}SA z4xl{uP+eZs>SJpuG25}V)UR2w650cM0Rm9DZf0h-FYfMUmG^E*lEzX>DXe_~aCdj# zYaGY13QQIDwpd{Tes30g_}`M9P2=j=T1v2E%hcL9pG7j$-XtKQ_O>WVW=cB=()ut1 zPA5qc2T4k+N`-$xl9W!8#B~MO=~1loFv`r#re&uco|| Date: Tue, 21 Apr 2020 11:37:56 +0100 Subject: [PATCH 32/35] Some redesigning of the settings API (#408) - The store is now split into two sections: - A list of possible options, with some metadata about them. - A list of values which have been changed. - settings.define can be used to register a new option. We have migrated all existing options over to use it. This can be used to define a default value, description, and a type the setting must have (such as `string` or `boolean). - settings.{set,unset,clear,load,store} operate using this value list. This means that only values which have been changed are stored to disk. Furthermore, clearing/unsetting will reset to the /default/ value, rather than removing entirely. - The set program will now display descriptions. - settings.{load,save} now default to `.settings` if no path is given. --- .../assets/computercraft/lua/bios.lua | 70 ++++++-- .../computercraft/lua/rom/apis/settings.lua | 165 +++++++++++++----- .../computercraft/lua/rom/programs/set.lua | 26 ++- .../test-rom/spec/apis/settings_spec.lua | 135 +++++++++++++- .../test-rom/spec/programs/set_spec.lua | 56 ++++-- 5 files changed, 378 insertions(+), 74 deletions(-) diff --git a/src/main/resources/assets/computercraft/lua/bios.lua b/src/main/resources/assets/computercraft/lua/bios.lua index 39dd252c3..f1e80cef4 100644 --- a/src/main/resources/assets/computercraft/lua/bios.lua +++ b/src/main/resources/assets/computercraft/lua/bios.lua @@ -881,18 +881,66 @@ if bAPIError then end -- Set default settings -settings.set("shell.allow_startup", true) -settings.set("shell.allow_disk_startup", commands == nil) -settings.set("shell.autocomplete", true) -settings.set("edit.autocomplete", true) -settings.set("edit.default_extension", "lua") -settings.set("paint.default_extension", "nfp") -settings.set("lua.autocomplete", true) -settings.set("list.show_hidden", false) -settings.set("motd.enable", false) -settings.set("motd.path", "/rom/motd.txt:/motd.txt") +settings.define("shell.allow_startup", { + default = true, + description = "Run startup files when the computer turns on.", + type = "boolean", +}) +settings.define("shell.allow_disk_startup", { + default = commands == nil, + description = "Run startup files from disk drives when the computer turns on.", + type = "boolean", +}) + +settings.define("shell.autocomplete", { + default = true, + description = "Autocomplete program and arguments in the shell.", + type = "boolean", +}) +settings.define("edit.autocomplete", { + default = true, + description = "Autocomplete API and function names in the editor.", + type = "boolean", +}) +settings.define("lua.autocomplete", { + default = true, + description = "Autocomplete API and function names in the Lua REPL.", + type = "boolean", +}) + +settings.define("edit.default_extension", { + default = "lua", + description = [[The file extension the editor will use if none is given. Set to "" to disable.]], + type = "string", +}) +settings.define("paint.default_extension", { + default = "nfp", + description = [[The file extension the paint program will use if none is given. Set to "" to disable.]], + type = "string", +}) + +settings.define("list.show_hidden", { + default = false, + description = [[Show hidden files (those starting with "." in the Lua REPL)]], + type = "boolean", +}) + +settings.define("motd.enable", { + default = false, + description = "Display a random message when the computer starts up.", + type = "boolean", +}) +settings.define("motd.path", { + default = "/rom/motd.txt:/motd.txt", + description = [[The path to load random messages from. Should be a colon (":") separated string of file paths.]], + type = "string", +}) if term.isColour() then - settings.set("bios.use_multishell", true) + settings.define("bios.use_multishell", { + default = true, + description = [[Allow running multiple programs at once, through the use of the "fg" and "bg" programs.]], + type = "boolean", + }) end if _CC_DEFAULT_SETTINGS then for sPair in string.gmatch(_CC_DEFAULT_SETTINGS, "[^,]+") do diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua index 40044c1a0..fcfb1d17a 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua @@ -6,9 +6,86 @@ -- -- @module settings -local expect = dofile("rom/modules/main/cc/expect.lua").expect +local expect = dofile("rom/modules/main/cc/expect.lua") +local type, expect, field = type, expect.expect, expect.field -local tSettings = {} +local details, values = {}, {} + +local function reserialize(value) + if type(value) ~= "table" then return value end + return textutils.unserialize(textutils.serialize(value)) +end + +local function copy(value) + if type(value) ~= "table" then return value end + local result = {} + for k, v in pairs(value) do result[k] = copy(v) end + return result +end + +local valid_types = { "number", "string", "boolean", "table" } +for _, v in ipairs(valid_types) do valid_types[v] = true end + +--- Define a new setting, optional specifying various properties about it. +-- +-- While settings do not have to be added before being used, doing so allows +-- you to provide defaults and additional metadata. +-- +-- @tparam string name The name of this option +-- @tparam[opt] { description? = string, default? = value, type? = string } options +-- Options for this setting. This table accepts the following fields: +-- +-- - `description`: A description which may be printed when running the `set` program. +-- - `default`: A default value, which is returned by @{settings.get} if the +-- setting has not been changed. +-- - `type`: Require values to be of this type. @{set|Setting} the value to another type +-- will error. +function define(name, options) + expect(1, name, "string") + expect(2, options, "table", nil) + + if options then + options = { + description = field(options, "description", "string", "nil"), + default = reserialize(field(options, "default", "number", "string", "boolean", "table", "nil")), + type = field(options, "type", "string", "nil"), + } + + if options.type and not valid_types[options.type] then + error(("Unknown type %q. Expected one of %s."):format(options.type, table.concat(valid_types, ", ")), 2) + end + else + options = {} + end + + details[name] = options +end + +--- Remove a @{define|definition} of a setting. +-- +-- If a setting has been changed, this does not remove its value. Use @{settings.unset} +-- for that. +-- +-- @tparam string name The name of this option +function undefine(name) + expect(1, name, "string") + details[name] = nil +end + +local function set_value(name, value) + local new = reserialize(value) + local old = values[name] + if old == nil then + local opt = details[name] + old = opt and opt.default + end + + values[name] = new + if old ~= new then + -- This should be safe, as os.queueEvent copies values anyway. + os.queueEvent("setting_changed", name, new, old) + end +end --- Set the value of a setting. -- @@ -21,43 +98,43 @@ function set(name, value) expect(1, name, "string") expect(2, value, "number", "string", "boolean", "table") - if type(value) == "table" then - -- Ensure value is serializeable - value = textutils.unserialize(textutils.serialize(value)) - end - tSettings[name] = value -end + local opt = details[name] + if opt and opt.type then expect(2, value, opt.type) end -local copy -function copy(value) - if type(value) == "table" then - local result = {} - for k, v in pairs(value) do - result[k] = copy(v) - end - return result - else - return value - end + set_value(name, value) end --- Get the value of a setting. -- -- @tparam string name The name of the setting to get. -- @param[opt] default The value to use should there be pre-existing value for --- this setting. Defaults to `nil`. --- @return The setting's, or `default` if the setting has not been set. +-- this setting. If not given, it will use the setting's default value if given, +-- or `nil` otherwise. +-- @return The setting's, or the default if the setting has not been changed. function get(name, default) expect(1, name, "string") - local result = tSettings[name] + local result = values[name] if result ~= nil then return copy(result) - else + elseif default ~= nil then return default + else + local opt = details[name] + return opt and copy(opt.default) end end ---- Remove the value of a setting, clearing it back to `nil`. +--- Get details about a specific setting +function getDetails(name) + expect(1, name, "string") + local deets = copy(details[name]) or {} + deets.value = values[name] + deets.changed = deets.value ~= nil + if deets.value == nil then deets.value = deets.default end + return deets +end + +--- Remove the value of a setting, setting it to the default. -- -- @{settings.get} will return the default value until the setting's value is -- @{settings.set|set}, or the computer is rebooted. @@ -67,15 +144,17 @@ end -- @see settings.clear function unset(name) expect(1, name, "string") - tSettings[name] = nil + set_value(name, nil) end ---- Removes the value of all settings. Equivalent to calling @{settings.unset} +--- Resets the value of all settings. Equivalent to calling @{settings.unset} --- on every setting. -- -- @see settings.unset function clear() - tSettings = {} + for name in pairs(values) do + set_value(name, nil) + end end --- Get the names of all currently defined settings. @@ -83,9 +162,12 @@ end -- @treturn { string } An alphabetically sorted list of all currently-defined -- settings. function getNames() - local result = {} - for k in pairs(tSettings) do - result[#result + 1] = k + local result, n = {}, 1 + for k in pairs(details) do + result[n], n = k, n + 1 + end + for k in pairs(values) do + if not details[k] then result[n], n = k, n + 1 end end table.sort(result) return result @@ -96,15 +178,15 @@ end -- Existing settings will be merged with any pre-existing ones. Conflicting -- entries will be overwritten, but any others will be preserved. -- --- @tparam string sPath The file to load from. +-- @tparam[opt] string sPath The file to load from, defaulting to `.settings`. -- @treturn boolean Whether settings were successfully read from this -- file. Reasons for failure may include the file not existing or being -- corrupted. -- -- @see settings.save function load(sPath) - expect(1, sPath, "string") - local file = fs.open(sPath, "r") + expect(1, sPath, "string", "nil") + local file = fs.open(sPath or ".settings", "r") if not file then return false end @@ -118,9 +200,12 @@ function load(sPath) end for k, v in pairs(tFile) do - if type(k) == "string" and - (type(v) == "string" or type(v) == "number" or type(v) == "boolean" or type(v) == "table") then - set(k, v) + local ty_v = type(k) + if type(k) == "string" and (ty_v == "string" or ty_v == "number" or ty_v == "boolean" or ty_v == "table") then + local opt = details[name] + if not opt or not opt.type or ty_v == opt.type then + set_value(k, v) + end end end @@ -132,18 +217,18 @@ end -- This will entirely overwrite the pre-existing file. Settings defined in the -- file, but not currently loaded will be removed. -- --- @tparam string sPath The path to save settings to. +-- @tparam[opt] string sPath The path to save settings to, defaulting to `.settings`. -- @treturn boolean If the settings were successfully saved. -- -- @see settings.load function save(sPath) - expect(1, sPath, "string") - local file = fs.open(sPath, "w") + expect(1, sPath, "string", "nil") + local file = fs.open(".settings", "w") if not file then return false end - file.write(textutils.serialize(tSettings)) + file.write(textutils.serialize(values)) file.close() return true diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/set.lua b/src/main/resources/assets/computercraft/lua/rom/programs/set.lua index a826d8ad6..01d4b246e 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/set.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/set.lua @@ -1,3 +1,4 @@ +local pp = require "cc.pretty" local tArgs = { ... } if #tArgs == 0 then @@ -12,7 +13,13 @@ if #tArgs == 0 then elseif #tArgs == 1 then -- "set foo" local sName = tArgs[1] - print(textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName))) + local deets = settings.getDetails(sName) + local msg = pp.text(sName, colors.cyan) .. " is " .. pp.pretty(deets.value) + if deets.default ~= nil and deets.value ~= deets.default then + msg = msg .. " (default is " .. pp.pretty(deets.default) .. ")" + end + pp.print(msg) + if deets.description then print(deets.description) end else -- "set foo bar" @@ -31,15 +38,18 @@ else value = sValue end - local oldValue = settings.get(sValue) - if value ~= nil then - settings.set(sName, value) - print(textutils.serialize(sName) .. " set to " .. textutils.serialize(value)) - else + local option = settings.getDetails(sName) + if value == nil then settings.unset(sName) print(textutils.serialize(sName) .. " unset") + elseif option.type and option.type ~= type(value) then + printError(("%s is not a valid %s."):format(textutils.serialize(sValue), option.type)) + else + settings.set(sName, value) + print(textutils.serialize(sName) .. " set to " .. textutils.serialize(value)) end - if value ~= oldValue then - settings.save(".settings") + + if value ~= option.value then + settings.save() end end diff --git a/src/test/resources/test-rom/spec/apis/settings_spec.lua b/src/test/resources/test-rom/spec/apis/settings_spec.lua index b54e24e67..40af033d1 100644 --- a/src/test/resources/test-rom/spec/apis/settings_spec.lua +++ b/src/test/resources/test-rom/spec/apis/settings_spec.lua @@ -1,4 +1,18 @@ describe("The settings library", function() + describe("settings.define", function() + it("ensures valid type names", function() + expect.error(settings.define, "test.defined", { type = "function" }) + :eq('Unknown type "function". Expected one of number, string, boolean, table.') + end) + end) + describe("settings.undefine", function() + it("clears defined settings", function() + settings.define("test.unset", { default = 123 }) + settings.undefine("test.unset") + expect(settings.get("test.unset")):eq(nil) + end) + end) + describe("settings.set", function() it("validates arguments", function() settings.set("test", 1) @@ -13,6 +27,30 @@ describe("The settings library", function() it("prevents storing unserialisable types", function() expect.error(settings.set, "", { print }):eq("Cannot serialize type function") end) + + it("setting changes the value", function() + local random = math.random(1, 0x7FFFFFFF) + settings.set("test", random) + expect(settings.get("test")):eq(random) + end) + + it("checks the type of the value", function() + settings.define("test.defined", { default = 123, description = "A description", type = "number" }) + expect.error(settings.set, "test.defined", "hello") + :eq("bad argument #2 (expected number, got string)") + settings.set("test.defined", 123) + end) + + it("setting fires an event", function() + settings.clear() + + local s = stub(os, "queueEvent") + settings.set("test", 1) + settings.set("test", 2) + + expect(s):called_with("setting_changed", "test", 1, nil) + expect(s):called_with("setting_changed", "test", 2, 1) + end) end) describe("settings.get", function() @@ -20,6 +58,43 @@ describe("The settings library", function() settings.get("test") expect.error(settings.get, nil):eq("bad argument #1 (expected string, got nil)") end) + + it("returns the default", function() + expect(settings.get("test.undefined")):eq(nil) + expect(settings.get("test.undefined", "?")):eq("?") + + settings.define("test.unset", { default = "default" }) + expect(settings.get("test.unset")):eq("default") + expect(settings.get("test.unset", "?")):eq("?") + end) + end) + + describe("settings.getDetails", function() + it("validates arguments", function() + expect.error(settings.getDetails, nil):eq("bad argument #1 (expected string, got nil)") + end) + + it("works on undefined and unset values", function() + expect(settings.getDetails("test.undefined")):same { value = nil, changed = false } + end) + + it("works on undefined but set values", function() + settings.set("test", 456) + expect(settings.getDetails("test")):same { value = 456, changed = true } + end) + + it("works on defined but unset values", function() + settings.define("test.unset", { default = 123, description = "A description" }) + expect(settings.getDetails("test.unset")):same + { default = 123, value = 123, changed = false, description = "A description" } + end) + + it("works on defined and set values", function() + settings.define("test.defined", { default = 123, description = "A description", type = "number" }) + settings.set("test.defined", 456) + expect(settings.getDetails("test.defined")):same + { default = 123, value = 456, changed = true, description = "A description", type = "number" } + end) end) describe("settings.unset", function() @@ -27,17 +102,73 @@ describe("The settings library", function() settings.unset("test") expect.error(settings.unset, nil):eq("bad argument #1 (expected string, got nil)") end) + + it("unsetting resets the value", function() + settings.set("test", true) + settings.unset("test") + expect(settings.get("test")):eq(nil) + end) + + it("unsetting does not touch defaults", function() + settings.define("test.defined", { default = 123 }) + settings.set("test.defined", 456) + settings.unset("test.defined") + expect(settings.get("test.defined")):eq(123) + end) + + it("unsetting fires an event", function() + settings.set("test", 1) + + local s = stub(os, "queueEvent") + settings.unset("test") + expect(s):called_with("setting_changed", "test", nil, 1) + end) + end) + + describe("settings.clear", function() + it("clearing resets all values", function() + settings.set("test", true) + settings.clear() + expect(settings.get("test")):eq(nil) + end) + + it("clearing does not touch defaults", function() + settings.define("test.defined", { default = 123 }) + settings.set("test.defined", 456) + settings.clear() + expect(settings.get("test.defined")):eq(123) + end) + + it("clearing fires an event", function() + settings.set("test", 1) + + local s = stub(os, "queueEvent") + settings.clear() + expect(s):called_with("setting_changed", "test", nil, 1) + end) end) describe("settings.load", function() it("validates arguments", function() - expect.error(settings.load, nil):eq("bad argument #1 (expected string, got nil)") + expect.error(settings.load, 1):eq("bad argument #1 (expected string, got number)") + end) + + it("defaults to .settings", function() + local s = stub(fs, "open") + settings.load() + expect(s):called_with(".settings", "r") end) end) describe("settings.save", function() it("validates arguments", function() - expect.error(settings.save, nil):eq("bad argument #1 (expected string, got nil)") + expect.error(settings.save, 1):eq("bad argument #1 (expected string, got number)") + end) + + it("defaults to .settings", function() + local s = stub(fs, "open") + settings.save() + expect(s):called_with(".settings", "w") end) end) end) diff --git a/src/test/resources/test-rom/spec/programs/set_spec.lua b/src/test/resources/test-rom/spec/programs/set_spec.lua index 46cf0b84d..8d74d951e 100644 --- a/src/test/resources/test-rom/spec/programs/set_spec.lua +++ b/src/test/resources/test-rom/spec/programs/set_spec.lua @@ -1,29 +1,59 @@ local capture = require "test_helpers".capture_program describe("The set program", function() + local function setup() + local set = setmetatable({}, { __index = _G }) + loadfile("/rom/apis/settings.lua", set)() + stub(_G, "settings", set) + + settings.set("test", "Hello World!") + settings.define("test.defined", { default = 456, description = "A description", type = "number" }) + end it("displays all settings", function() - settings.clear() - settings.set("Test", "Hello World!") - settings.set("123", 456) + setup() expect(capture(stub, "set")) - :matches { ok = true, output = '"123" is 456\n"Test" is "Hello World!"\n', error = "" } + :matches { ok = true, output = '"test" is "Hello World!"\n"test.defined" is 456\n', error = "" } end) - it("displays a single settings", function() - settings.clear() - settings.set("Test", "Hello World!") - settings.set("123", 456) + it("displays a single setting", function() + setup() - expect(capture(stub, "set Test")) - :matches { ok = true, output = '"Test" is "Hello World!"\n', error = "" } + expect(capture(stub, "set test")) + :matches { ok = true, output = 'test is "Hello World!"\n', error = "" } + end) + + it("displays a single setting with description", function() + setup() + + expect(capture(stub, "set test")) + :matches { ok = true, output = 'test is "Hello World!"\n', error = "" } + end) + + it("displays a changed setting with description", function() + setup() + + settings.set("test.defined", 123) + expect(capture(stub, "set test.defined")) + :matches { ok = true, output = 'test.defined is 123 (default is 456)\nA description\n', error = "" } end) it("set a setting", function() - expect(capture(stub, "set Test Hello")) - :matches { ok = true, output = '"Test" set to "Hello"\n', error = "" } + setup() - expect(settings.get("Test")):eq("Hello") + expect(capture(stub, "set test Hello")) + :matches { ok = true, output = '"test" set to "Hello"\n', error = "" } + + expect(settings.get("test")):eq("Hello") + end) + + it("checks the type of a setting", function() + setup() + + expect(capture(stub, "set test.defined Hello")) + :matches { ok = true, output = "", error = '"Hello" is not a valid number.\n' } + expect(capture(stub, "set test.defined 456")) + :matches { ok = true, output = '"test.defined" set to 456\n', error = "" } end) end) From 3b7b8459304ced12b3a893aa9369b50fd64fd8a1 Mon Sep 17 00:00:00 2001 From: SquidDev Date: Tue, 21 Apr 2020 12:12:44 +0100 Subject: [PATCH 33/35] Some tiny bits of documentation cleanup I'm really good at this English lark :D:. --- illuaminate.sexp | 17 +++++++++++++++++ .../computercraft/lua/rom/apis/settings.lua | 7 ++++++- .../computercraft/lua/rom/apis/window.lua | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/illuaminate.sexp b/illuaminate.sexp index 38ed350c5..f727a3a8e 100644 --- a/illuaminate.sexp +++ b/illuaminate.sexp @@ -59,3 +59,20 @@ (at /doc/stub (linters -var:unused-global) (lint (allow-toplevel-global true))) + +;; Ensure any fully documented modules stay fully documented. +(at + (/src/main/resources/assets/computercraft/lua/rom/apis/colors.lua + /src/main/resources/assets/computercraft/lua/rom/apis/colours.lua + /src/main/resources/assets/computercraft/lua/rom/apis/disk.lua + /src/main/resources/assets/computercraft/lua/rom/apis/gps.lua + /src/main/resources/assets/computercraft/lua/rom/apis/help.lua + /src/main/resources/assets/computercraft/lua/rom/apis/keys.lua + /src/main/resources/assets/computercraft/lua/rom/apis/paintutils.lua + /src/main/resources/assets/computercraft/lua/rom/apis/parallel.lua + /src/main/resources/assets/computercraft/lua/rom/apis/peripheral.lua + /src/main/resources/assets/computercraft/lua/rom/apis/rednet.lua + /src/main/resources/assets/computercraft/lua/rom/apis/settings.lua + /src/main/resources/assets/computercraft/lua/rom/apis/texutils.lua + /src/main/resources/assets/computercraft/lua/rom/apis/vector.lua) + (linters doc:undocumented doc:undocumented-arg)) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua index fcfb1d17a..3046f65b1 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua @@ -124,7 +124,12 @@ function get(name, default) end end ---- Get details about a specific setting +--- Get details about a specific setting. +-- +-- @tparam string name The name of the setting to get. +-- @treturn { description? = string, default? = value, type? = string, value? = value } +-- Information about this setting. This includes all information from @{settings.define}, +-- as well as this setting's value. function getDetails(name) expect(1, name, "string") local deets = copy(details[name]) or {} diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua index 57bd70c54..d93533f03 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/window.lua @@ -445,7 +445,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- @treturn string The textual content of this line. -- @treturn string The text colours of this line, suitable for use with @{term.blit}. -- @treturn string The background colours of this line, suitable for use with @{term.blit}. - -- @throws If `y` is a valid line. + -- @throws If `y` is not between 1 and this window's height. function window.getLine(y) if type(y) ~= "number" then expect(1, y, "number") end From 463635a45918b54606732ddc204018b000e17d06 Mon Sep 17 00:00:00 2001 From: Wojbie Date: Tue, 21 Apr 2020 18:28:55 +0200 Subject: [PATCH 34/35] Fix save() sPath param. --- .../resources/assets/computercraft/lua/rom/apis/settings.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua index 3046f65b1..c1fcb45f6 100644 --- a/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/assets/computercraft/lua/rom/apis/settings.lua @@ -228,7 +228,7 @@ end -- @see settings.load function save(sPath) expect(1, sPath, "string", "nil") - local file = fs.open(".settings", "w") + local file = fs.open(sPath or ".settings", "w") if not file then return false end From f3de97d67fc74876b85f823b69da163b8d37b11b Mon Sep 17 00:00:00 2001 From: JakobDev Date: Wed, 22 Apr 2020 07:30:49 +0200 Subject: [PATCH 35/35] Remove / and \ at the start of shell.setDir() (#410) Uses fs.combine to normalise the file path. This removes leading/trailing slashes, as well as any redundant "../"s within the path. --- .../resources/assets/computercraft/lua/rom/programs/shell.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua b/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua index 8a63138f8..0d4b9f337 100644 --- a/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua +++ b/src/main/resources/assets/computercraft/lua/rom/programs/shell.lua @@ -194,7 +194,7 @@ function shell.setDir(_sDir) if not fs.isDir(_sDir) then error("Not a directory", 2) end - sDir = _sDir + sDir = fs.combine(_sDir, "") end function shell.path()