| rev | line source | 
| jbe/bsw@0 | 1 #!/usr/bin/env lua | 
| jbe/bsw@0 | 2 | 
| jbe/bsw@0 | 3 local _G             = _G | 
| jbe/bsw@0 | 4 local _VERSION       = _VERSION | 
| jbe/bsw@0 | 5 local assert         = assert | 
| jbe/bsw@0 | 6 local error          = error | 
| jbe/bsw@0 | 7 local getfenv        = getfenv | 
| jbe/bsw@0 | 8 local getmetatable   = getmetatable | 
| jbe/bsw@0 | 9 local ipairs         = ipairs | 
| jbe/bsw@0 | 10 local module         = module | 
| jbe/bsw@0 | 11 local next           = next | 
| jbe/bsw@0 | 12 local pairs          = pairs | 
| jbe/bsw@0 | 13 local print          = print | 
| jbe/bsw@0 | 14 local rawequal       = rawequal | 
| jbe/bsw@0 | 15 local rawget         = rawget | 
| jbe/bsw@0 | 16 local rawset         = rawset | 
| jbe/bsw@0 | 17 local require        = require | 
| jbe/bsw@0 | 18 local select         = select | 
| jbe/bsw@0 | 19 local setfenv        = setfenv | 
| jbe/bsw@0 | 20 local setmetatable   = setmetatable | 
| jbe/bsw@0 | 21 local tonumber       = tonumber | 
| jbe/bsw@0 | 22 local tostring       = tostring | 
| jbe/bsw@0 | 23 local type           = type | 
| jbe/bsw@0 | 24 local unpack         = unpack | 
| jbe/bsw@0 | 25 | 
| jbe/bsw@0 | 26 local coroutine = coroutine | 
| jbe/bsw@0 | 27 local io        = io | 
| jbe/bsw@0 | 28 local math      = math | 
| jbe/bsw@0 | 29 local os        = os | 
| jbe/bsw@0 | 30 local string    = string | 
| jbe/bsw@0 | 31 local table     = table | 
| jbe/bsw@0 | 32 | 
| jbe/bsw@0 | 33 module(...) | 
| jbe/bsw@0 | 34 | 
| jbe/bsw@0 | 35 | 
| jbe/bsw@0 | 36 | 
| jbe/bsw@0 | 37 --------------------------------------- | 
| jbe/bsw@0 | 38 -- general functions and definitions -- | 
| jbe/bsw@0 | 39 --------------------------------------- | 
| jbe/bsw@0 | 40 | 
| jbe/bsw@0 | 41 --[[-- | 
| jbe/bsw@0 | 42 bool =            -- true, if value is an integer within resolution | 
| jbe/bsw@0 | 43 atom.is_integer( | 
| jbe/bsw@0 | 44   value           -- value to be tested | 
| jbe/bsw@0 | 45 ) | 
| jbe/bsw@0 | 46 | 
| jbe/bsw@0 | 47 This function returns true if the given object is an integer within resolution. | 
| jbe/bsw@0 | 48 | 
| jbe/bsw@0 | 49 --]]-- | 
| jbe/bsw@0 | 50 function is_integer(i) | 
| jbe/bsw@0 | 51   return | 
| jbe/bsw@0 | 52     type(i) == "number" and i % 1 == 0 and | 
| jbe/bsw@0 | 53     (i + 1) - i == 1 and i - (i - 1) == 1 | 
| jbe/bsw@0 | 54 end | 
| jbe/bsw@0 | 55 --//-- | 
| jbe/bsw@0 | 56 | 
| jbe/bsw@0 | 57 --[[-- | 
| jbe/bsw@0 | 58 atom.not_a_number | 
| jbe/bsw@0 | 59 | 
| jbe/bsw@0 | 60 Value representing an invalid numeric result. Used for atom.integer.invalid and atom.number.invalid. | 
| jbe/bsw@0 | 61 | 
| jbe/bsw@0 | 62 --]]-- | 
| jbe/bsw@0 | 63 not_a_number = 0 / 0 | 
| jbe/bsw@0 | 64 --//-- | 
| jbe/bsw@0 | 65 | 
| jbe/bsw@0 | 66 do | 
| jbe/bsw@0 | 67 | 
| jbe/bsw@0 | 68   local shadow = setmetatable({}, { __mode = "k" }) | 
| jbe/bsw@0 | 69 | 
| jbe/bsw@0 | 70   local type_mt = { __index = {} } | 
| jbe/bsw@0 | 71 | 
| jbe/bsw@0 | 72   function type_mt:__call(...) | 
| jbe/bsw@0 | 73     return self:new(...) | 
| jbe/bsw@0 | 74   end | 
| jbe/bsw@0 | 75 | 
| jbe/bsw@0 | 76   function type_mt.__index:_create(data) | 
| jbe/bsw@0 | 77     local value = setmetatable({}, self) | 
| jbe/bsw@0 | 78     shadow[value] = data | 
| jbe/bsw@0 | 79     return value | 
| jbe/bsw@0 | 80   end | 
| jbe/bsw@0 | 81 | 
| jbe/bsw@0 | 82   local function write_prohibited() | 
| jbe/bsw@0 | 83     error("Modification of an atom is prohibited.") | 
| jbe/bsw@0 | 84   end | 
| jbe/bsw@0 | 85 | 
| jbe/bsw@0 | 86   -- returns a new type as a table, which serves also as metatable | 
| jbe/bsw@0 | 87   function create_new_type(name) | 
| jbe/bsw@0 | 88     local t = setmetatable( | 
| jbe/bsw@0 | 89       { methods = {}, getters = {}, name = name }, | 
| jbe/bsw@0 | 90       type_mt | 
| jbe/bsw@0 | 91     ) | 
| jbe/bsw@0 | 92     function t.__index(self, key) | 
| jbe/bsw@0 | 93       local data = shadow[self] | 
| jbe/bsw@0 | 94       local value = data[key] | 
| jbe/bsw@0 | 95       if value ~= nil then return value end | 
| jbe/bsw@0 | 96       local method = t.methods[key] | 
| jbe/bsw@0 | 97       if method then return method end | 
| jbe/bsw@0 | 98       local getter = t.getters[key] | 
| jbe/bsw@0 | 99       if getter then return getter(self) end | 
| jbe/bsw@0 | 100     end | 
| jbe/bsw@0 | 101     t.__newindex = write_prohibited | 
| jbe/bsw@0 | 102     return t | 
| jbe/bsw@0 | 103   end | 
| jbe/bsw@0 | 104 | 
| jbe/bsw@0 | 105   --[[-- | 
| jbe/bsw@0 | 106   bool =          -- true, if 'value' is of type 't' | 
| jbe/bsw@0 | 107   atom.has_type( | 
| jbe/bsw@0 | 108     value,        -- any value | 
| jbe/bsw@0 | 109     t             -- atom time, e.g. atom.date, or lua type, e.g. "string" | 
| jbe/bsw@0 | 110   ) | 
| jbe/bsw@0 | 111 | 
| jbe/bsw@0 | 112   This function checks, if a value is of a given type. The value may be an invalid value though, e.g. atom.date.invalid. | 
| jbe/bsw@0 | 113 | 
| jbe/bsw@0 | 114   --]]-- | 
| jbe/bsw@0 | 115   function has_type(value, t) | 
| jbe/bsw@0 | 116     if t == nil then error("No type passed to has_type(...) function.") end | 
| jbe/bsw@0 | 117     local lua_type = type(value) | 
| jbe/bsw@0 | 118     return | 
| jbe/bsw@0 | 119       lua_type == t or | 
| jbe/bsw@0 | 120       getmetatable(value) == t or | 
| jbe/bsw@0 | 121       (lua_type == "boolean" and t == _M.boolean) or | 
| jbe/bsw@0 | 122       (lua_type == "string" and t == _M.string) or ( | 
| jbe/bsw@0 | 123         lua_type == "number" and | 
| jbe/bsw@0 | 124         (t == _M.number or ( | 
| jbe/bsw@0 | 125           t == _M.integer and ( | 
| jbe/bsw@0 | 126             not (value <= 0 or value >= 0) or ( | 
| jbe/bsw@0 | 127               value % 1 == 0 and | 
| jbe/bsw@0 | 128               (value + 1) - value == 1 and | 
| jbe/bsw@0 | 129               value - (value - 1) == 1 | 
| jbe/bsw@0 | 130             ) | 
| jbe/bsw@0 | 131           ) | 
| jbe/bsw@0 | 132         )) | 
| jbe/bsw@0 | 133       ) | 
| jbe/bsw@0 | 134   end | 
| jbe/bsw@0 | 135   --//-- | 
| jbe/bsw@0 | 136 | 
| jbe/bsw@0 | 137   --[[-- | 
| jbe/bsw@0 | 138   bool =          -- true, if 'value' is of type 't' | 
| jbe/bsw@0 | 139   atom.is_valid( | 
| jbe/bsw@0 | 140     value,        -- any value | 
| jbe/bsw@0 | 141     t             -- atom time, e.g. atom.date, or lua type, e.g. "string" | 
| jbe/bsw@0 | 142   ) | 
| jbe/bsw@0 | 143 | 
| jbe/bsw@0 | 144   This function checks, if a value is valid. It optionally checks, if the value is of a given type. | 
| jbe/bsw@0 | 145 | 
| jbe/bsw@0 | 146   --]]-- | 
| jbe/bsw@0 | 147   function is_valid(value, t) | 
| jbe/bsw@0 | 148     local lua_type = type(value) | 
| jbe/bsw@0 | 149     if lua_type == "table" then | 
| jbe/bsw@0 | 150       local mt = getmetatable(value) | 
| jbe/bsw@0 | 151       if t then | 
| jbe/bsw@0 | 152         return t == mt and not value.invalid | 
| jbe/bsw@0 | 153       else | 
| jbe/bsw@0 | 154         return (getmetatable(mt) == type_mt) and not value.invalid | 
| jbe/bsw@0 | 155       end | 
| jbe/bsw@0 | 156     elseif lua_type == "boolean" then | 
| jbe/bsw@0 | 157       return not t or t == "boolean" or t == _M.boolean | 
| jbe/bsw@0 | 158     elseif lua_type == "string" then | 
| jbe/bsw@0 | 159       return not t or t == "string" or t == _M.string | 
| jbe/bsw@0 | 160     elseif lua_type == "number" then | 
| jbe/bsw@0 | 161       if t == _M.integer then | 
| jbe/bsw@0 | 162         return | 
| jbe/bsw@0 | 163           value % 1 == 0 and | 
| jbe/bsw@0 | 164           (value + 1) - value == 1 and | 
| jbe/bsw@0 | 165           value - (value - 1) == 1 | 
| jbe/bsw@0 | 166       else | 
| jbe/bsw@0 | 167         return | 
| jbe/bsw@0 | 168           (not t or t == "number" or t == _M.number) and | 
| jbe/bsw@0 | 169           (value <= 0 or value >= 0) | 
| jbe/bsw@0 | 170       end | 
| jbe/bsw@0 | 171     else | 
| jbe/bsw@0 | 172       return false | 
| jbe/bsw@0 | 173     end | 
| jbe/bsw@0 | 174   end | 
| jbe/bsw@0 | 175   --//-- | 
| jbe/bsw@0 | 176 | 
| jbe/bsw@0 | 177 end | 
| jbe/bsw@0 | 178 | 
| jbe/bsw@0 | 179 --[[-- | 
| jbe/bsw@0 | 180 string =    -- string representation to be passed to a load function | 
| jbe/bsw@0 | 181 atom.dump( | 
| jbe/bsw@0 | 182   value     -- value to be dumped | 
| jbe/bsw@0 | 183 ) | 
| jbe/bsw@0 | 184 | 
| jbe/bsw@0 | 185 This function returns a string representation of the given value. | 
| jbe/bsw@0 | 186 | 
| jbe/bsw@0 | 187 --]]-- | 
| jbe/bsw@0 | 188 function dump(obj) | 
| jbe/bsw@0 | 189   if obj == nil then | 
| jbe/bsw@0 | 190     return "" | 
| jbe/bsw@0 | 191   else | 
| jbe/bsw@0 | 192     return tostring(obj) | 
| jbe/bsw@0 | 193   end | 
| jbe/bsw@0 | 194 end | 
| jbe/bsw@0 | 195 --//-- | 
| jbe/bsw@0 | 196 | 
| jbe/bsw@0 | 197 | 
| jbe/bsw@0 | 198 | 
| jbe/bsw@0 | 199 ------------- | 
| jbe/bsw@0 | 200 -- boolean -- | 
| jbe/bsw@0 | 201 ------------- | 
| jbe/bsw@0 | 202 | 
| jbe/bsw@0 | 203 boolean = { name = "boolean" } | 
| jbe/bsw@0 | 204 | 
| jbe/bsw@0 | 205 --[[-- | 
| jbe/bsw@0 | 206 bool =              -- true, false, or nil | 
| jbe/bsw@0 | 207 atom.boolean:load( | 
| jbe/bsw@0 | 208   string            -- string to be interpreted as boolean | 
| jbe/bsw@0 | 209 ) | 
| jbe/bsw@0 | 210 | 
| jbe/bsw@0 | 211 This method returns true or false or nil, depending on the input string. | 
| jbe/bsw@0 | 212 | 
| jbe/bsw@0 | 213 --]]-- | 
| jbe/bsw@0 | 214 function boolean:load(str) | 
| jbe/bsw@0 | 215   if type(str) ~= "string" then | 
| jbe/bsw@0 | 216     error("String expected") | 
| jbe/bsw@0 | 217   elseif str == "" then | 
| jbe/bsw@0 | 218     return nil | 
| jbe/bsw@0 | 219   elseif string.find(str, "^[TtYy1]") then | 
| jbe/bsw@0 | 220     return true | 
| jbe/bsw@0 | 221   elseif string.find(str, "^[FfNn0]") then | 
| jbe/bsw@0 | 222     return false | 
| jbe/bsw@0 | 223   else | 
| jbe/bsw@0 | 224     return nil  -- we don't have an undefined bool | 
| jbe/bsw@0 | 225   end | 
| jbe/bsw@0 | 226 end | 
| jbe/bsw@0 | 227 --//-- | 
| jbe/bsw@0 | 228 | 
| jbe/bsw@0 | 229 | 
| jbe/bsw@0 | 230 | 
| jbe/bsw@0 | 231 ------------ | 
| jbe/bsw@0 | 232 -- string -- | 
| jbe/bsw@0 | 233 ------------ | 
| jbe/bsw@0 | 234 | 
| jbe/bsw@0 | 235 _M.string = { name = "string" } | 
| jbe/bsw@0 | 236 | 
| jbe/bsw@0 | 237 --[[-- | 
| jbe/bsw@0 | 238 string =            -- the same string | 
| jbe/bsw@0 | 239 atom.string:load( | 
| jbe/bsw@0 | 240   string            -- a string | 
| jbe/bsw@0 | 241 ) | 
| jbe/bsw@0 | 242 | 
| jbe/bsw@0 | 243 This method returns the passed string, or throws an error, if the passed argument is not a string. | 
| jbe/bsw@0 | 244 | 
| jbe/bsw@0 | 245 --]]-- | 
| jbe/bsw@0 | 246 function _M.string:load(str) | 
| jbe/bsw@0 | 247   if type(str) ~= "string" then | 
| jbe/bsw@0 | 248     error("String expected") | 
| jbe/bsw@0 | 249   else | 
| jbe/bsw@0 | 250     return str | 
| jbe/bsw@0 | 251   end | 
| jbe/bsw@0 | 252 end | 
| jbe/bsw@0 | 253 --//-- | 
| jbe/bsw@0 | 254 | 
| jbe/bsw@0 | 255 | 
| jbe/bsw@0 | 256 | 
| jbe/bsw@0 | 257 ------------- | 
| jbe/bsw@0 | 258 -- integer -- | 
| jbe/bsw@0 | 259 ------------- | 
| jbe/bsw@0 | 260 | 
| jbe/bsw@0 | 261 integer = { name = "integer" } | 
| jbe/bsw@0 | 262 | 
| jbe/bsw@0 | 263 --[[-- | 
| jbe/bsw@0 | 264 int =              -- an integer or atom.integer.invalid (atom.not_a_number) | 
| jbe/bsw@0 | 265 atom.integer:load( | 
| jbe/bsw@0 | 266   string           -- a string representing an integer | 
| jbe/bsw@0 | 267 ) | 
| jbe/bsw@0 | 268 | 
| jbe/bsw@0 | 269 This method returns an integer represented by the given string. If the string doesn't represent a valid integer, then not-a-number is returned. | 
| jbe/bsw@0 | 270 | 
| jbe/bsw@0 | 271 --]]-- | 
| jbe/bsw@0 | 272 function integer:load(str) | 
| jbe/bsw@0 | 273   if type(str) ~= "string" then | 
| jbe/bsw@0 | 274     error("String expected") | 
| jbe/bsw@0 | 275   elseif str == "" then | 
| jbe/bsw@0 | 276     return nil | 
| jbe/bsw@0 | 277   else | 
| jbe/bsw@0 | 278     local num = tonumber(str) | 
| jbe/bsw@0 | 279     if is_integer(num) then return num else return not_a_number end | 
| jbe/bsw@0 | 280   end | 
| jbe/bsw@0 | 281 end | 
| jbe/bsw@0 | 282 --//-- | 
| jbe/bsw@0 | 283 | 
| jbe/bsw@0 | 284 --[[-- | 
| jbe/bsw@0 | 285 atom.integer.invalid | 
| jbe/bsw@0 | 286 | 
| jbe/bsw@0 | 287 This represents an invalid integer. | 
| jbe/bsw@0 | 288 | 
| jbe/bsw@0 | 289 --]]-- | 
| jbe/bsw@0 | 290 integer.invalid = not_a_number | 
| jbe/bsw@0 | 291 --// | 
| jbe/bsw@0 | 292 | 
| jbe/bsw@0 | 293 | 
| jbe/bsw@0 | 294 | 
| jbe/bsw@0 | 295 ------------ | 
| jbe/bsw@0 | 296 -- number -- | 
| jbe/bsw@0 | 297 ------------ | 
| jbe/bsw@0 | 298 | 
| jbe/bsw@0 | 299 number = create_new_type("number") | 
| jbe/bsw@0 | 300 | 
| jbe/bsw@0 | 301 --[[-- | 
| jbe/bsw@0 | 302 int =              -- a number or atom.number.invalid (atom.not_a_number) | 
| jbe/bsw@0 | 303 atom.number:load( | 
| jbe/bsw@0 | 304   string           -- a string representing a number | 
| jbe/bsw@0 | 305 ) | 
| jbe/bsw@0 | 306 | 
| jbe/bsw@0 | 307 This method returns a number represented by the given string. If the string doesn't represent a valid number, then not-a-number is returned. | 
| jbe/bsw@0 | 308 | 
| jbe/bsw@0 | 309 --]]-- | 
| jbe/bsw@0 | 310 function number:load(str) | 
| jbe/bsw@0 | 311   if type(str) ~= "string" then | 
| jbe/bsw@0 | 312     error("String expected") | 
| jbe/bsw@0 | 313   elseif str == "" then | 
| jbe/bsw@0 | 314     return nil | 
| jbe/bsw@0 | 315   else | 
| jbe/bsw@0 | 316     return tonumber(str) or not_a_number | 
| jbe/bsw@0 | 317   end | 
| jbe/bsw@0 | 318 end | 
| jbe/bsw@0 | 319 --//-- | 
| jbe/bsw@0 | 320 | 
| jbe/bsw@0 | 321 --[[-- | 
| jbe/bsw@0 | 322 atom.number.invalid | 
| jbe/bsw@0 | 323 | 
| jbe/bsw@0 | 324 This represents an invalid number. | 
| jbe/bsw@0 | 325 | 
| jbe/bsw@0 | 326 --]]-- | 
| jbe/bsw@0 | 327 number.invalid = not_a_number | 
| jbe/bsw@0 | 328 --//-- | 
| jbe/bsw@0 | 329 | 
| jbe/bsw@0 | 330 | 
| jbe/bsw@0 | 331 | 
| jbe/bsw@0 | 332 -------------- | 
| jbe/bsw@0 | 333 -- fraction -- | 
| jbe/bsw@0 | 334 -------------- | 
| jbe/bsw@0 | 335 | 
| jbe/bsw@0 | 336 fraction = create_new_type("fraction") | 
| jbe/bsw@0 | 337 | 
| jbe/bsw@0 | 338 --[[-- | 
| jbe/bsw@0 | 339 i =        -- the greatest common divisor (GCD) of all given natural numbers | 
| jbe/bsw@0 | 340 atom.gcd( | 
| jbe/bsw@0 | 341   a,      -- a natural number | 
| jbe/bsw@0 | 342   b,      -- another natural number | 
| jbe/bsw@0 | 343   ...     -- optionally more natural numbers | 
| jbe/bsw@0 | 344 ) | 
| jbe/bsw@0 | 345 | 
| jbe/bsw@0 | 346 This function returns the greatest common divisor (GCD) of two or more natural numbers. | 
| jbe/bsw@0 | 347 | 
| jbe/bsw@0 | 348 --]]-- | 
| jbe/bsw@0 | 349 function gcd(a, b, ...) | 
| jbe/bsw@0 | 350   if a % 1 ~= 0 or a <= 0 then return 0 / 0 end | 
| jbe/bsw@0 | 351   if b == nil then | 
| jbe/bsw@0 | 352     return a | 
| jbe/bsw@0 | 353   else | 
| jbe/bsw@0 | 354     if b % 1 ~= 0 or b <= 0 then return 0 / 0 end | 
| jbe/bsw@0 | 355     if ... == nil then | 
| jbe/bsw@0 | 356       local k = 0 | 
| jbe/bsw@0 | 357       local t | 
| jbe/bsw@0 | 358       while a % 2 == 0 and b % 2 == 0 do | 
| jbe/bsw@0 | 359         a = a / 2; b = b / 2; k = k + 1 | 
| jbe/bsw@0 | 360       end | 
| jbe/bsw@0 | 361       if a % 2 == 0 then t = a else t = -b end | 
| jbe/bsw@0 | 362       while t ~= 0 do | 
| jbe/bsw@0 | 363         while t % 2 == 0 do t = t / 2 end | 
| jbe/bsw@0 | 364         if t > 0 then a = t else b = -t end | 
| jbe/bsw@0 | 365         t = a - b | 
| jbe/bsw@0 | 366       end | 
| jbe/bsw@0 | 367       return a * 2 ^ k | 
| jbe/bsw@0 | 368     else | 
| jbe/bsw@0 | 369       return gcd(gcd(a, b), ...) | 
| jbe/bsw@0 | 370     end | 
| jbe/bsw@0 | 371   end | 
| jbe/bsw@0 | 372 end | 
| jbe/bsw@0 | 373 --//-- | 
| jbe/bsw@0 | 374 | 
| jbe/bsw@0 | 375 --[[-- | 
| jbe/bsw@0 | 376 i =        --the least common multiple (LCD) of all given natural numbers | 
| jbe/bsw@0 | 377 atom.lcm( | 
| jbe/bsw@0 | 378   a,       -- a natural number | 
| jbe/bsw@0 | 379   b,       -- another natural number | 
| jbe/bsw@0 | 380   ...      -- optionally more natural numbers | 
| jbe/bsw@0 | 381 ) | 
| jbe/bsw@0 | 382 | 
| jbe/bsw@0 | 383 This function returns the least common multiple (LCD) of two or more natural numbers. | 
| jbe/bsw@0 | 384 | 
| jbe/bsw@0 | 385 --]]-- | 
| jbe/bsw@0 | 386 function lcm(a, b, ...) | 
| jbe/bsw@0 | 387   if a % 1 ~= 0 or a <= 0 then return 0 / 0 end | 
| jbe/bsw@0 | 388   if b == nil then | 
| jbe/bsw@0 | 389     return a | 
| jbe/bsw@0 | 390   else | 
| jbe/bsw@0 | 391     if b % 1 ~= 0 or b <= 0 then return 0 / 0 end | 
| jbe/bsw@0 | 392     if ... == nil then | 
| jbe/bsw@0 | 393       return a * b / gcd(a, b) | 
| jbe/bsw@0 | 394     else | 
| jbe/bsw@0 | 395       return lcm(lcm(a, b), ...) | 
| jbe/bsw@0 | 396     end | 
| jbe/bsw@0 | 397   end | 
| jbe/bsw@0 | 398 end | 
| jbe/bsw@0 | 399 --//-- | 
| jbe/bsw@0 | 400 | 
| jbe/bsw@0 | 401 --[[-- | 
| jbe/bsw@0 | 402 atom.fraction.invalid | 
| jbe/bsw@0 | 403 | 
| jbe/bsw@0 | 404 Value representing an invalid fraction. | 
| jbe/bsw@0 | 405 | 
| jbe/bsw@0 | 406 --]]-- | 
| jbe/bsw@0 | 407 fraction.invalid = fraction:_create{ | 
| jbe/bsw@0 | 408   numerator = not_a_number, denominator = not_a_number, invalid = true | 
| jbe/bsw@0 | 409 } | 
| jbe/bsw@0 | 410 --//-- | 
| jbe/bsw@0 | 411 | 
| jbe/bsw@0 | 412 --[[-- | 
| jbe/bsw@0 | 413 frac =              -- fraction | 
| jbe/bsw@0 | 414 atom.fraction:new( | 
| jbe/bsw@0 | 415   numerator,        -- numerator | 
| jbe/bsw@0 | 416   denominator       -- denominator | 
| jbe/bsw@0 | 417 ) | 
| jbe/bsw@0 | 418 | 
| jbe/bsw@0 | 419 This method creates a new fraction. | 
| jbe/bsw@0 | 420 | 
| jbe/bsw@0 | 421 --]]-- | 
| jbe/bsw@0 | 422 function fraction:new(numerator, denominator) | 
| jbe/bsw@0 | 423   if not ( | 
| jbe/bsw@0 | 424     (numerator == nil   or type(numerator)   == "number") and | 
| jbe/bsw@0 | 425     (denominator == nil or type(denominator) == "number") | 
| jbe/bsw@0 | 426   ) then | 
| jbe/bsw@0 | 427     error("Invalid arguments passed to fraction constructor.") | 
| jbe/bsw@0 | 428   elseif | 
| jbe/bsw@0 | 429     (not is_integer(numerator)) or | 
| jbe/bsw@0 | 430     (denominator and (not is_integer(denominator))) | 
| jbe/bsw@0 | 431   then | 
| jbe/bsw@0 | 432     return fraction.invalid | 
| jbe/bsw@0 | 433   elseif denominator then | 
| jbe/bsw@0 | 434     if denominator == 0 then | 
| jbe/bsw@0 | 435       return fraction.invalid | 
| jbe/bsw@0 | 436     elseif numerator == 0 then | 
| jbe/bsw@0 | 437       return fraction:_create{ numerator = 0, denominator = 1, float = 0 } | 
| jbe/bsw@0 | 438     else | 
| jbe/bsw@0 | 439       local d = gcd(math.abs(numerator), math.abs(denominator)) | 
| jbe/bsw@0 | 440       if denominator < 0 then d = -d end | 
| jbe/bsw@0 | 441       local numerator2, denominator2 = numerator / d, denominator / d | 
| jbe/bsw@0 | 442       return fraction:_create{ | 
| jbe/bsw@0 | 443         numerator   = numerator2, | 
| jbe/bsw@0 | 444         denominator = denominator2, | 
| jbe/bsw@0 | 445         float       = numerator2 / denominator2 | 
| jbe/bsw@0 | 446       } | 
| jbe/bsw@0 | 447     end | 
| jbe/bsw@0 | 448   else | 
| jbe/bsw@0 | 449     return fraction:_create{ | 
| jbe/bsw@0 | 450       numerator = numerator, denominator = 1, float = numerator | 
| jbe/bsw@0 | 451     } | 
| jbe/bsw@0 | 452   end | 
| jbe/bsw@0 | 453 end | 
| jbe/bsw@0 | 454 --//-- | 
| jbe/bsw@0 | 455 | 
| jbe/bsw@0 | 456 --[[-- | 
| jbe/bsw@0 | 457 frac =               -- fraction represented by the given string | 
| jbe/bsw@0 | 458 atom.fraction:load( | 
| jbe/bsw@0 | 459   string             -- string representation of a fraction | 
| jbe/bsw@0 | 460 ) | 
| jbe/bsw@0 | 461 | 
| jbe/bsw@0 | 462 This method returns a fraction represented by the given string. | 
| jbe/bsw@0 | 463 | 
| jbe/bsw@0 | 464 --]]-- | 
| jbe/bsw@0 | 465 function fraction:load(str) | 
| jbe/bsw@0 | 466   if str == "" then | 
| jbe/bsw@0 | 467     return nil | 
| jbe/bsw@0 | 468   else | 
| jbe/bsw@0 | 469     local sign, int = string.match(str, "^(%-?)([0-9]+)$") | 
| jbe/bsw@0 | 470     if sign == "" then return fraction:new(tonumber(int)) | 
| jbe/bsw@0 | 471     elseif sign == "-" then return fraction:new(- tonumber(int)) | 
| jbe/bsw@0 | 472     end | 
| jbe/bsw@0 | 473     local sign, n, d = string.match(str, "^(%-?)([0-9]+)/([0-9]+)$") | 
| jbe/bsw@0 | 474     if sign == "" then return fraction:new(tonumber(n), tonumber(d)) | 
| jbe/bsw@0 | 475     elseif sign == "-" then return fraction:new(- tonumber(n), tonumber(d)) | 
| jbe/bsw@0 | 476     end | 
| jbe/bsw@0 | 477     return fraction.invalid | 
| jbe/bsw@0 | 478   end | 
| jbe/bsw@0 | 479 end | 
| jbe/bsw@0 | 480 --//-- | 
| jbe/bsw@0 | 481 | 
| jbe/bsw@0 | 482 function fraction:__tostring() | 
| jbe/bsw@0 | 483   if self.invalid then | 
| jbe/bsw@0 | 484     return "not_a_fraction" | 
| jbe/bsw@0 | 485   else | 
| jbe/bsw@0 | 486     return self.numerator .. "/" .. self.denominator | 
| jbe/bsw@0 | 487   end | 
| jbe/bsw@0 | 488 end | 
| jbe/bsw@0 | 489 | 
| jbe/bsw@0 | 490 function fraction.__eq(value1, value2) | 
| jbe/bsw@0 | 491   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 492     return false | 
| jbe/bsw@0 | 493   else | 
| jbe/bsw@0 | 494     return | 
| jbe/bsw@0 | 495       value1.numerator == value2.numerator and | 
| jbe/bsw@0 | 496       value1.denominator == value2.denominator | 
| jbe/bsw@0 | 497   end | 
| jbe/bsw@0 | 498 end | 
| jbe/bsw@0 | 499 | 
| jbe/bsw@0 | 500 function fraction.__lt(value1, value2) | 
| jbe/bsw@0 | 501   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 502     return false | 
| jbe/bsw@0 | 503   else | 
| jbe/bsw@0 | 504     return value1.float < value2.float | 
| jbe/bsw@0 | 505   end | 
| jbe/bsw@0 | 506 end | 
| jbe/bsw@0 | 507 | 
| jbe/bsw@0 | 508 function fraction.__le(value1, value2) | 
| jbe/bsw@0 | 509   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 510     return false | 
| jbe/bsw@0 | 511   else | 
| jbe/bsw@0 | 512     return value1.float <= value2.float | 
| jbe/bsw@0 | 513   end | 
| jbe/bsw@0 | 514 end | 
| jbe/bsw@0 | 515 | 
| jbe/bsw@0 | 516 function fraction:__unm() | 
| jbe/bsw@0 | 517   return fraction(-self.numerator, self.denominator) | 
| jbe/bsw@0 | 518 end | 
| jbe/bsw@0 | 519 | 
| jbe/bsw@0 | 520 do | 
| jbe/bsw@0 | 521 | 
| jbe/bsw@0 | 522   local function extract(value1, value2) | 
| jbe/bsw@0 | 523     local n1, d1, n2, d2 | 
| jbe/bsw@0 | 524     if getmetatable(value1) == fraction then | 
| jbe/bsw@0 | 525       n1 = value1.numerator | 
| jbe/bsw@0 | 526       d1 = value1.denominator | 
| jbe/bsw@0 | 527     elseif type(value1) == "number" then | 
| jbe/bsw@0 | 528       n1 = value1 | 
| jbe/bsw@0 | 529       d1 = 1 | 
| jbe/bsw@0 | 530     else | 
| jbe/bsw@0 | 531       error("Left operand of operator has wrong type.") | 
| jbe/bsw@0 | 532     end | 
| jbe/bsw@0 | 533     if getmetatable(value2) == fraction then | 
| jbe/bsw@0 | 534       n2 = value2.numerator | 
| jbe/bsw@0 | 535       d2 = value2.denominator | 
| jbe/bsw@0 | 536     elseif type(value2) == "number" then | 
| jbe/bsw@0 | 537       n2 = value2 | 
| jbe/bsw@0 | 538       d2 = 1 | 
| jbe/bsw@0 | 539     else | 
| jbe/bsw@0 | 540       error("Right operand of operator has wrong type.") | 
| jbe/bsw@0 | 541     end | 
| jbe/bsw@0 | 542     return n1, d1, n2, d2 | 
| jbe/bsw@0 | 543   end | 
| jbe/bsw@0 | 544 | 
| jbe/bsw@0 | 545   function fraction.__add(value1, value2) | 
| jbe/bsw@0 | 546     local n1, d1, n2, d2 = extract(value1, value2) | 
| jbe/bsw@0 | 547     return fraction(n1 * d2 + n2 * d1, d1 * d2) | 
| jbe/bsw@0 | 548   end | 
| jbe/bsw@0 | 549 | 
| jbe/bsw@0 | 550   function fraction.__sub(value1, value2) | 
| jbe/bsw@0 | 551     local n1, d1, n2, d2 = extract(value1, value2) | 
| jbe/bsw@0 | 552     return fraction(n1 * d2 - n2 * d1, d1 * d2) | 
| jbe/bsw@0 | 553   end | 
| jbe/bsw@0 | 554 | 
| jbe/bsw@0 | 555   function fraction.__mul(value1, value2) | 
| jbe/bsw@0 | 556     local n1, d1, n2, d2 = extract(value1, value2) | 
| jbe/bsw@0 | 557     return fraction(n1 * n2, d1 * d2) | 
| jbe/bsw@0 | 558   end | 
| jbe/bsw@0 | 559 | 
| jbe/bsw@0 | 560   function fraction.__div(value1, value2) | 
| jbe/bsw@0 | 561     local n1, d1, n2, d2 = extract(value1, value2) | 
| jbe/bsw@0 | 562     return fraction(n1 * d2, d1 * n2) | 
| jbe/bsw@0 | 563   end | 
| jbe/bsw@0 | 564 | 
| jbe/bsw@0 | 565   function fraction.__pow(value1, value2) | 
| jbe/bsw@0 | 566     local n1, d1, n2, d2 = extract(value1, value2) | 
| jbe/bsw@0 | 567     local n1_abs = math.abs(n1) | 
| jbe/bsw@0 | 568     local d1_abs = math.abs(d1) | 
| jbe/bsw@0 | 569     local n2_abs = math.abs(n2) | 
| jbe/bsw@0 | 570     local d2_abs = math.abs(d2) | 
| jbe/bsw@0 | 571     local numerator, denominator | 
| jbe/bsw@0 | 572     if d2_abs == 1 then | 
| jbe/bsw@0 | 573       numerator = n1_abs | 
| jbe/bsw@0 | 574       denominator = d1_abs | 
| jbe/bsw@0 | 575     else | 
| jbe/bsw@0 | 576       numerator = 0 | 
| jbe/bsw@0 | 577       while true do | 
| jbe/bsw@0 | 578         local t = numerator ^ d2_abs | 
| jbe/bsw@0 | 579         if t == n1_abs then break end | 
| jbe/bsw@0 | 580         if not (t < n1_abs) then return value1.float / value2.float end | 
| jbe/bsw@0 | 581         numerator = numerator + 1 | 
| jbe/bsw@0 | 582       end | 
| jbe/bsw@0 | 583       denominator = 1 | 
| jbe/bsw@0 | 584       while true do | 
| jbe/bsw@0 | 585         local t = denominator ^ d2_abs | 
| jbe/bsw@0 | 586         if t == d1_abs then break end | 
| jbe/bsw@0 | 587         if not (t < d1_abs) then return value1.float / value2.float end | 
| jbe/bsw@0 | 588         denominator = denominator + 1 | 
| jbe/bsw@0 | 589       end | 
| jbe/bsw@0 | 590     end | 
| jbe/bsw@0 | 591     if n1 < 0 then | 
| jbe/bsw@0 | 592       if d2_abs % 2 == 1 then | 
| jbe/bsw@0 | 593         numerator = -numerator | 
| jbe/bsw@0 | 594       else | 
| jbe/bsw@0 | 595         return fraction.invalid | 
| jbe/bsw@0 | 596       end | 
| jbe/bsw@0 | 597     end | 
| jbe/bsw@0 | 598     if n2 < 0 then | 
| jbe/bsw@0 | 599       numerator, denominator = denominator, numerator | 
| jbe/bsw@0 | 600     end | 
| jbe/bsw@0 | 601     return fraction(numerator ^ n2_abs, denominator ^ n2_abs) | 
| jbe/bsw@0 | 602   end | 
| jbe/bsw@0 | 603 | 
| jbe/bsw@0 | 604 end | 
| jbe/bsw@0 | 605 | 
| jbe/bsw@0 | 606 | 
| jbe/bsw@0 | 607 | 
| jbe/bsw@0 | 608 ---------- | 
| jbe/bsw@0 | 609 -- date -- | 
| jbe/bsw@0 | 610 ---------- | 
| jbe/bsw@0 | 611 | 
| jbe/bsw@0 | 612 date = create_new_type("date") | 
| jbe/bsw@0 | 613 | 
| jbe/bsw@0 | 614 do | 
| jbe/bsw@0 | 615   local c1 = 365             -- days of a non-leap year | 
| jbe/bsw@0 | 616   local c4 = 4 * c1 + 1      -- days of a full 4 year cycle | 
| jbe/bsw@0 | 617   local c100 = 25 * c4 - 1   -- days of a full 100 year cycle | 
| jbe/bsw@0 | 618   local c400 = 4 * c100 + 1  -- days of a full 400 year cycle | 
| jbe/bsw@0 | 619   local get_month_offset     -- function returning days elapsed within | 
| jbe/bsw@0 | 620                              -- the given year until the given month | 
| jbe/bsw@0 | 621                              -- (exclusive the given month) | 
| jbe/bsw@0 | 622   do | 
| jbe/bsw@0 | 623     local normal_month_offsets = {} | 
| jbe/bsw@0 | 624     local normal_month_lengths = { | 
| jbe/bsw@0 | 625       31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | 
| jbe/bsw@0 | 626     } | 
| jbe/bsw@0 | 627     local sum = 0 | 
| jbe/bsw@0 | 628     for i = 1, 12 do | 
| jbe/bsw@0 | 629       normal_month_offsets[i] = sum | 
| jbe/bsw@0 | 630       sum = sum + normal_month_lengths[i] | 
| jbe/bsw@0 | 631     end | 
| jbe/bsw@0 | 632     function get_month_offset(year, month) | 
| jbe/bsw@0 | 633       if | 
| jbe/bsw@0 | 634         (((year % 4 == 0) and not (year % 100 == 0)) or (year % 400 == 0)) | 
| jbe/bsw@0 | 635         and month > 2 | 
| jbe/bsw@0 | 636       then | 
| jbe/bsw@0 | 637         return normal_month_offsets[month] + 1 | 
| jbe/bsw@0 | 638       else | 
| jbe/bsw@0 | 639         return normal_month_offsets[month] | 
| jbe/bsw@0 | 640       end | 
| jbe/bsw@0 | 641     end | 
| jbe/bsw@0 | 642   end | 
| jbe/bsw@0 | 643 | 
| jbe/bsw@0 | 644   --[[-- | 
| jbe/bsw@0 | 645   jd =                  -- days from January 1st 1970 | 
| jbe/bsw@0 | 646   atom.date.ymd_to_jd( | 
| jbe/bsw@0 | 647     year,               -- year | 
| jbe/bsw@0 | 648     month,              -- month from 1 to 12 | 
| jbe/bsw@0 | 649     day                 -- day from 1 to 31 | 
| jbe/bsw@0 | 650   ) | 
| jbe/bsw@0 | 651 | 
| jbe/bsw@0 | 652   This function calculates the days from January 1st 1970 for a given year, month and day. | 
| jbe/bsw@0 | 653 | 
| jbe/bsw@0 | 654   --]]-- | 
| jbe/bsw@0 | 655   local offset = 0 | 
| jbe/bsw@0 | 656   function date.ymd_to_jd(year, month, day) | 
| jbe/bsw@0 | 657     assert(is_integer(year), "Invalid year specified.") | 
| jbe/bsw@0 | 658     assert(is_integer(month), "Invalid month specified.") | 
| jbe/bsw@0 | 659     assert(is_integer(day), "Invalid day specified.") | 
| jbe/bsw@0 | 660     local calc_year = year - 1 | 
| jbe/bsw@0 | 661     local n400 = math.floor(calc_year / 400) | 
| jbe/bsw@0 | 662     local r400 = calc_year % 400 | 
| jbe/bsw@0 | 663     local n100 = math.floor(r400 / 100) | 
| jbe/bsw@0 | 664     local r100 = r400 % 100 | 
| jbe/bsw@0 | 665     local n4 = math.floor(r100 / 4) | 
| jbe/bsw@0 | 666     local n1 = r100 % 4 | 
| jbe/bsw@0 | 667     local jd = ( | 
| jbe/bsw@0 | 668       c400 * n400 + c100 * n100 + c4 * n4 + c1 * n1 + | 
| jbe/bsw@0 | 669       get_month_offset(year, month) + (day - 1) | 
| jbe/bsw@0 | 670     ) | 
| jbe/bsw@0 | 671     return jd - offset | 
| jbe/bsw@0 | 672   end | 
| jbe/bsw@0 | 673   offset = date.ymd_to_jd(1970, 1, 1) | 
| jbe/bsw@0 | 674   --//-- | 
| jbe/bsw@0 | 675 | 
| jbe/bsw@0 | 676   --[[-- | 
| jbe/bsw@0 | 677   year,                 -- year | 
| jbe/bsw@0 | 678   month,                -- month from 1 to 12 | 
| jbe/bsw@0 | 679   day =                 -- day from 1 to 31 | 
| jbe/bsw@0 | 680   atom.date.jd_to_ymd( | 
| jbe/bsw@0 | 681     jd,                 -- days from January 1st 1970 | 
| jbe/bsw@0 | 682   ) | 
| jbe/bsw@0 | 683 | 
| jbe/bsw@0 | 684   Given the days from January 1st 1970 this function returns year, month and day. | 
| jbe/bsw@0 | 685 | 
| jbe/bsw@0 | 686   --]]-- | 
| jbe/bsw@0 | 687   function date.jd_to_ymd(jd) | 
| jbe/bsw@0 | 688     assert(is_integer(jd), "Invalid julian date specified.") | 
| jbe/bsw@0 | 689     local calc_jd = jd + offset | 
| jbe/bsw@0 | 690     assert(is_integer(calc_jd), "Julian date is out of range.") | 
| jbe/bsw@0 | 691     local n400 = math.floor(calc_jd / c400) | 
| jbe/bsw@0 | 692     local r400 = calc_jd % c400 | 
| jbe/bsw@0 | 693     local n100 = math.floor(r400 / c100) | 
| jbe/bsw@0 | 694     local r100 = r400 % c100 | 
| jbe/bsw@0 | 695     if n100 == 4 then n100, r100 = 3, c100 end | 
| jbe/bsw@0 | 696     local n4 = math.floor(r100 / c4) | 
| jbe/bsw@0 | 697     local r4 = r100 % c4 | 
| jbe/bsw@0 | 698     local n1 = math.floor(r4 / c1) | 
| jbe/bsw@0 | 699     local r1 = r4 % c1 | 
| jbe/bsw@0 | 700     if n1 == 4 then n1, r1 = 3, c1 end | 
| jbe/bsw@0 | 701     local year = 1 + 400 * n400 + 100 * n100 + 4 * n4 + n1 | 
| jbe/bsw@0 | 702     local month = 1 + math.floor(r1 / 31) | 
| jbe/bsw@0 | 703     local month_offset = get_month_offset(year, month) | 
| jbe/bsw@0 | 704     if month < 12 then | 
| jbe/bsw@0 | 705       local next_month_offset = get_month_offset(year, month + 1) | 
| jbe/bsw@0 | 706       if r1 >= next_month_offset then | 
| jbe/bsw@0 | 707         month = month + 1 | 
| jbe/bsw@0 | 708         month_offset = next_month_offset | 
| jbe/bsw@0 | 709       end | 
| jbe/bsw@0 | 710     end | 
| jbe/bsw@0 | 711     local day = 1 + r1 - month_offset | 
| jbe/bsw@0 | 712     return year, month, day | 
| jbe/bsw@0 | 713   end | 
| jbe/bsw@0 | 714   --//-- | 
| jbe/bsw@0 | 715 end | 
| jbe/bsw@0 | 716 | 
| jbe/bsw@0 | 717 --[[-- | 
| jbe/bsw@0 | 718 atom.date.invalid | 
| jbe/bsw@0 | 719 | 
| jbe/bsw@0 | 720 Value representing an invalid date. | 
| jbe/bsw@0 | 721 | 
| jbe/bsw@0 | 722 --]]-- | 
| jbe/bsw@0 | 723 date.invalid = date:_create{ | 
| jbe/bsw@0 | 724   jd = not_a_number, | 
| jbe/bsw@0 | 725   year = not_a_number, month = not_a_number, day = not_a_number, | 
| jbe/bsw@0 | 726   invalid = true | 
| jbe/bsw@0 | 727 } | 
| jbe/bsw@0 | 728 --//-- | 
| jbe/bsw@0 | 729 | 
| jbe/bsw@0 | 730 --[[-- | 
| jbe/bsw@0 | 731 d =                             -- date based on the given data | 
| jbe/bsw@0 | 732 atom.date:new{ | 
| jbe/bsw@0 | 733   jd           = jd,            -- days since January 1st 1970 | 
| jbe/bsw@0 | 734   year         = year,          -- year | 
| jbe/bsw@0 | 735   month        = month,         -- month from 1 to 12 | 
| jbe/bsw@0 | 736   day          = day,           -- day from 1 to 31 | 
| jbe/bsw@0 | 737   iso_weekyear = iso_weekyear,  -- year according to ISO 8601 | 
| jbe/bsw@0 | 738   iso_week     = iso_week,      -- week number according to ISO 8601 | 
| jbe/bsw@0 | 739   iso_weekday  = iso_weekday,   -- day of week from 1 for monday to 7 for sunday | 
| jbe/bsw@0 | 740   us_weekyear  = us_weekyear,   -- year | 
| jbe/bsw@0 | 741   us_week      = us_week,       -- week number according to US style counting | 
| jbe/bsw@0 | 742   us_weekday   = us_weekday     -- day of week from 1 for sunday to 7 for saturday | 
| jbe/bsw@0 | 743 } | 
| jbe/bsw@0 | 744 | 
| jbe/bsw@0 | 745 This method returns a new date value, based on given data. | 
| jbe/bsw@0 | 746 | 
| jbe/bsw@0 | 747 --]]-- | 
| jbe/bsw@0 | 748 function date:new(args) | 
| jbe/bsw@0 | 749   local args = args | 
| jbe/bsw@0 | 750   if type(args) == "number" then args = { jd = args } end | 
| jbe/bsw@0 | 751   if type(args) == "table" then | 
| jbe/bsw@0 | 752     local year, month, day = args.year, args.month, args.day | 
| jbe/bsw@0 | 753     local jd = args.jd | 
| jbe/bsw@0 | 754     local iso_weekyear = args.iso_weekyear | 
| jbe/bsw@0 | 755     local iso_week     = args.iso_week | 
| jbe/bsw@0 | 756     local iso_weekday  = args.iso_weekday | 
| jbe/bsw@0 | 757     local us_week      = args.us_week | 
| jbe/bsw@0 | 758     local us_weekday   = args.us_weekday | 
| jbe/bsw@0 | 759     if | 
| jbe/bsw@0 | 760       type(year)  == "number" and | 
| jbe/bsw@0 | 761       type(month) == "number" and | 
| jbe/bsw@0 | 762       type(day)   == "number" | 
| jbe/bsw@0 | 763     then | 
| jbe/bsw@0 | 764       if | 
| jbe/bsw@0 | 765         is_integer(year)  and year >= 1  and year <= 9999 and | 
| jbe/bsw@0 | 766         is_integer(month) and month >= 1 and month <= 12  and | 
| jbe/bsw@0 | 767         is_integer(day)   and day >= 1   and day <= 31 | 
| jbe/bsw@0 | 768       then | 
| jbe/bsw@0 | 769         return date:_create{ | 
| jbe/bsw@0 | 770           jd = date.ymd_to_jd(year, month, day), | 
| jbe/bsw@0 | 771           year = year, month = month, day = day | 
| jbe/bsw@0 | 772         } | 
| jbe/bsw@0 | 773       else | 
| jbe/bsw@0 | 774         return date.invalid | 
| jbe/bsw@0 | 775       end | 
| jbe/bsw@0 | 776     elseif type(jd) == "number" then | 
| jbe/bsw@0 | 777       if is_integer(jd) and jd >= -719162 and jd <= 2932896 then | 
| jbe/bsw@0 | 778         local year, month, day = date.jd_to_ymd(jd) | 
| jbe/bsw@0 | 779         return date:_create{ | 
| jbe/bsw@0 | 780           jd = jd, year = year, month = month, day = day | 
| jbe/bsw@0 | 781         } | 
| jbe/bsw@0 | 782       else | 
| jbe/bsw@0 | 783         return date.invalid | 
| jbe/bsw@0 | 784       end | 
| jbe/bsw@0 | 785     elseif | 
| jbe/bsw@0 | 786       type(year)        == "number" and not iso_weekyear and | 
| jbe/bsw@0 | 787       type(iso_week)    == "number" and | 
| jbe/bsw@0 | 788       type(iso_weekday) == "number" | 
| jbe/bsw@0 | 789     then | 
| jbe/bsw@0 | 790       if | 
| jbe/bsw@0 | 791         is_integer(year) and | 
| jbe/bsw@0 | 792         is_integer(iso_week)     and iso_week >= 0 and iso_week <= 53 and | 
| jbe/bsw@0 | 793         is_integer(iso_weekday)  and iso_weekday >= 1 and iso_weekday <= 7 | 
| jbe/bsw@0 | 794       then | 
| jbe/bsw@0 | 795         local jan4 = date{ year = year, month = 1, day = 4 } | 
| jbe/bsw@0 | 796         local reference = jan4 - jan4.iso_weekday - 7  -- Sun. of week -1 | 
| jbe/bsw@0 | 797         return date(reference + 7 * iso_week + iso_weekday) | 
| jbe/bsw@0 | 798       else | 
| jbe/bsw@0 | 799         return date.invalid | 
| jbe/bsw@0 | 800       end | 
| jbe/bsw@0 | 801     elseif | 
| jbe/bsw@0 | 802       type(iso_weekyear) == "number" and not year and | 
| jbe/bsw@0 | 803       type(iso_week)     == "number" and | 
| jbe/bsw@0 | 804       type(iso_weekday)  == "number" | 
| jbe/bsw@0 | 805     then | 
| jbe/bsw@0 | 806       if | 
| jbe/bsw@0 | 807         is_integer(iso_weekyear) and | 
| jbe/bsw@0 | 808         is_integer(iso_week)     and iso_week >= 0 and iso_week <= 53 and | 
| jbe/bsw@0 | 809         is_integer(iso_weekday)  and iso_weekday >= 1 and iso_weekday <= 7 | 
| jbe/bsw@0 | 810       then | 
| jbe/bsw@0 | 811         local guessed = date{ | 
| jbe/bsw@0 | 812           year        = iso_weekyear, | 
| jbe/bsw@0 | 813           iso_week    = iso_week, | 
| jbe/bsw@0 | 814           iso_weekday = iso_weekday | 
| jbe/bsw@0 | 815         } | 
| jbe/bsw@0 | 816         if guessed.invalid or guessed.iso_weekyear == iso_weekyear then | 
| jbe/bsw@0 | 817           return guessed | 
| jbe/bsw@0 | 818         else | 
| jbe/bsw@0 | 819           local year | 
| jbe/bsw@0 | 820           if iso_week <= 1 then | 
| jbe/bsw@0 | 821             year = iso_weekyear - 1 | 
| jbe/bsw@0 | 822           elseif iso_week >= 52 then | 
| jbe/bsw@0 | 823             year = iso_weekyear + 1 | 
| jbe/bsw@0 | 824           else | 
| jbe/bsw@0 | 825             error("Internal error in ISO week computation occured.") | 
| jbe/bsw@0 | 826           end | 
| jbe/bsw@0 | 827           return date{ | 
| jbe/bsw@0 | 828             year = year, iso_week = iso_week, iso_weekday = iso_weekday | 
| jbe/bsw@0 | 829           } | 
| jbe/bsw@0 | 830         end | 
| jbe/bsw@0 | 831       else | 
| jbe/bsw@0 | 832         return date.invalid | 
| jbe/bsw@0 | 833       end | 
| jbe/bsw@0 | 834     elseif | 
| jbe/bsw@0 | 835       type(year) == "number" and | 
| jbe/bsw@0 | 836       type(us_week)     == "number" and | 
| jbe/bsw@0 | 837       type(us_weekday)  == "number" | 
| jbe/bsw@0 | 838     then | 
| jbe/bsw@0 | 839       if | 
| jbe/bsw@0 | 840         is_integer(year) and | 
| jbe/bsw@0 | 841         is_integer(us_week)     and us_week >= 0    and us_week <= 54   and | 
| jbe/bsw@0 | 842         is_integer(us_weekday)  and us_weekday >= 1 and us_weekday <= 7 | 
| jbe/bsw@0 | 843       then | 
| jbe/bsw@0 | 844         local jan1 = date{ year = year, month = 1, day = 1 } | 
| jbe/bsw@0 | 845         local reference = jan1 - jan1.us_weekday - 7  -- Sat. of week -1 | 
| jbe/bsw@0 | 846         return date(reference + 7 * us_week + us_weekday) | 
| jbe/bsw@0 | 847       else | 
| jbe/bsw@0 | 848         return date.invalid | 
| jbe/bsw@0 | 849       end | 
| jbe/bsw@0 | 850     end | 
| jbe/bsw@0 | 851   end | 
| jbe/bsw@0 | 852   error("Illegal arguments passed to date constructor.") | 
| jbe/bsw@0 | 853 end | 
| jbe/bsw@0 | 854 --//-- | 
| jbe/bsw@0 | 855 | 
| jbe/bsw@0 | 856 --[[-- | 
| jbe/bsw@0 | 857 atom.date:get_current() | 
| jbe/bsw@0 | 858 | 
| jbe/bsw@0 | 859 This function returns today's date. | 
| jbe/bsw@0 | 860 | 
| jbe/bsw@0 | 861 --]]-- | 
| jbe/bsw@0 | 862 function date:get_current() | 
| jbe/bsw@0 | 863   local now = os.date("*t") | 
| jbe/bsw@0 | 864   return date{ | 
| jbe/bsw@0 | 865     year = now.year, month = now.month, day = now.day | 
| jbe/bsw@0 | 866   } | 
| jbe/bsw@0 | 867 end | 
| jbe/bsw@0 | 868 --//-- | 
| jbe/bsw@0 | 869 | 
| jbe/bsw@0 | 870 --[[-- | 
| jbe/bsw@0 | 871 date =           -- date represented by the string | 
| jbe/bsw@0 | 872 atom.date:load( | 
| jbe/bsw@0 | 873   string         -- string representing a date | 
| jbe/bsw@0 | 874 ) | 
| jbe/bsw@0 | 875 | 
| jbe/bsw@0 | 876 This method returns a date represented by the given string. | 
| jbe/bsw@0 | 877 | 
| jbe/bsw@0 | 878 --]]-- | 
| jbe/bsw@0 | 879 function date:load(str) | 
| jbe/bsw@0 | 880   if str == "" then | 
| jbe/bsw@0 | 881     return nil | 
| jbe/bsw@0 | 882   else | 
| jbe/bsw@0 | 883     local year, month, day = string.match( | 
| jbe/bsw@0 | 884       str, "^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$" | 
| jbe/bsw@0 | 885     ) | 
| jbe/bsw@0 | 886     if year then | 
| jbe/bsw@0 | 887       return date{ | 
| jbe/bsw@0 | 888         year = tonumber(year), | 
| jbe/bsw@0 | 889         month = tonumber(month), | 
| jbe/bsw@0 | 890         day = tonumber(day) | 
| jbe/bsw@0 | 891       } | 
| jbe/bsw@0 | 892     else | 
| jbe/bsw@0 | 893       return date.invalid | 
| jbe/bsw@0 | 894     end | 
| jbe/bsw@0 | 895   end | 
| jbe/bsw@0 | 896 end | 
| jbe/bsw@0 | 897 --//-- | 
| jbe/bsw@0 | 898 | 
| jbe/bsw@0 | 899 function date:__tostring() | 
| jbe/bsw@0 | 900   if self.invalid then | 
| jbe/bsw@0 | 901     return "invalid_date" | 
| jbe/bsw@0 | 902   else | 
| jbe/bsw@0 | 903     return string.format( | 
| jbe/bsw@0 | 904       "%04i-%02i-%02i", self.year, self.month, self.day | 
| jbe/bsw@0 | 905     ) | 
| jbe/bsw@0 | 906   end | 
| jbe/bsw@0 | 907 end | 
| jbe/bsw@0 | 908 | 
| jbe/bsw@0 | 909 function date.getters:midnight() | 
| jbe/bsw@0 | 910   return time{ year = self.year, month = self.month, day = self.day } | 
| jbe/bsw@0 | 911 end | 
| jbe/bsw@0 | 912 | 
| jbe/bsw@0 | 913 function date.getters:midday() | 
| jbe/bsw@0 | 914   return time{ | 
| jbe/bsw@0 | 915     year = self.year, month = self.month, day = self.day, | 
| jbe/bsw@0 | 916     hour = 12 | 
| jbe/bsw@0 | 917   } | 
| jbe/bsw@0 | 918 end | 
| jbe/bsw@0 | 919 | 
| jbe/bsw@0 | 920 function date.getters:iso_weekday()  -- 1 = Monday | 
| jbe/bsw@0 | 921   return (self.jd + 3) % 7 + 1 | 
| jbe/bsw@0 | 922 end | 
| jbe/bsw@0 | 923 | 
| jbe/bsw@0 | 924 function date.getters:us_weekday()  -- 1 = Sunday | 
| jbe/bsw@0 | 925   return (self.jd + 4) % 7 + 1 | 
| jbe/bsw@0 | 926 end | 
| jbe/bsw@0 | 927 | 
| jbe/bsw@0 | 928 function date.getters:iso_weekyear()  -- ISO week-numbering year | 
| jbe/bsw@0 | 929   local year, month, day = self.year, self.month, self.day | 
| jbe/bsw@0 | 930   local iso_weekday      = self.iso_weekday | 
| jbe/bsw@0 | 931   if month == 1 then | 
| jbe/bsw@0 | 932     if | 
| jbe/bsw@0 | 933       (day == 3 and iso_weekday == 7) or | 
| jbe/bsw@0 | 934       (day == 2 and iso_weekday >= 6) or | 
| jbe/bsw@0 | 935       (day == 1 and iso_weekday >= 5) | 
| jbe/bsw@0 | 936     then | 
| jbe/bsw@0 | 937       return year - 1 | 
| jbe/bsw@0 | 938     end | 
| jbe/bsw@0 | 939   elseif month == 12 then | 
| jbe/bsw@0 | 940     if | 
| jbe/bsw@0 | 941       (day == 29 and iso_weekday == 1) or | 
| jbe/bsw@0 | 942       (day == 30 and iso_weekday <= 2) or | 
| jbe/bsw@0 | 943       (day == 31 and iso_weekday <= 3) | 
| jbe/bsw@0 | 944     then | 
| jbe/bsw@0 | 945       return year + 1 | 
| jbe/bsw@0 | 946     end | 
| jbe/bsw@0 | 947   end | 
| jbe/bsw@0 | 948   return year | 
| jbe/bsw@0 | 949 end | 
| jbe/bsw@0 | 950 | 
| jbe/bsw@0 | 951 function date.getters:iso_week() | 
| jbe/bsw@0 | 952   local jan4 = date{ year = self.iso_weekyear, month = 1, day = 4 } | 
| jbe/bsw@0 | 953   local reference = jan4.jd - jan4.iso_weekday - 6  -- monday of week 0 | 
| jbe/bsw@0 | 954   return math.floor((self.jd - reference) / 7) | 
| jbe/bsw@0 | 955 end | 
| jbe/bsw@0 | 956 | 
| jbe/bsw@0 | 957 function date.getters:us_week() | 
| jbe/bsw@0 | 958   local jan1 = date{ year = self.year, month = 1, day = 1 } | 
| jbe/bsw@0 | 959   local reference = jan1.jd - jan1.us_weekday - 6  -- sunday of week 0 | 
| jbe/bsw@0 | 960   return math.floor((self.jd - reference) / 7) | 
| jbe/bsw@0 | 961 end | 
| jbe/bsw@0 | 962 | 
| jbe/bsw@0 | 963 function date.__eq(value1, value2) | 
| jbe/bsw@0 | 964   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 965     return false | 
| jbe/bsw@0 | 966   else | 
| jbe/bsw@0 | 967     return value1.jd == value2.jd | 
| jbe/bsw@0 | 968   end | 
| jbe/bsw@0 | 969 end | 
| jbe/bsw@0 | 970 | 
| jbe/bsw@0 | 971 function date.__lt(value1, value2) | 
| jbe/bsw@0 | 972   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 973     return false | 
| jbe/bsw@0 | 974   else | 
| jbe/bsw@0 | 975     return value1.jd < value2.jd | 
| jbe/bsw@0 | 976   end | 
| jbe/bsw@0 | 977 end | 
| jbe/bsw@0 | 978 | 
| jbe/bsw@0 | 979 function date.__le(value1, value2) | 
| jbe/bsw@0 | 980   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 981     return false | 
| jbe/bsw@0 | 982   else | 
| jbe/bsw@0 | 983     return value1.jd <= value2.jd | 
| jbe/bsw@0 | 984   end | 
| jbe/bsw@0 | 985 end | 
| jbe/bsw@0 | 986 | 
| jbe/bsw@0 | 987 function date.__add(value1, value2) | 
| jbe/bsw@0 | 988   if getmetatable(value1) == date then | 
| jbe/bsw@0 | 989     if getmetatable(value2) == date then | 
| jbe/bsw@0 | 990       error("Can not add two dates.") | 
| jbe/bsw@0 | 991     elseif type(value2) == "number" then | 
| jbe/bsw@0 | 992       return date(value1.jd + value2) | 
| jbe/bsw@0 | 993     else | 
| jbe/bsw@0 | 994       error("Right operand of '+' operator has wrong type.") | 
| jbe/bsw@0 | 995     end | 
| jbe/bsw@0 | 996   elseif type(value1) == "number" then | 
| jbe/bsw@0 | 997     if getmetatable(value2) == date then | 
| jbe/bsw@0 | 998       return date(value1 + value2.jd) | 
| jbe/bsw@0 | 999     else | 
| jbe/bsw@0 | 1000       error("Assertion failed") | 
| jbe/bsw@0 | 1001     end | 
| jbe/bsw@0 | 1002   else | 
| jbe/bsw@0 | 1003     error("Left operand of '+' operator has wrong type.") | 
| jbe/bsw@0 | 1004   end | 
| jbe/bsw@0 | 1005 end | 
| jbe/bsw@0 | 1006 | 
| jbe/bsw@0 | 1007 function date.__sub(value1, value2) | 
| jbe/bsw@0 | 1008   if not getmetatable(value1) == date then | 
| jbe/bsw@0 | 1009     error("Left operand of '-' operator has wrong type.") | 
| jbe/bsw@0 | 1010   end | 
| jbe/bsw@0 | 1011   if getmetatable(value2) == date then | 
| jbe/bsw@0 | 1012     return value1.jd - value2.jd  -- TODO: transform to interval | 
| jbe/bsw@0 | 1013   elseif type(value2) == "number" then | 
| jbe/bsw@0 | 1014     return date(value1.jd - value2) | 
| jbe/bsw@0 | 1015   else | 
| jbe/bsw@0 | 1016     error("Right operand of '-' operator has wrong type.") | 
| jbe/bsw@0 | 1017   end | 
| jbe/bsw@0 | 1018 end | 
| jbe/bsw@0 | 1019 | 
| jbe/bsw@0 | 1020 | 
| jbe/bsw@0 | 1021 | 
| jbe/bsw@0 | 1022 --------------- | 
| jbe/bsw@0 | 1023 -- timestamp -- | 
| jbe/bsw@0 | 1024 --------------- | 
| jbe/bsw@0 | 1025 | 
| jbe/bsw@0 | 1026 timestamp = create_new_type("timestamp") | 
| jbe/bsw@0 | 1027 | 
| jbe/bsw@0 | 1028 --[[-- | 
| jbe/bsw@0 | 1029 tsec =                          -- seconds since January 1st 1970 00:00 | 
| jbe/bsw@0 | 1030 atom.timestamp.ymdhms_to_tsec( | 
| jbe/bsw@0 | 1031   year,                         -- year | 
| jbe/bsw@0 | 1032   month,                        -- month from 1 to 12 | 
| jbe/bsw@0 | 1033   day,                          -- day from 1 to 31 | 
| jbe/bsw@0 | 1034   hour,                         -- hour from 0 to 23 | 
| jbe/bsw@0 | 1035   minute,                       -- minute from 0 to 59 | 
| jbe/bsw@0 | 1036   second                        -- second from 0 to 59 | 
| jbe/bsw@0 | 1037 ) | 
| jbe/bsw@0 | 1038 | 
| jbe/bsw@0 | 1039 Given the year, month, day, hour, minute and second, this function returns the number of seconds since January 1st 1970 00:00. | 
| jbe/bsw@0 | 1040 | 
| jbe/bsw@0 | 1041 --]]-- | 
| jbe/bsw@0 | 1042 function timestamp.ymdhms_to_tsec(year, month, day, hour, minute, second) | 
| jbe/bsw@0 | 1043   return | 
| jbe/bsw@0 | 1044     86400 * date.ymd_to_jd(year, month, day) + | 
| jbe/bsw@0 | 1045     3600 * hour + 60 * minute + second | 
| jbe/bsw@0 | 1046 end | 
| jbe/bsw@0 | 1047 --//-- | 
| jbe/bsw@0 | 1048 | 
| jbe/bsw@0 | 1049 --[[-- | 
| jbe/bsw@0 | 1050 year,                      -- year | 
| jbe/bsw@0 | 1051 month,                     -- month from 1 to 12 | 
| jbe/bsw@0 | 1052 day,                       -- day from 1 to 31 | 
| jbe/bsw@0 | 1053 hour,                      -- hour from 0 to 23 | 
| jbe/bsw@0 | 1054 minute,                    -- minute from 0 to 59 | 
| jbe/bsw@0 | 1055 second =                   -- second from 0 to 59 | 
| jbe/bsw@0 | 1056 atom.timestamp.tsec_to_ymdhms( | 
| jbe/bsw@0 | 1057   tsec                     -- seconds since January 1st 1970 00:00 | 
| jbe/bsw@0 | 1058 ) | 
| jbe/bsw@0 | 1059 | 
| jbe/bsw@0 | 1060 Given the seconds since January 1st 1970 00:00, this function returns the year, month, day, hour, minute and second. | 
| jbe/bsw@0 | 1061 | 
| jbe/bsw@0 | 1062 --]]-- | 
| jbe/bsw@0 | 1063 function timestamp.tsec_to_ymdhms(tsec) | 
| jbe/bsw@0 | 1064   local jd   = math.floor(tsec / 86400) | 
| jbe/bsw@0 | 1065   local dsec = tsec % 86400 | 
| jbe/bsw@0 | 1066   local year, month, day = date.jd_to_ymd(jd) | 
| jbe/bsw@0 | 1067   local hour   = math.floor(dsec / 3600) | 
| jbe/bsw@0 | 1068   local minute = math.floor((dsec % 3600) / 60) | 
| jbe/bsw@0 | 1069   local second = dsec % 60 | 
| jbe/bsw@0 | 1070   return year, month, day, hour, minute, second | 
| jbe/bsw@0 | 1071 end | 
| jbe/bsw@0 | 1072 --//-- | 
| jbe/bsw@0 | 1073 | 
| jbe/bsw@0 | 1074 --[[-- | 
| jbe/bsw@0 | 1075 timestamp.invalid | 
| jbe/bsw@0 | 1076 | 
| jbe/bsw@0 | 1077 Value representing an invalid timestamp. | 
| jbe/bsw@0 | 1078 | 
| jbe/bsw@0 | 1079 --]]-- | 
| jbe/bsw@0 | 1080 timestamp.invalid = timestamp:_create{ | 
| jbe/bsw@0 | 1081   tsec = not_a_number, | 
| jbe/bsw@0 | 1082   year = not_a_number, month = not_a_number, day = not_a_number, | 
| jbe/bsw@0 | 1083   hour = not_a_number, minute = not_a_number, second = not_a_number, | 
| jbe/bsw@0 | 1084   invalid = true | 
| jbe/bsw@0 | 1085 } | 
| jbe/bsw@0 | 1086 --//-- | 
| jbe/bsw@0 | 1087 | 
| jbe/bsw@0 | 1088 --[[-- | 
| jbe/bsw@0 | 1089 ts =                 -- timestamp based on given data | 
| jbe/bsw@0 | 1090 atom.timestamp:new{ | 
| jbe/bsw@0 | 1091   tsec   = tsec,     -- seconds since January 1st 1970 00:00 | 
| jbe/bsw@0 | 1092   year   = year,     -- year | 
| jbe/bsw@0 | 1093   month  = month,    -- month from 1 to 12 | 
| jbe/bsw@0 | 1094   day    = day,      -- day from 1 to 31 | 
| jbe/bsw@0 | 1095   hour   = hour,     -- hour from 0 to 23 | 
| jbe/bsw@0 | 1096   minute = minute,   -- minute from 0 to 59 | 
| jbe/bsw@0 | 1097   second = second    -- second from 0 to 59 | 
| jbe/bsw@0 | 1098 } | 
| jbe/bsw@0 | 1099 | 
| jbe/bsw@0 | 1100 This method returns a new timestamp value, based on given data. | 
| jbe/bsw@0 | 1101 | 
| jbe/bsw@0 | 1102 --]]-- | 
| jbe/bsw@0 | 1103 function timestamp:new(args) | 
| jbe/bsw@0 | 1104   local args = args | 
| jbe/bsw@0 | 1105   if type(args) == "number" then args = { tsec = args } end | 
| jbe/bsw@0 | 1106   if type(args) == "table" then | 
| jbe/bsw@0 | 1107     if not args.second then | 
| jbe/bsw@0 | 1108       args.second = 0 | 
| jbe/bsw@0 | 1109       if not args.minute then | 
| jbe/bsw@0 | 1110         args.minute = 0 | 
| jbe/bsw@0 | 1111         if not args.hour then | 
| jbe/bsw@0 | 1112           args.hour = 0 | 
| jbe/bsw@0 | 1113         end | 
| jbe/bsw@0 | 1114       end | 
| jbe/bsw@0 | 1115     end | 
| jbe/bsw@0 | 1116     if | 
| jbe/bsw@0 | 1117       type(args.year)   == "number" and | 
| jbe/bsw@0 | 1118       type(args.month)  == "number" and | 
| jbe/bsw@0 | 1119       type(args.day)    == "number" and | 
| jbe/bsw@0 | 1120       type(args.hour)   == "number" and | 
| jbe/bsw@0 | 1121       type(args.minute) == "number" and | 
| jbe/bsw@0 | 1122       type(args.second) == "number" | 
| jbe/bsw@0 | 1123     then | 
| jbe/bsw@0 | 1124       if | 
| jbe/bsw@0 | 1125         is_integer(args.year) and | 
| jbe/bsw@0 | 1126         args.year >= 1 and args.year <= 9999 and | 
| jbe/bsw@0 | 1127         is_integer(args.month) and | 
| jbe/bsw@0 | 1128         args.month >= 1 and args.month <= 12 and | 
| jbe/bsw@0 | 1129         is_integer(args.day) and | 
| jbe/bsw@0 | 1130         args.day >= 1 and args.day <= 31 and | 
| jbe/bsw@0 | 1131         is_integer(args.hour) and | 
| jbe/bsw@0 | 1132         args.hour >= 0 and args.hour <= 23 and | 
| jbe/bsw@0 | 1133         is_integer(args.minute) and | 
| jbe/bsw@0 | 1134         args.minute >= 0 and args.minute <= 59 and | 
| jbe/bsw@0 | 1135         is_integer(args.second) and | 
| jbe/bsw@0 | 1136         args.second >= 0 and args.second <= 59 | 
| jbe/bsw@0 | 1137       then | 
| jbe/bsw@0 | 1138         return timestamp:_create{ | 
| jbe/bsw@0 | 1139           tsec = timestamp.ymdhms_to_tsec( | 
| jbe/bsw@0 | 1140             args.year, args.month, args.day, | 
| jbe/bsw@0 | 1141             args.hour, args.minute, args.second | 
| jbe/bsw@0 | 1142           ), | 
| jbe/bsw@0 | 1143           year   = args.year, | 
| jbe/bsw@0 | 1144           month  = args.month, | 
| jbe/bsw@0 | 1145           day    = args.day, | 
| jbe/bsw@0 | 1146           hour   = args.hour, | 
| jbe/bsw@0 | 1147           minute = args.minute, | 
| jbe/bsw@0 | 1148           second = args.second | 
| jbe/bsw@0 | 1149         } | 
| jbe/bsw@0 | 1150       else | 
| jbe/bsw@0 | 1151         return timestamp.invalid | 
| jbe/bsw@0 | 1152       end | 
| jbe/bsw@0 | 1153     elseif type(args.tsec) == "number" then | 
| jbe/bsw@0 | 1154       if | 
| jbe/bsw@0 | 1155         is_integer(args.tsec) and | 
| jbe/bsw@0 | 1156         args.tsec >= -62135596800 and args.tsec <= 253402300799 | 
| jbe/bsw@0 | 1157       then | 
| jbe/bsw@0 | 1158         local year, month, day, hour, minute, second = | 
| jbe/bsw@0 | 1159           timestamp.tsec_to_ymdhms(args.tsec) | 
| jbe/bsw@0 | 1160         return timestamp:_create{ | 
| jbe/bsw@0 | 1161           tsec = args.tsec, | 
| jbe/bsw@0 | 1162           year = year, month = month, day = day, | 
| jbe/bsw@0 | 1163           hour = hour, minute = minute, second = second | 
| jbe/bsw@0 | 1164         } | 
| jbe/bsw@0 | 1165       else | 
| jbe/bsw@0 | 1166         return timestamp.invalid | 
| jbe/bsw@0 | 1167       end | 
| jbe/bsw@0 | 1168     end | 
| jbe/bsw@0 | 1169   end | 
| jbe/bsw@0 | 1170   error("Invalid arguments passed to timestamp constructor.") | 
| jbe/bsw@0 | 1171 end | 
| jbe/bsw@0 | 1172 --//-- | 
| jbe/bsw@0 | 1173 | 
| jbe/bsw@0 | 1174 --[[-- | 
| jbe/bsw@0 | 1175 ts =                          -- current date/time as timestamp | 
| jbe/bsw@0 | 1176 atom.timestamp:get_current() | 
| jbe/bsw@0 | 1177 | 
| jbe/bsw@0 | 1178 This function returns the current date and time as timestamp. | 
| jbe/bsw@0 | 1179 | 
| jbe/bsw@0 | 1180 --]]-- | 
| jbe/bsw@0 | 1181 function timestamp:get_current() | 
| jbe/bsw@0 | 1182   local now = os.date("*t") | 
| jbe/bsw@0 | 1183   return timestamp{ | 
| jbe/bsw@0 | 1184     year = now.year, month = now.month, day = now.day, | 
| jbe/bsw@0 | 1185     hour = now.hour, minute = now.min, second = now.sec | 
| jbe/bsw@0 | 1186   } | 
| jbe/bsw@0 | 1187 end | 
| jbe/bsw@0 | 1188 --//-- | 
| jbe/bsw@0 | 1189 | 
| jbe/bsw@0 | 1190 --[[-- | 
| jbe/bsw@0 | 1191 ts =             -- timestamp represented by the string | 
| jbe/bsw@0 | 1192 atom.timestamp:load( | 
| jbe/bsw@0 | 1193   string         -- string representing a timestamp | 
| jbe/bsw@0 | 1194 ) | 
| jbe/bsw@0 | 1195 | 
| jbe/bsw@0 | 1196 This method returns a timestamp represented by the given string. | 
| jbe/bsw@0 | 1197 | 
| jbe/bsw@0 | 1198 --]]-- | 
| jbe/bsw@0 | 1199 function timestamp:load(str) | 
| jbe/bsw@0 | 1200   if str == "" then | 
| jbe/bsw@0 | 1201     return nil | 
| jbe/bsw@0 | 1202   else | 
| jbe/bsw@0 | 1203     local year, month, day, hour, minute, second = string.match( | 
| jbe/bsw@0 | 1204       str, | 
| jbe/bsw@0 | 1205       "^([0-9][0-9][0-9][0-9])%-([0-9][0-9])%-([0-9][0-9]) ([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$" | 
| jbe/bsw@0 | 1206     ) | 
| jbe/bsw@0 | 1207     if year then | 
| jbe/bsw@0 | 1208       return timestamp{ | 
| jbe/bsw@0 | 1209         year   = tonumber(year), | 
| jbe/bsw@0 | 1210         month  = tonumber(month), | 
| jbe/bsw@0 | 1211         day    = tonumber(day), | 
| jbe/bsw@0 | 1212         hour   = tonumber(hour), | 
| jbe/bsw@0 | 1213         minute = tonumber(minute), | 
| jbe/bsw@0 | 1214         second = tonumber(second) | 
| jbe/bsw@0 | 1215       } | 
| jbe/bsw@0 | 1216     else | 
| jbe/bsw@0 | 1217       return timestamp.invalid | 
| jbe/bsw@0 | 1218     end | 
| jbe/bsw@0 | 1219   end | 
| jbe/bsw@0 | 1220 end | 
| jbe/bsw@0 | 1221 | 
| jbe/bsw@0 | 1222 function timestamp:__tostring() | 
| jbe/bsw@0 | 1223   if self.invalid then | 
| jbe/bsw@0 | 1224     return "invalid_timestamp" | 
| jbe/bsw@0 | 1225   else | 
| jbe/bsw@0 | 1226     return string.format( | 
| jbe/bsw@0 | 1227       "%04i-%02i-%02i %02i:%02i:%02i", | 
| jbe/bsw@0 | 1228       self.year, self.month, self.day, self.hour, self.minute, self.second | 
| jbe/bsw@0 | 1229     ) | 
| jbe/bsw@0 | 1230   end | 
| jbe/bsw@0 | 1231 end | 
| jbe/bsw@0 | 1232 | 
| jbe/bsw@0 | 1233 function timestamp.getters:date() | 
| jbe/bsw@0 | 1234   return date{ year = self.year, month = self.month, day = self.day } | 
| jbe/bsw@0 | 1235 end | 
| jbe/bsw@0 | 1236 | 
| jbe/bsw@0 | 1237 function timestamp.getters:time() | 
| jbe/bsw@0 | 1238   return time{ | 
| jbe/bsw@0 | 1239     hour = self.hour, | 
| jbe/bsw@0 | 1240     minute = self.minute, | 
| jbe/bsw@0 | 1241     second = self.second | 
| jbe/bsw@0 | 1242   } | 
| jbe/bsw@0 | 1243 end | 
| jbe/bsw@0 | 1244 | 
| jbe/bsw@0 | 1245 function timestamp.__eq(value1, value2) | 
| jbe/bsw@0 | 1246   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 1247     return false | 
| jbe/bsw@0 | 1248   else | 
| jbe/bsw@0 | 1249     return value1.tsec == value2.tsec | 
| jbe/bsw@0 | 1250   end | 
| jbe/bsw@0 | 1251 end | 
| jbe/bsw@0 | 1252 | 
| jbe/bsw@0 | 1253 function timestamp.__lt(value1, value2) | 
| jbe/bsw@0 | 1254   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 1255     return false | 
| jbe/bsw@0 | 1256   else | 
| jbe/bsw@0 | 1257     return value1.tsec < value2.tsec | 
| jbe/bsw@0 | 1258   end | 
| jbe/bsw@0 | 1259 end | 
| jbe/bsw@0 | 1260 | 
| jbe/bsw@0 | 1261 function timestamp.__le(value1, value2) | 
| jbe/bsw@0 | 1262   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 1263     return false | 
| jbe/bsw@0 | 1264   else | 
| jbe/bsw@0 | 1265     return value1.tsec <= value2.tsec | 
| jbe/bsw@0 | 1266   end | 
| jbe/bsw@0 | 1267 end | 
| jbe/bsw@0 | 1268 | 
| jbe/bsw@0 | 1269 function timestamp.__add(value1, value2) | 
| jbe/bsw@0 | 1270   if getmetatable(value1) == timestamp then | 
| jbe/bsw@0 | 1271     if getmetatable(value2) == timestamp then | 
| jbe/bsw@0 | 1272       error("Can not add two timestamps.") | 
| jbe/bsw@0 | 1273     elseif type(value2) == "number" then | 
| jbe/bsw@0 | 1274       return timestamp(value1.tsec + value2) | 
| jbe/bsw@0 | 1275     else | 
| jbe/bsw@0 | 1276       error("Right operand of '+' operator has wrong type.") | 
| jbe/bsw@0 | 1277     end | 
| jbe/bsw@0 | 1278   elseif type(value1) == "number" then | 
| jbe/bsw@0 | 1279     if getmetatable(value2) == timestamp then | 
| jbe/bsw@0 | 1280       return timestamp(value1 + value2.tsec) | 
| jbe/bsw@0 | 1281     else | 
| jbe/bsw@0 | 1282       error("Assertion failed") | 
| jbe/bsw@0 | 1283     end | 
| jbe/bsw@0 | 1284   else | 
| jbe/bsw@0 | 1285     error("Left operand of '+' operator has wrong type.") | 
| jbe/bsw@0 | 1286   end | 
| jbe/bsw@0 | 1287 end | 
| jbe/bsw@0 | 1288 | 
| jbe/bsw@0 | 1289 function timestamp.__sub(value1, value2) | 
| jbe/bsw@0 | 1290   if not getmetatable(value1) == timestamp then | 
| jbe/bsw@0 | 1291     error("Left operand of '-' operator has wrong type.") | 
| jbe/bsw@0 | 1292   end | 
| jbe/bsw@0 | 1293   if getmetatable(value2) == timestamp then | 
| jbe/bsw@0 | 1294     return value1.tsec - value2.tsec  -- TODO: transform to interval | 
| jbe/bsw@0 | 1295   elseif type(value2) == "number" then | 
| jbe/bsw@0 | 1296     return timestamp(value1.tsec - value2) | 
| jbe/bsw@0 | 1297   else | 
| jbe/bsw@0 | 1298     error("Right operand of '-' operator has wrong type.") | 
| jbe/bsw@0 | 1299   end | 
| jbe/bsw@0 | 1300 end | 
| jbe/bsw@0 | 1301 | 
| jbe/bsw@0 | 1302 | 
| jbe/bsw@0 | 1303 | 
| jbe/bsw@0 | 1304 ---------- | 
| jbe/bsw@0 | 1305 -- time -- | 
| jbe/bsw@0 | 1306 ---------- | 
| jbe/bsw@0 | 1307 | 
| jbe/bsw@0 | 1308 time = create_new_type("time") | 
| jbe/bsw@0 | 1309 | 
| jbe/bsw@0 | 1310 function time.hms_to_dsec(hour, minute, second) | 
| jbe/bsw@0 | 1311   return 3600 * hour + 60 * minute + second | 
| jbe/bsw@0 | 1312 end | 
| jbe/bsw@0 | 1313 | 
| jbe/bsw@0 | 1314 function time.dsec_to_hms(dsec) | 
| jbe/bsw@0 | 1315   local hour   = math.floor(dsec / 3600) | 
| jbe/bsw@0 | 1316   local minute = math.floor((dsec % 3600) / 60) | 
| jbe/bsw@0 | 1317   local second = dsec % 60 | 
| jbe/bsw@0 | 1318   return hour, minute, second | 
| jbe/bsw@0 | 1319 end | 
| jbe/bsw@0 | 1320 | 
| jbe/bsw@0 | 1321 --[[-- | 
| jbe/bsw@0 | 1322 atom.time.invalid | 
| jbe/bsw@0 | 1323 | 
| jbe/bsw@0 | 1324 Value representing an invalid time of day. | 
| jbe/bsw@0 | 1325 | 
| jbe/bsw@0 | 1326 --]]-- | 
| jbe/bsw@0 | 1327 time.invalid = time:_create{ | 
| jbe/bsw@0 | 1328   dsec = not_a_number, | 
| jbe/bsw@0 | 1329   hour = not_a_number, minute = not_a_number, second = not_a_number, | 
| jbe/bsw@0 | 1330   invalid = true | 
| jbe/bsw@0 | 1331 } | 
| jbe/bsw@0 | 1332 --//-- | 
| jbe/bsw@0 | 1333 | 
| jbe/bsw@0 | 1334 --[[-- | 
| jbe/bsw@0 | 1335 t =                 -- time based on given data | 
| jbe/bsw@0 | 1336 atom.time:new{ | 
| jbe/bsw@0 | 1337   dsec = dsec,      -- seconds since 00:00:00 | 
| jbe/bsw@0 | 1338   hour = hour,      -- hour from 0 to 23 | 
| jbe/bsw@0 | 1339   minute = minute,  -- minute from 0 to 59 | 
| jbe/bsw@0 | 1340   second = second   -- second from 0 to 59 | 
| jbe/bsw@0 | 1341 } | 
| jbe/bsw@0 | 1342 | 
| jbe/bsw@0 | 1343 This method returns a new time value, based on given data. | 
| jbe/bsw@0 | 1344 | 
| jbe/bsw@0 | 1345 --]]-- | 
| jbe/bsw@0 | 1346 function time:new(args) | 
| jbe/bsw@0 | 1347   local args = args | 
| jbe/bsw@0 | 1348   if type(args) == "number" then args = { dsec = args } end | 
| jbe/bsw@0 | 1349   if type(args) == "table" then | 
| jbe/bsw@0 | 1350     if not args.second then | 
| jbe/bsw@0 | 1351       args.second = 0 | 
| jbe/bsw@0 | 1352       if not args.minute then | 
| jbe/bsw@0 | 1353         args.minute = 0 | 
| jbe/bsw@0 | 1354       end | 
| jbe/bsw@0 | 1355     end | 
| jbe/bsw@0 | 1356     if | 
| jbe/bsw@0 | 1357       type(args.hour)   == "number" and | 
| jbe/bsw@0 | 1358       type(args.minute) == "number" and | 
| jbe/bsw@0 | 1359       type(args.second) == "number" | 
| jbe/bsw@0 | 1360     then | 
| jbe/bsw@0 | 1361       if | 
| jbe/bsw@0 | 1362         is_integer(args.hour) and | 
| jbe/bsw@0 | 1363         args.hour >= 0 and args.hour <= 23 and | 
| jbe/bsw@0 | 1364         is_integer(args.minute) and | 
| jbe/bsw@0 | 1365         args.minute >= 0 and args.minute <= 59 and | 
| jbe/bsw@0 | 1366         is_integer(args.second) and | 
| jbe/bsw@0 | 1367         args.second >= 0 and args.second <= 59 | 
| jbe/bsw@0 | 1368       then | 
| jbe/bsw@0 | 1369         return time:_create{ | 
| jbe/bsw@0 | 1370           dsec = time.hms_to_dsec(args.hour, args.minute, args.second), | 
| jbe/bsw@0 | 1371           hour   = args.hour, | 
| jbe/bsw@0 | 1372           minute = args.minute, | 
| jbe/bsw@0 | 1373           second = args.second | 
| jbe/bsw@0 | 1374         } | 
| jbe/bsw@0 | 1375       else | 
| jbe/bsw@0 | 1376         return time.invalid | 
| jbe/bsw@0 | 1377       end | 
| jbe/bsw@0 | 1378     elseif type(args.dsec) == "number" then | 
| jbe/bsw@0 | 1379       if | 
| jbe/bsw@0 | 1380         is_integer(args.dsec) and | 
| jbe/bsw@0 | 1381         args.dsec >= 0 and args.dsec <= 86399 | 
| jbe/bsw@0 | 1382       then | 
| jbe/bsw@0 | 1383         local hour, minute, second = | 
| jbe/bsw@0 | 1384           time.dsec_to_hms(args.dsec) | 
| jbe/bsw@0 | 1385         return time:_create{ | 
| jbe/bsw@0 | 1386           dsec = args.dsec, | 
| jbe/bsw@0 | 1387           hour = hour, minute = minute, second = second | 
| jbe/bsw@0 | 1388         } | 
| jbe/bsw@0 | 1389       else | 
| jbe/bsw@0 | 1390         return time.invalid | 
| jbe/bsw@0 | 1391       end | 
| jbe/bsw@0 | 1392     end | 
| jbe/bsw@0 | 1393   end | 
| jbe/bsw@0 | 1394   error("Invalid arguments passed to time constructor.") | 
| jbe/bsw@0 | 1395 end | 
| jbe/bsw@0 | 1396 --//-- | 
| jbe/bsw@0 | 1397 | 
| jbe/bsw@0 | 1398 --[[-- | 
| jbe/bsw@0 | 1399 t =                      -- current time of day | 
| jbe/bsw@0 | 1400 atom.time:get_current() | 
| jbe/bsw@0 | 1401 | 
| jbe/bsw@0 | 1402 This method returns the current time of the day. | 
| jbe/bsw@0 | 1403 | 
| jbe/bsw@0 | 1404 --]]-- | 
| jbe/bsw@0 | 1405 function time:get_current() | 
| jbe/bsw@0 | 1406   local now = os.date("*t") | 
| jbe/bsw@0 | 1407   return time{ hour = now.hour, minute = now.min, second = now.sec } | 
| jbe/bsw@0 | 1408 end | 
| jbe/bsw@0 | 1409 --//-- | 
| jbe/bsw@0 | 1410 | 
| jbe/bsw@0 | 1411 --[[-- | 
| jbe/bsw@0 | 1412 t =              -- time represented by the string | 
| jbe/bsw@0 | 1413 atom.time:load( | 
| jbe/bsw@0 | 1414   string         -- string representing a time of day | 
| jbe/bsw@0 | 1415 ) | 
| jbe/bsw@0 | 1416 | 
| jbe/bsw@0 | 1417 This method returns a time represented by the given string. | 
| jbe/bsw@0 | 1418 | 
| jbe/bsw@0 | 1419 --]]-- | 
| jbe/bsw@0 | 1420 function time:load(str) | 
| jbe/bsw@0 | 1421   if str == "" then | 
| jbe/bsw@0 | 1422     return nil | 
| jbe/bsw@0 | 1423   else | 
| jbe/bsw@0 | 1424     local hour, minute, second = string.match( | 
| jbe/bsw@0 | 1425       str, | 
| jbe/bsw@0 | 1426       "^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$" | 
| jbe/bsw@0 | 1427     ) | 
| jbe/bsw@0 | 1428     if hour then | 
| jbe/bsw@0 | 1429       return time{ | 
| jbe/bsw@0 | 1430         hour   = tonumber(hour), | 
| jbe/bsw@0 | 1431         minute = tonumber(minute), | 
| jbe/bsw@0 | 1432         second = tonumber(second) | 
| jbe/bsw@0 | 1433       } | 
| jbe/bsw@0 | 1434     else | 
| jbe/bsw@0 | 1435       return time.invalid | 
| jbe/bsw@0 | 1436     end | 
| jbe/bsw@0 | 1437   end | 
| jbe/bsw@0 | 1438 end | 
| jbe/bsw@0 | 1439 --//-- | 
| jbe/bsw@0 | 1440 | 
| jbe/bsw@0 | 1441 function time:__tostring() | 
| jbe/bsw@0 | 1442   if self.invalid then | 
| jbe/bsw@0 | 1443     return "invalid_time" | 
| jbe/bsw@0 | 1444   else | 
| jbe/bsw@0 | 1445     return string.format( | 
| jbe/bsw@0 | 1446       "%02i:%02i:%02i", | 
| jbe/bsw@0 | 1447       self.hour, self.minute, self.second | 
| jbe/bsw@0 | 1448     ) | 
| jbe/bsw@0 | 1449   end | 
| jbe/bsw@0 | 1450 end | 
| jbe/bsw@0 | 1451 | 
| jbe/bsw@0 | 1452 function time.__eq(value1, value2) | 
| jbe/bsw@0 | 1453   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 1454     return false | 
| jbe/bsw@0 | 1455   else | 
| jbe/bsw@0 | 1456     return value1.dsec == value2.dsec | 
| jbe/bsw@0 | 1457   end | 
| jbe/bsw@0 | 1458 end | 
| jbe/bsw@0 | 1459 | 
| jbe/bsw@0 | 1460 function time.__lt(value1, value2) | 
| jbe/bsw@0 | 1461   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 1462     return false | 
| jbe/bsw@0 | 1463   else | 
| jbe/bsw@0 | 1464     return value1.dsec < value2.dsec | 
| jbe/bsw@0 | 1465   end | 
| jbe/bsw@0 | 1466 end | 
| jbe/bsw@0 | 1467 | 
| jbe/bsw@0 | 1468 function time.__le(value1, value2) | 
| jbe/bsw@0 | 1469   if value1.invalid or value2.invalid then | 
| jbe/bsw@0 | 1470     return false | 
| jbe/bsw@0 | 1471   else | 
| jbe/bsw@0 | 1472     return value1.dsec <= value2.dsec | 
| jbe/bsw@0 | 1473   end | 
| jbe/bsw@0 | 1474 end | 
| jbe/bsw@0 | 1475 | 
| jbe/bsw@0 | 1476 function time.__add(value1, value2) | 
| jbe/bsw@0 | 1477   if getmetatable(value1) == time then | 
| jbe/bsw@0 | 1478     if getmetatable(value2) == time then | 
| jbe/bsw@0 | 1479       error("Can not add two times.") | 
| jbe/bsw@0 | 1480     elseif type(value2) == "number" then | 
| jbe/bsw@0 | 1481       return time((value1.dsec + value2) % 86400) | 
| jbe/bsw@0 | 1482     else | 
| jbe/bsw@0 | 1483       error("Right operand of '+' operator has wrong type.") | 
| jbe/bsw@0 | 1484     end | 
| jbe/bsw@0 | 1485   elseif type(value1) == "number" then | 
| jbe/bsw@0 | 1486     if getmetatable(value2) == time then | 
| jbe/bsw@0 | 1487       return time((value1 + value2.dsec) % 86400) | 
| jbe/bsw@0 | 1488     else | 
| jbe/bsw@0 | 1489       error("Assertion failed") | 
| jbe/bsw@0 | 1490     end | 
| jbe/bsw@0 | 1491   else | 
| jbe/bsw@0 | 1492     error("Left operand of '+' operator has wrong type.") | 
| jbe/bsw@0 | 1493   end | 
| jbe/bsw@0 | 1494 end | 
| jbe/bsw@0 | 1495 | 
| jbe/bsw@0 | 1496 function time.__sub(value1, value2) | 
| jbe/bsw@0 | 1497   if not getmetatable(value1) == time then | 
| jbe/bsw@0 | 1498     error("Left operand of '-' operator has wrong type.") | 
| jbe/bsw@0 | 1499   end | 
| jbe/bsw@0 | 1500   if getmetatable(value2) == time then | 
| jbe/bsw@0 | 1501     return value1.dsec - value2.dsec  -- TODO: transform to interval | 
| jbe/bsw@0 | 1502   elseif type(value2) == "number" then | 
| jbe/bsw@0 | 1503     return time((value1.dsec - value2) % 86400) | 
| jbe/bsw@0 | 1504   else | 
| jbe/bsw@0 | 1505     error("Right operand of '-' operator has wrong type.") | 
| jbe/bsw@0 | 1506   end | 
| jbe/bsw@0 | 1507 end | 
| jbe/bsw@0 | 1508 | 
| jbe/bsw@0 | 1509 function time.__concat(value1, value2) | 
| jbe/bsw@0 | 1510   local mt1, mt2 = getmetatable(value1), getmetatable(value2) | 
| jbe/bsw@0 | 1511   if mt1 == date and mt2 == time then | 
| jbe/bsw@0 | 1512     return timestamp{ | 
| jbe/bsw@0 | 1513       year = value1.year, month = value1.month, day = value1.day, | 
| jbe/bsw@0 | 1514       hour = value2.hour, minute = value2.minute, second = value2.second | 
| jbe/bsw@0 | 1515     } | 
| jbe/bsw@0 | 1516   elseif mt1 == time and mt2 == date then | 
| jbe/bsw@0 | 1517     return timestamp{ | 
| jbe/bsw@0 | 1518       year = value2.year, month = value2.month, day = value2.day, | 
| jbe/bsw@0 | 1519       hour = value1.hour, minute = value1.minute, second = value1.second | 
| jbe/bsw@0 | 1520     } | 
| jbe/bsw@0 | 1521   elseif mt1 == time then | 
| jbe/bsw@0 | 1522     error("Right operand of '..' operator has wrong type.") | 
| jbe/bsw@0 | 1523   elseif mt2 == time then | 
| jbe/bsw@0 | 1524     error("Left operand of '..' operator has wrong type.") | 
| jbe/bsw@0 | 1525   else | 
| jbe/bsw@0 | 1526     error("Assertion failed") | 
| jbe/bsw@0 | 1527   end | 
| jbe/bsw@0 | 1528 end | 
| jbe/bsw@0 | 1529 |