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