-- see if the file exists local function file_exists(file) local f = io.open(file, "rb") if f then f:close() end return f ~= nil end -- get all lines from a file, returns an empty -- list/table if the file does not exist local function lines_from(file) if not file_exists(file) then return {} end local lines = {} for line in io.lines(file) do lines[#lines + 1] = line end return lines end local function splitstr(inputstr, sep) if sep == nil then sep = "%s" end local t = {} for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do t[#t + 1] = str end return t end -- tests the functions above local file = "input" local lines = lines_from(file) local reports = {} for _, line in ipairs(lines) do local raw_report = splitstr(line) for i, v in ipairs(raw_report) do raw_report[i] = tonumber(v) end reports[#reports + 1] = raw_report end -- return the sign of a number local function sign(number) if number > 0 then return 1 elseif number == 0 then return 0 else return -1 end end function table.shallow_copy(t) local t2 = {} for k, v in pairs(t) do t2[k] = v end return t2 end local function is_safe(report) local s = nil -- keeps track of the sign over the course of a report local unsafe = false local unsafe_idx_1, unsafe_idx_2 for i, v1 in ipairs(report) do local v2 = report[i + 1] -- if v2 doesn't exist, we are at the end of the report if v2 ~= nil then if s == nil then -- if we are on the first iteration, we need to set the direction for -- the rest of the report s = sign(v1 - v2) else -- we check that the sign matches the rest of the report, breaking early if not if sign(v1 - v2) ~= s then unsafe = true unsafe_idx_1, unsafe_idx_2 = i, i + 1 break end end -- Checking if the rate of change in the report is within acceptable bounds local distance = math.abs(v1 - v2) if distance < 1 or distance > 3 then unsafe = true unsafe_idx_1, unsafe_idx_2 = i, i + 1 break end end end return unsafe, unsafe_idx_1, unsafe_idx_2 end -- Copy the report, remove the specified index, and test if it is safe local function test_altered_report(report, idx) local test_report = table.shallow_copy(report) table.remove(test_report, idx) return is_safe(test_report) end local safe_count = 0 for _, report in ipairs(reports) do -- if unsafe == true, idx1 and idx2 will be null local unsafe, idx1, idx2 = is_safe(report) if unsafe then -- if the report is unsafe, generate 2 reports with the potentially bad values removed, and test them again if not test_altered_report(report, idx1) or not test_altered_report(report, idx2) then unsafe = false end end if not unsafe then safe_count = safe_count + 1 end end print(safe_count)