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