Merge branch 'master' of https://github.com/Araq/Nim into patch-global-log-level
This commit is contained in:
commit
863dd62bc4
186 changed files with 5652 additions and 4330 deletions
|
|
@ -288,7 +288,7 @@ proc defaultOnProgressChanged*(total, progress: BiggestInt,
|
|||
result.complete()
|
||||
|
||||
proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
|
||||
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
||||
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
|
||||
## Downloads ``file`` and saves it to ``dest``.
|
||||
## The ``EvRetr`` event is passed to the specified ``handleEvent`` function
|
||||
## when the download is finished. The event's ``filename`` field will be equal
|
||||
|
|
@ -339,7 +339,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File,
|
|||
await countdownFut or sendFut
|
||||
|
||||
proc store*(ftp: AsyncFtpClient, file, dest: string,
|
||||
onProgressChanged = defaultOnProgressChanged) {.async.} =
|
||||
onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
|
||||
## Uploads ``file`` to ``dest`` on the remote FTP server. Usage of this
|
||||
## function asynchronously is recommended to view the progress of
|
||||
## the download.
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] =
|
|||
## yields all (key, value)-pairs of `c`. The yielded values can be modified.
|
||||
for x in leaves(c.root): yield (x.key, x.val)
|
||||
|
||||
proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
||||
proc allprefixedAux[T](c: CritBitTree[T], key: string; longestMatch: bool): Node[T] =
|
||||
var p = c.root
|
||||
var top = p
|
||||
if p != nil:
|
||||
|
|
@ -242,43 +242,51 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] =
|
|||
let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8
|
||||
p = p.child[dir]
|
||||
if q.byte < key.len: top = p
|
||||
for i in 0 .. <key.len:
|
||||
if p.key[i] != key[i]: return
|
||||
if not longestMatch:
|
||||
for i in 0 .. <key.len:
|
||||
if p.key[i] != key[i]: return
|
||||
result = top
|
||||
|
||||
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
||||
## yields all keys starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string;
|
||||
longestMatch=false): string =
|
||||
## yields all keys starting with `prefix`. If `longestMatch` is true,
|
||||
## the longest match is returned, it doesn't have to be a complete match then.
|
||||
let top = allprefixedAux(c, prefix, longestMatch)
|
||||
for x in leaves(top): yield x.key
|
||||
|
||||
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string =
|
||||
iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string;
|
||||
longestMatch=false): string =
|
||||
## yields all keys starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
let top = allprefixedAux(c, prefix, longestMatch)
|
||||
for x in leaves(top): yield x.key
|
||||
|
||||
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T =
|
||||
iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string;
|
||||
longestMatch=false): T =
|
||||
## yields all values of `c` starting with `prefix` of the
|
||||
## corresponding keys.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
let top = allprefixedAux(c, prefix, longestMatch)
|
||||
for x in leaves(top): yield x.val
|
||||
|
||||
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T =
|
||||
iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string;
|
||||
longestMatch=false): var T =
|
||||
## yields all values of `c` starting with `prefix` of the
|
||||
## corresponding keys. The values can be modified.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
let top = allprefixedAux(c, prefix, longestMatch)
|
||||
for x in leaves(top): yield x.val
|
||||
|
||||
iterator pairsWithPrefix*[T](c: CritBitTree[T],
|
||||
prefix: string): tuple[key: string, val: T] =
|
||||
prefix: string;
|
||||
longestMatch=false): tuple[key: string, val: T] =
|
||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
let top = allprefixedAux(c, prefix, longestMatch)
|
||||
for x in leaves(top): yield (x.key, x.val)
|
||||
|
||||
iterator mpairsWithPrefix*[T](c: var CritBitTree[T],
|
||||
prefix: string): tuple[key: string, val: var T] =
|
||||
prefix: string;
|
||||
longestMatch=false): tuple[key: string, val: var T] =
|
||||
## yields all (key, value)-pairs of `c` starting with `prefix`.
|
||||
## The yielded values can be modified.
|
||||
let top = allprefixedAux(c, prefix)
|
||||
let top = allprefixedAux(c, prefix, longestMatch)
|
||||
for x in leaves(top): yield (x.key, x.val)
|
||||
|
||||
proc `$`*[T](c: CritBitTree[T]): string =
|
||||
|
|
|
|||
|
|
@ -10,12 +10,9 @@
|
|||
## :Author: Alexander Mitchell-Robinson (Amrykid)
|
||||
##
|
||||
## This module implements operations for the built-in `seq`:idx: type which
|
||||
## were inspired by functional programming languages. If you are looking for
|
||||
## the typical `map` function which applies a function to every element in a
|
||||
## sequence, it already exists in the `system <system.html>`_ module in both
|
||||
## mutable and immutable styles.
|
||||
## were inspired by functional programming languages.
|
||||
##
|
||||
## Also, for functional style programming you may want to pass `anonymous procs
|
||||
## For functional style programming you may want to pass `anonymous procs
|
||||
## <manual.html#anonymous-procs>`_ to procs like ``filter`` to reduce typing.
|
||||
## Anonymous procs can use `the special do notation <manual.html#do-notation>`_
|
||||
## which is more convenient in certain situations.
|
||||
|
|
@ -471,7 +468,7 @@ template toSeq*(iter: expr): expr {.immediate.} =
|
|||
## if x mod 2 == 1:
|
||||
## result = true)
|
||||
## assert odd_numbers == @[1, 3, 5, 7, 9]
|
||||
|
||||
|
||||
when compiles(iter.len):
|
||||
var i = 0
|
||||
var result = newSeq[type(iter)](iter.len)
|
||||
|
|
|
|||
|
|
@ -887,7 +887,7 @@ proc mget*[A](t: CountTableRef[A], key: A): var int {.deprecated.} =
|
|||
result = t[][key]
|
||||
|
||||
proc getOrDefault*[A](t: CountTableRef[A], key: A): int =
|
||||
getOrDefaultImpl(t, key)
|
||||
result = t[].getOrDefault(key)
|
||||
|
||||
proc hasKey*[A](t: CountTableRef[A], key: A): bool =
|
||||
## returns true iff `key` is in the table `t`.
|
||||
|
|
@ -1028,3 +1028,15 @@ when isMainModule:
|
|||
assert(merged["foo"] == 5)
|
||||
assert(merged["bar"] == 3)
|
||||
assert(merged["baz"] == 14)
|
||||
|
||||
block:
|
||||
const testKey = "TESTKEY"
|
||||
let t: CountTableRef[string] = newCountTable[string]()
|
||||
|
||||
# Before, does not compile with error message:
|
||||
#test_counttable.nim(7, 43) template/generic instantiation from here
|
||||
#lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here
|
||||
#lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode
|
||||
doAssert 0 == t.getOrDefault(testKey)
|
||||
t.inc(testKey,3)
|
||||
doAssert 3 == t.getOrDefault(testKey)
|
||||
|
|
|
|||
103
lib/pure/db_common.nim
Normal file
103
lib/pure/db_common.nim
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Common datatypes and definitions for all ``db_*.nim`` (
|
||||
## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
|
||||
## and `db_sqlite <db_sqlite.html>`_) modules.
|
||||
|
||||
type
|
||||
DbError* = object of IOError ## exception that is raised if a database error occurs
|
||||
|
||||
SqlQuery* = distinct string ## an SQL query string
|
||||
|
||||
|
||||
DbEffect* = object of IOEffect ## effect that denotes a database operation
|
||||
ReadDbEffect* = object of DbEffect ## effect that denotes a read operation
|
||||
WriteDbEffect* = object of DbEffect ## effect that denotes a write operation
|
||||
|
||||
DbTypeKind* = enum ## a superset of datatypes that might be supported.
|
||||
dbUnknown, ## unknown datatype
|
||||
dbSerial, ## datatype used for primary auto-increment keys
|
||||
dbNull, ## datatype used for the NULL value
|
||||
dbBit, ## bit datatype
|
||||
dbBool, ## boolean datatype
|
||||
dbBlob, ## blob datatype
|
||||
dbFixedChar, ## string of fixed length
|
||||
dbVarchar, ## string datatype
|
||||
dbJson, ## JSON datatype
|
||||
dbXml, ## XML datatype
|
||||
dbInt, ## some integer type
|
||||
dbUInt, ## some unsigned integer type
|
||||
dbDecimal, ## decimal numbers (fixed-point number)
|
||||
dbFloat, ## some floating point type
|
||||
dbDate, ## a year-month-day description
|
||||
dbTime, ## HH:MM:SS information
|
||||
dbDatetime, ## year-month-day and HH:MM:SS information,
|
||||
## plus optional time or timezone information
|
||||
dbTimestamp, ## Timestamp values are stored as the number of seconds
|
||||
## since the epoch ('1970-01-01 00:00:00' UTC).
|
||||
dbTimeInterval, ## an interval [a,b] of times
|
||||
dbEnum, ## some enum
|
||||
dbSet, ## set of enum values
|
||||
dbArray, ## an array of values
|
||||
dbComposite, ## composite type (record, struct, etc)
|
||||
dbUrl, ## a URL
|
||||
dbUuid, ## a UUID
|
||||
dbInet, ## an IP address
|
||||
dbMacAddress, ## a MAC address
|
||||
dbGeometry, ## some geometric type
|
||||
dbPoint, ## Point on a plane (x,y)
|
||||
dbLine, ## Infinite line ((x1,y1),(x2,y2))
|
||||
dbLseg, ## Finite line segment ((x1,y1),(x2,y2))
|
||||
dbBox, ## Rectangular box ((x1,y1),(x2,y2))
|
||||
dbPath, ## Closed or open path (similar to polygon) ((x1,y1),...)
|
||||
dbPolygon, ## Polygon (similar to closed path) ((x1,y1),...)
|
||||
dbCircle, ## Circle <(x,y),r> (center point and radius)
|
||||
dbUser1, ## user definable datatype 1 (for unknown extensions)
|
||||
dbUser2, ## user definable datatype 2 (for unknown extensions)
|
||||
dbUser3, ## user definable datatype 3 (for unknown extensions)
|
||||
dbUser4, ## user definable datatype 4 (for unknown extensions)
|
||||
dbUser5 ## user definable datatype 5 (for unknown extensions)
|
||||
|
||||
DbType* = object ## describes a database type
|
||||
kind*: DbTypeKind ## the kind of the described type
|
||||
notNull*: bool ## does the type contain NULL?
|
||||
name*: string ## the name of the type
|
||||
size*: Natural ## the size of the datatype; 0 if of variable size
|
||||
maxReprLen*: Natural ## maximal length required for the representation
|
||||
precision*, scale*: Natural ## precision and scale of the number
|
||||
min*, max*: BiggestInt ## the minimum and maximum of allowed values
|
||||
validValues*: seq[string] ## valid values of an enum or a set
|
||||
|
||||
DbColumn* = object ## information about a database column
|
||||
name*: string ## name of the column
|
||||
tableName*: string ## name of the table the column belongs to (optional)
|
||||
typ*: DbType ## type of the column
|
||||
primaryKey*: bool ## is this a primary key?
|
||||
foreignKey*: bool ## is this a foreign key?
|
||||
DbColumns* = seq[DbColumn]
|
||||
|
||||
{.deprecated: [EDb: DbError, TSqlQuery: SqlQuery, FDb: DbEffect,
|
||||
FReadDb: ReadDbEffect, FWriteDb: WriteDbEffect].}
|
||||
|
||||
template sql*(query: string): SqlQuery =
|
||||
## constructs a SqlQuery from the string `query`. This is supposed to be
|
||||
## used as a raw-string-literal modifier:
|
||||
## ``sql"update user set counter = counter + 1"``
|
||||
##
|
||||
## If assertions are turned off, it does nothing. If assertions are turned
|
||||
## on, later versions will check the string for valid syntax.
|
||||
SqlQuery(query)
|
||||
|
||||
proc dbError*(msg: string) {.noreturn, noinline.} =
|
||||
## raises an DbError exception with message `msg`.
|
||||
var e: ref DbError
|
||||
new(e)
|
||||
e.msg = msg
|
||||
raise e
|
||||
|
|
@ -57,7 +57,7 @@ proc addHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.})
|
|||
|
||||
proc removeHandler*(handler: var EventHandler, fn: proc(e: EventArgs) {.closure.}) =
|
||||
## Removes the callback from the specified event handler.
|
||||
for i in countup(0, len(handler.handlers) -1):
|
||||
for i in countup(0, len(handler.handlers)-1):
|
||||
if fn == handler.handlers[i]:
|
||||
handler.handlers.del(i)
|
||||
break
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
## This module allows you to monitor files or directories for changes using
|
||||
## asyncio.
|
||||
##
|
||||
## **Warning**: This module will likely disappear soon and be moved into a
|
||||
## new Nimble package.
|
||||
##
|
||||
## Windows support is not yet implemented.
|
||||
##
|
||||
## **Note:** This module uses ``inotify`` on Linux (Other Unixes are not yet
|
||||
|
|
@ -34,8 +37,8 @@ type
|
|||
MonitorEventType* = enum ## Monitor event type
|
||||
MonitorAccess, ## File was accessed.
|
||||
MonitorAttrib, ## Metadata changed.
|
||||
MonitorCloseWrite, ## Writtable file was closed.
|
||||
MonitorCloseNoWrite, ## Unwrittable file closed.
|
||||
MonitorCloseWrite, ## Writable file was closed.
|
||||
MonitorCloseNoWrite, ## Non-writable file closed.
|
||||
MonitorCreate, ## Subfile was created.
|
||||
MonitorDelete, ## Subfile was deleted.
|
||||
MonitorDeleteSelf, ## Watched file/directory was itself deleted.
|
||||
|
|
@ -78,21 +81,21 @@ proc add*(monitor: FSMonitor, target: string,
|
|||
## watched paths of ``monitor``.
|
||||
## You can specify the events to report using the ``filters`` parameter.
|
||||
|
||||
var INFilter = -1
|
||||
var INFilter = 0
|
||||
for f in filters:
|
||||
case f
|
||||
of MonitorAccess: INFilter = INFilter and IN_ACCESS
|
||||
of MonitorAttrib: INFilter = INFilter and IN_ATTRIB
|
||||
of MonitorCloseWrite: INFilter = INFilter and IN_CLOSE_WRITE
|
||||
of MonitorCloseNoWrite: INFilter = INFilter and IN_CLOSE_NO_WRITE
|
||||
of MonitorCreate: INFilter = INFilter and IN_CREATE
|
||||
of MonitorDelete: INFilter = INFilter and IN_DELETE
|
||||
of MonitorDeleteSelf: INFilter = INFilter and IN_DELETE_SELF
|
||||
of MonitorModify: INFilter = INFilter and IN_MODIFY
|
||||
of MonitorMoveSelf: INFilter = INFilter and IN_MOVE_SELF
|
||||
of MonitorMoved: INFilter = INFilter and IN_MOVED_FROM and IN_MOVED_TO
|
||||
of MonitorOpen: INFilter = INFilter and IN_OPEN
|
||||
of MonitorAll: INFilter = INFilter and IN_ALL_EVENTS
|
||||
of MonitorAccess: INFilter = INFilter or IN_ACCESS
|
||||
of MonitorAttrib: INFilter = INFilter or IN_ATTRIB
|
||||
of MonitorCloseWrite: INFilter = INFilter or IN_CLOSE_WRITE
|
||||
of MonitorCloseNoWrite: INFilter = INFilter or IN_CLOSE_NO_WRITE
|
||||
of MonitorCreate: INFilter = INFilter or IN_CREATE
|
||||
of MonitorDelete: INFilter = INFilter or IN_DELETE
|
||||
of MonitorDeleteSelf: INFilter = INFilter or IN_DELETE_SELF
|
||||
of MonitorModify: INFilter = INFilter or IN_MODIFY
|
||||
of MonitorMoveSelf: INFilter = INFilter or IN_MOVE_SELF
|
||||
of MonitorMoved: INFilter = INFilter or IN_MOVED_FROM or IN_MOVED_TO
|
||||
of MonitorOpen: INFilter = INFilter or IN_OPEN
|
||||
of MonitorAll: INFilter = INFilter or IN_ALL_EVENTS
|
||||
|
||||
result = inotifyAddWatch(monitor.fd, target, INFilter.uint32)
|
||||
if result < 0:
|
||||
|
|
@ -200,9 +203,18 @@ proc register*(d: Dispatcher, monitor: FSMonitor,
|
|||
|
||||
when not defined(testing) and isMainModule:
|
||||
proc main =
|
||||
var disp = newDispatcher()
|
||||
var monitor = newMonitor()
|
||||
echo monitor.add("/home/dom/inotifytests/")
|
||||
var
|
||||
disp = newDispatcher()
|
||||
monitor = newMonitor()
|
||||
n = 0
|
||||
n = monitor.add("/tmp")
|
||||
assert n == 1
|
||||
n = monitor.add("/tmp", {MonitorAll})
|
||||
assert n == 1
|
||||
n = monitor.add("/tmp", {MonitorCloseWrite, MonitorCloseNoWrite})
|
||||
assert n == 1
|
||||
n = monitor.add("/tmp", {MonitorMoved, MonitorOpen, MonitorAccess})
|
||||
assert n == 1
|
||||
disp.register(monitor,
|
||||
proc (m: FSMonitor, ev: MonitorEvent) =
|
||||
echo("Got event: ", ev.kind)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
## key-value mapping. The keys are required to be strings, but the values
|
||||
## may be any Nim or user defined type. This module supports matching
|
||||
## of keys in case-sensitive, case-insensitive and style-insensitive modes.
|
||||
##
|
||||
## **Warning:** This module is deprecated, new code shouldn't use it!
|
||||
|
||||
{.deprecated.}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ type
|
|||
EInvalidProtocol: ProtocolError, EHttpRequestErr: HttpRequestError
|
||||
].}
|
||||
|
||||
const defUserAgent* = "Nim httpclient/0.1"
|
||||
const defUserAgent* = "Nim httpclient/" & NimVersion
|
||||
|
||||
proc httpError(msg: string) =
|
||||
var e: ref ProtocolError
|
||||
|
|
@ -389,6 +389,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
|||
## | An optional timeout can be specified in milliseconds, if reading from the
|
||||
## server takes longer than specified an ETimeout exception will be raised.
|
||||
var r = if proxy == nil: parseUri(url) else: proxy.url
|
||||
var hostUrl = if proxy == nil: r else: parseUri(url)
|
||||
var headers = substr(httpMethod, len("http"))
|
||||
# TODO: Use generateHeaders further down once it supports proxies.
|
||||
if proxy == nil:
|
||||
|
|
@ -402,10 +403,10 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
|||
|
||||
headers.add(" HTTP/1.1\c\L")
|
||||
|
||||
if r.port == "":
|
||||
add(headers, "Host: " & r.hostname & "\c\L")
|
||||
if hostUrl.port == "":
|
||||
add(headers, "Host: " & hostUrl.hostname & "\c\L")
|
||||
else:
|
||||
add(headers, "Host: " & r.hostname & ":" & r.port & "\c\L")
|
||||
add(headers, "Host: " & hostUrl.hostname & ":" & hostUrl.port & "\c\L")
|
||||
|
||||
if userAgent != "":
|
||||
add(headers, "User-Agent: " & userAgent & "\c\L")
|
||||
|
|
@ -414,7 +415,6 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
|
|||
add(headers, "Proxy-Authorization: basic " & auth & "\c\L")
|
||||
add(headers, extraHeaders)
|
||||
add(headers, "\c\L")
|
||||
|
||||
var s = newSocket()
|
||||
if s == nil: raiseOSError(osLastError())
|
||||
var port = net.Port(80)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
## This module implements a simple HTTP-Server.
|
||||
##
|
||||
## **Warning**: This module will soon be deprecated in favour of
|
||||
## the ``asyncdispatch`` module, you should use it instead.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ type
|
|||
BaseLexer* = object of RootObj ## the base lexer. Inherit your lexer from
|
||||
## this object.
|
||||
bufpos*: int ## the current position within the buffer
|
||||
buf*: cstring ## the buffer itself
|
||||
when defined(js): ## the buffer itself
|
||||
buf*: string
|
||||
else:
|
||||
buf*: cstring
|
||||
bufLen*: int ## length of buffer in characters
|
||||
input: Stream ## the input stream
|
||||
lineNumber*: int ## the current line number
|
||||
|
|
@ -43,7 +46,8 @@ const
|
|||
|
||||
proc close*(L: var BaseLexer) =
|
||||
## closes the base lexer. This closes `L`'s associated stream too.
|
||||
dealloc(L.buf)
|
||||
when not defined(js):
|
||||
dealloc(L.buf)
|
||||
close(L.input)
|
||||
|
||||
proc fillBuffer(L: var BaseLexer) =
|
||||
|
|
@ -58,8 +62,11 @@ proc fillBuffer(L: var BaseLexer) =
|
|||
toCopy = L.bufLen - L.sentinel - 1
|
||||
assert(toCopy >= 0)
|
||||
if toCopy > 0:
|
||||
moveMem(L.buf, addr(L.buf[L.sentinel + 1]), toCopy * chrSize)
|
||||
# "moveMem" handles overlapping regions
|
||||
when defined(js):
|
||||
for i in 0 ..< toCopy: L.buf[i] = L.buf[L.sentinel + 1 + i]
|
||||
else:
|
||||
# "moveMem" handles overlapping regions
|
||||
moveMem(L.buf, addr L.buf[L.sentinel + 1], toCopy * chrSize)
|
||||
charsRead = readData(L.input, addr(L.buf[toCopy]),
|
||||
(L.sentinel + 1) * chrSize) div chrSize
|
||||
s = toCopy + charsRead
|
||||
|
|
@ -81,7 +88,10 @@ proc fillBuffer(L: var BaseLexer) =
|
|||
# double the buffer's size and try again:
|
||||
oldBufLen = L.bufLen
|
||||
L.bufLen = L.bufLen * 2
|
||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||
when defined(js):
|
||||
L.buf.setLen(L.bufLen)
|
||||
else:
|
||||
L.buf = cast[cstring](realloc(L.buf, L.bufLen * chrSize))
|
||||
assert(L.bufLen - oldBufLen == oldBufLen)
|
||||
charsRead = readData(L.input, addr(L.buf[oldBufLen]),
|
||||
oldBufLen * chrSize) div chrSize
|
||||
|
|
@ -139,7 +149,10 @@ proc open*(L: var BaseLexer, input: Stream, bufLen: int = 8192;
|
|||
L.bufpos = 0
|
||||
L.bufLen = bufLen
|
||||
L.refillChars = refillChars
|
||||
L.buf = cast[cstring](alloc(bufLen * chrSize))
|
||||
when defined(js):
|
||||
L.buf = newString(bufLen)
|
||||
else:
|
||||
L.buf = cast[cstring](alloc(bufLen * chrSize))
|
||||
L.sentinel = bufLen - 1
|
||||
L.lineStart = 0
|
||||
L.lineNumber = 1 # lines start at 1
|
||||
|
|
|
|||
|
|
@ -118,26 +118,6 @@ proc sum*[T](x: openArray[T]): T {.noSideEffect.} =
|
|||
## If `x` is empty, 0 is returned.
|
||||
for i in items(x): result = result + i
|
||||
|
||||
template toFloat(f: float): float = f
|
||||
|
||||
proc mean*[T](x: openArray[T]): float {.noSideEffect.} =
|
||||
## Computes the mean of the elements in `x`, which are first converted to floats.
|
||||
## If `x` is empty, NaN is returned.
|
||||
## ``toFloat(x: T): float`` must be defined.
|
||||
for i in items(x): result = result + toFloat(i)
|
||||
result = result / toFloat(len(x))
|
||||
|
||||
proc variance*[T](x: openArray[T]): float {.noSideEffect.} =
|
||||
## Computes the variance of the elements in `x`.
|
||||
## If `x` is empty, NaN is returned.
|
||||
## ``toFloat(x: T): float`` must be defined.
|
||||
result = 0.0
|
||||
var m = mean(x)
|
||||
for i in items(x):
|
||||
var diff = toFloat(i) - m
|
||||
result = result + diff*diff
|
||||
result = result / toFloat(len(x))
|
||||
|
||||
proc random*(max: int): int {.benign.}
|
||||
## Returns a random number in the range 0..max-1. The sequence of
|
||||
## random number is always the same, unless `randomize` is called
|
||||
|
|
@ -376,48 +356,6 @@ proc random*[T](a: openArray[T]): T =
|
|||
## returns a random element from the openarray `a`.
|
||||
result = a[random(a.low..a.len)]
|
||||
|
||||
type
|
||||
RunningStat* = object ## an accumulator for statistical data
|
||||
n*: int ## number of pushed data
|
||||
sum*, min*, max*, mean*: float ## self-explaining
|
||||
oldM, oldS, newS: float
|
||||
|
||||
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
|
||||
|
||||
proc push*(s: var RunningStat, x: float) =
|
||||
## pushes a value `x` for processing
|
||||
inc(s.n)
|
||||
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
||||
if s.n == 1:
|
||||
s.min = x
|
||||
s.max = x
|
||||
s.oldM = x
|
||||
s.mean = x
|
||||
s.oldS = 0.0
|
||||
else:
|
||||
if s.min > x: s.min = x
|
||||
if s.max < x: s.max = x
|
||||
s.mean = s.oldM + (x - s.oldM)/toFloat(s.n)
|
||||
s.newS = s.oldS + (x - s.oldM)*(x - s.mean)
|
||||
|
||||
# set up for next iteration:
|
||||
s.oldM = s.mean
|
||||
s.oldS = s.newS
|
||||
s.sum = s.sum + x
|
||||
|
||||
proc push*(s: var RunningStat, x: int) =
|
||||
## pushes a value `x` for processing. `x` is simply converted to ``float``
|
||||
## and the other push operation is called.
|
||||
push(s, toFloat(x))
|
||||
|
||||
proc variance*(s: RunningStat): float =
|
||||
## computes the current variance of `s`
|
||||
if s.n > 1: result = s.newS / (toFloat(s.n - 1))
|
||||
|
||||
proc standardDeviation*(s: RunningStat): float =
|
||||
## computes the current standard deviation of `s`
|
||||
result = sqrt(variance(s))
|
||||
|
||||
{.pop.}
|
||||
{.pop.}
|
||||
|
||||
|
|
|
|||
|
|
@ -203,9 +203,12 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
|
|||
hints.ai_family = toInt(domain)
|
||||
hints.ai_socktype = toInt(sockType)
|
||||
hints.ai_protocol = toInt(protocol)
|
||||
# OpenBSD doesn't support AI_V4MAPPED and doesn't define the macro AI_V4MAPPED.
|
||||
# FreeBSD doesn't support AI_V4MAPPED but defines the macro.
|
||||
# https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198092
|
||||
when not defined(freebsd):
|
||||
hints.ai_flags = AI_V4MAPPED
|
||||
when not defined(freebsd) and not defined(openbsd) and not defined(netbsd):
|
||||
if domain == AF_INET6:
|
||||
hints.ai_flags = AI_V4MAPPED
|
||||
var gaiResult = getaddrinfo(address, $port, addr(hints), result)
|
||||
if gaiResult != 0'i32:
|
||||
when useWinVersion:
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
|
|
@ -117,24 +117,38 @@ when defined(memProfiler):
|
|||
var
|
||||
gTicker {.threadvar.}: int
|
||||
|
||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||
proc requestedHook(): bool {.nimcall.} =
|
||||
if gTicker == 0:
|
||||
gTicker = -1
|
||||
when defined(ignoreAllocationSize):
|
||||
hookAux(st, 1)
|
||||
else:
|
||||
hookAux(st, size)
|
||||
gTicker = SamplingInterval
|
||||
result = true
|
||||
dec gTicker
|
||||
|
||||
proc hook(st: StackTrace, size: int) {.nimcall.} =
|
||||
when defined(ignoreAllocationSize):
|
||||
hookAux(st, 1)
|
||||
else:
|
||||
hookAux(st, size)
|
||||
|
||||
else:
|
||||
var
|
||||
t0 {.threadvar.}: Ticks
|
||||
gTicker: int # we use an additional counter to
|
||||
# avoid calling 'getTicks' too frequently
|
||||
|
||||
proc requestedHook(): bool {.nimcall.} =
|
||||
if interval == 0: result = true
|
||||
elif gTicker == 0:
|
||||
gTicker = 500
|
||||
if getTicks() - t0 > interval:
|
||||
result = true
|
||||
else:
|
||||
dec gTicker
|
||||
|
||||
proc hook(st: StackTrace) {.nimcall.} =
|
||||
#echo "profiling! ", interval
|
||||
if interval == 0:
|
||||
hookAux(st, 1)
|
||||
elif int64(t0) == 0 or getTicks() - t0 > interval:
|
||||
else:
|
||||
hookAux(st, 1)
|
||||
t0 = getTicks()
|
||||
|
||||
|
|
@ -145,9 +159,10 @@ proc cmpEntries(a, b: ptr ProfileEntry): int =
|
|||
result = b.getTotal - a.getTotal
|
||||
|
||||
proc `//`(a, b: int): string =
|
||||
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDefault, 2))
|
||||
result = format("$1/$2 = $3%", a, b, formatFloat(a / b * 100.0, ffDecimal, 2))
|
||||
|
||||
proc writeProfile() {.noconv.} =
|
||||
system.profilingRequestedHook = nil
|
||||
when declared(system.StackTrace):
|
||||
system.profilerHook = nil
|
||||
const filename = "profile_results.txt"
|
||||
|
|
@ -193,14 +208,15 @@ var
|
|||
proc disableProfiling*() =
|
||||
when declared(system.StackTrace):
|
||||
atomicDec disabled
|
||||
system.profilerHook = nil
|
||||
system.profilingRequestedHook = nil
|
||||
|
||||
proc enableProfiling*() =
|
||||
when declared(system.StackTrace):
|
||||
if atomicInc(disabled) >= 0:
|
||||
system.profilerHook = hook
|
||||
system.profilingRequestedHook = requestedHook
|
||||
|
||||
when declared(system.StackTrace):
|
||||
system.profilingRequestedHook = requestedHook
|
||||
system.profilerHook = hook
|
||||
addQuitProc(writeProfile)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## **Warning:** This module will be moved out of the stdlib and into a
|
||||
## Nimble package, don't use it.
|
||||
|
||||
type OneVarFunction* = proc (x: float): float
|
||||
|
||||
{.deprecated: [TOneVarFunction: OneVarFunction].}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## import optionals
|
||||
## import options
|
||||
##
|
||||
## proc find(haystack: string, needle: char): Option[int] =
|
||||
## for i, c in haystack:
|
||||
|
|
@ -156,7 +156,7 @@ proc `$`*[T]( self: Option[T] ): string =
|
|||
when isMainModule:
|
||||
import unittest, sequtils
|
||||
|
||||
suite "optionals":
|
||||
suite "options":
|
||||
# work around a bug in unittest
|
||||
let intNone = none(int)
|
||||
let stringNone = none(string)
|
||||
|
|
|
|||
|
|
@ -810,6 +810,10 @@ type
|
|||
|
||||
{.deprecated: [TPathComponent: PathComponent].}
|
||||
|
||||
proc staticWalkDir(dir: string; relative: bool): seq[
|
||||
tuple[kind: PathComponent, path: string]] =
|
||||
discard
|
||||
|
||||
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {.
|
||||
tags: [ReadDirEffect].} =
|
||||
## walks over the directory `dir` and yields for each directory or file in
|
||||
|
|
@ -833,49 +837,53 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path:
|
|||
## dirA/dirC
|
||||
## dirA/fileA1.txt
|
||||
## dirA/fileA2.txt
|
||||
when defined(windows):
|
||||
var f: WIN32_FIND_DATA
|
||||
var h = findFirstFile(dir / "*", f)
|
||||
if h != -1:
|
||||
while true:
|
||||
var k = pcFile
|
||||
if not skipFindData(f):
|
||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
|
||||
k = pcDir
|
||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
||||
k = succ(k)
|
||||
let xx = if relative: extractFilename(getFilename(f))
|
||||
else: dir / extractFilename(getFilename(f))
|
||||
yield (k, xx)
|
||||
if findNextFile(h, f) == 0'i32: break
|
||||
findClose(h)
|
||||
when nimvm:
|
||||
for k, v in items(staticWalkDir(dir, relative)):
|
||||
yield (k, v)
|
||||
else:
|
||||
var d = opendir(dir)
|
||||
if d != nil:
|
||||
while true:
|
||||
var x = readdir(d)
|
||||
if x == nil: break
|
||||
var y = $x.d_name
|
||||
if y != "." and y != "..":
|
||||
var s: Stat
|
||||
if not relative:
|
||||
y = dir / y
|
||||
when defined(windows):
|
||||
var f: WIN32_FIND_DATA
|
||||
var h = findFirstFile(dir / "*", f)
|
||||
if h != -1:
|
||||
while true:
|
||||
var k = pcFile
|
||||
if not skipFindData(f):
|
||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
|
||||
k = pcDir
|
||||
if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
|
||||
k = succ(k)
|
||||
let xx = if relative: extractFilename(getFilename(f))
|
||||
else: dir / extractFilename(getFilename(f))
|
||||
yield (k, xx)
|
||||
if findNextFile(h, f) == 0'i32: break
|
||||
findClose(h)
|
||||
else:
|
||||
var d = opendir(dir)
|
||||
if d != nil:
|
||||
while true:
|
||||
var x = readdir(d)
|
||||
if x == nil: break
|
||||
var y = $x.d_name
|
||||
if y != "." and y != "..":
|
||||
var s: Stat
|
||||
if not relative:
|
||||
y = dir / y
|
||||
var k = pcFile
|
||||
|
||||
when defined(linux) or defined(macosx) or defined(bsd):
|
||||
if x.d_type != DT_UNKNOWN:
|
||||
if x.d_type == DT_DIR: k = pcDir
|
||||
if x.d_type == DT_LNK:
|
||||
if dirExists(y): k = pcLinkToDir
|
||||
else: k = succ(k)
|
||||
yield (k, y)
|
||||
continue
|
||||
when defined(linux) or defined(macosx) or defined(bsd):
|
||||
if x.d_type != DT_UNKNOWN:
|
||||
if x.d_type == DT_DIR: k = pcDir
|
||||
if x.d_type == DT_LNK:
|
||||
if dirExists(y): k = pcLinkToDir
|
||||
else: k = succ(k)
|
||||
yield (k, y)
|
||||
continue
|
||||
|
||||
if lstat(y, s) < 0'i32: break
|
||||
if S_ISDIR(s.st_mode): k = pcDir
|
||||
if S_ISLNK(s.st_mode): k = succ(k)
|
||||
yield (k, y)
|
||||
discard closedir(d)
|
||||
if lstat(y, s) < 0'i32: break
|
||||
if S_ISDIR(s.st_mode): k = pcDir
|
||||
if S_ISLNK(s.st_mode): k = succ(k)
|
||||
yield (k, y)
|
||||
discard closedir(d)
|
||||
|
||||
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string {.
|
||||
tags: [ReadDirEffect].} =
|
||||
|
|
@ -1353,7 +1361,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
|
|||
# /proc/<pid>/file
|
||||
when defined(windows):
|
||||
when useWinUnicode:
|
||||
var buf = cast[WideCString](alloc(256*2))
|
||||
var buf = newWideCString("", 256)
|
||||
var len = getModuleFileNameW(0, buf, 256)
|
||||
result = buf$len
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -886,7 +886,7 @@ elif not defined(useNimRtl):
|
|||
discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
|
||||
exitnow(1)
|
||||
|
||||
when defined(macosx) or defined(freebsd):
|
||||
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
|
||||
var environ {.importc.}: cstringArray
|
||||
|
||||
proc startProcessAfterFork(data: ptr StartProcessData) =
|
||||
|
|
@ -916,7 +916,7 @@ elif not defined(useNimRtl):
|
|||
discard fcntl(data.pErrorPipe[writeIdx], F_SETFD, FD_CLOEXEC)
|
||||
|
||||
if data.optionPoUsePath:
|
||||
when defined(macosx) or defined(freebsd):
|
||||
when defined(macosx) or defined(freebsd) or defined(netbsd) or defined(android):
|
||||
# MacOSX doesn't have execvpe, so we need workaround.
|
||||
# On MacOSX we can arrive here only from fork, so this is safe:
|
||||
environ = data.sysEnv
|
||||
|
|
@ -937,9 +937,10 @@ elif not defined(useNimRtl):
|
|||
if p.inStream != nil: close(p.inStream)
|
||||
if p.outStream != nil: close(p.outStream)
|
||||
if p.errStream != nil: close(p.errStream)
|
||||
discard close(p.inHandle)
|
||||
discard close(p.outHandle)
|
||||
discard close(p.errHandle)
|
||||
if poParentStreams notin p.options:
|
||||
discard close(p.inHandle)
|
||||
discard close(p.outHandle)
|
||||
discard close(p.errHandle)
|
||||
|
||||
proc suspend(p: Process) =
|
||||
if kill(p.id, SIGSTOP) != 0'i32: raiseOsError(osLastError())
|
||||
|
|
|
|||
27
lib/pure/oswalkdir.nim
Normal file
27
lib/pure/oswalkdir.nim
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
## Compile-time only version for walkDir if you need it at compile-time
|
||||
## for JavaScript.
|
||||
|
||||
type
|
||||
PathComponent* = enum ## Enumeration specifying a path component.
|
||||
pcFile, ## path refers to a file
|
||||
pcLinkToFile, ## path refers to a symbolic link to a file
|
||||
pcDir, ## path refers to a directory
|
||||
pcLinkToDir ## path refers to a symbolic link to a directory
|
||||
|
||||
proc staticWalkDir(dir: string; relative: bool): seq[
|
||||
tuple[kind: PathComponent, path: string]] =
|
||||
discard
|
||||
|
||||
iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] =
|
||||
for k, v in items(staticWalkDir(dir, relative)):
|
||||
yield (k, v)
|
||||
|
||||
iterator walkDirRec*(dir: string, filter={pcFile, pcDir}): string =
|
||||
var stack = @[dir]
|
||||
while stack.len > 0:
|
||||
for k,p in walkDir(stack.pop()):
|
||||
if k in filter:
|
||||
case k
|
||||
of pcFile, pcLinkToFile: yield p
|
||||
of pcDir, pcLinkToDir: stack.add(p)
|
||||
|
|
@ -70,7 +70,7 @@ when not defined(createNimRtl):
|
|||
## Initializes option parser from current command line arguments.
|
||||
return initOptParser(commandLineParams())
|
||||
|
||||
proc next*(p: var OptParser) {.rtl, extern: "npo$1".}
|
||||
proc next*(p: var OptParser) {.rtl, extern: "npo2$1".}
|
||||
|
||||
proc nextOption(p: var OptParser, token: string, allowEmpty: bool) =
|
||||
for splitchar in [':', '=']:
|
||||
|
|
@ -113,7 +113,7 @@ proc next(p: var OptParser) =
|
|||
p.key = token
|
||||
p.val = ""
|
||||
|
||||
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1", deprecated.} =
|
||||
proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo2$1", deprecated.} =
|
||||
## Returns part of command line string that has not been parsed yet.
|
||||
## Do not use - does not correctly handle whitespace.
|
||||
return p.cmd[p.pos..p.cmd.len-1].join(" ")
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const
|
|||
proc toLower(c: char): char {.inline.} =
|
||||
result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
|
||||
|
||||
proc parseHex*(s: string, number: var int, start = 0): int {.
|
||||
proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {.
|
||||
rtl, extern: "npuParseHex", noSideEffect.} =
|
||||
## Parses a hexadecimal number and stores its value in ``number``.
|
||||
##
|
||||
|
|
@ -45,11 +45,14 @@ proc parseHex*(s: string, number: var int, start = 0): int {.
|
|||
## discard parseHex("0x38", value)
|
||||
## assert value == -200
|
||||
##
|
||||
## If 'maxLen==0' the length of the hexadecimal number has no
|
||||
## upper bound. Not more than ```maxLen`` characters are parsed.
|
||||
var i = start
|
||||
var foundDigit = false
|
||||
if s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2)
|
||||
elif s[i] == '#': inc(i)
|
||||
while true:
|
||||
let last = if maxLen == 0: s.len else: i+maxLen
|
||||
while i < last:
|
||||
case s[i]
|
||||
of '_': discard
|
||||
of '0'..'9':
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## **Warning:** This module will be moved out of the stdlib and into a
|
||||
## Nimble package, don't use it.
|
||||
|
||||
import math
|
||||
import strutils
|
||||
import numeric
|
||||
|
|
|
|||
1096
lib/pure/redis.nim
1096
lib/pure/redis.nim
File diff suppressed because it is too large
Load diff
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
## Module for converting an integer to a Roman numeral.
|
||||
## See http://en.wikipedia.org/wiki/Roman_numerals for reference.
|
||||
##
|
||||
## **Warning:** This module will be moved out of the stdlib and into a
|
||||
## Nimble package, don't use it.
|
||||
|
||||
const
|
||||
RomanNumeralDigits* = {'I', 'i', 'V', 'v', 'X', 'x', 'L', 'l', 'C', 'c',
|
||||
|
|
|
|||
348
lib/pure/stats.nim
Normal file
348
lib/pure/stats.nim
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
#
|
||||
#
|
||||
# Nim's Runtime Library
|
||||
# (c) Copyright 2015 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
## Statistical analysis framework for performing
|
||||
## basic statistical analysis of data.
|
||||
## The data is analysed in a single pass, when a data value
|
||||
## is pushed to the ``RunningStat`` or ``RunningRegress`` objects
|
||||
##
|
||||
## ``RunningStat`` calculates for a single data set
|
||||
## - n (data count)
|
||||
## - min (smallest value)
|
||||
## - max (largest value)
|
||||
## - sum
|
||||
## - mean
|
||||
## - variance
|
||||
## - varianceS (sample var)
|
||||
## - standardDeviation
|
||||
## - standardDeviationS (sample stddev)
|
||||
## - skewness (the third statistical moment)
|
||||
## - kurtosis (the fourth statistical moment)
|
||||
##
|
||||
## ``RunningRegress`` calculates for two sets of data
|
||||
## - n
|
||||
## - slope
|
||||
## - intercept
|
||||
## - correlation
|
||||
##
|
||||
## Procs have been provided to calculate statistics on arrays and sequences.
|
||||
##
|
||||
## However, if more than a single statistical calculation is required, it is more
|
||||
## efficient to push the data once to the RunningStat object, and
|
||||
## call the numerous statistical procs for the RunningStat object.
|
||||
##
|
||||
## .. code-block:: Nim
|
||||
##
|
||||
## var rs: RunningStat
|
||||
## rs.push(MySeqOfData)
|
||||
## rs.mean()
|
||||
## rs.variance()
|
||||
## rs.skewness()
|
||||
## rs.kurtosis()
|
||||
|
||||
from math import FloatClass, sqrt, pow, round
|
||||
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
{.push checks:off, line_dir:off, stack_trace:off.}
|
||||
|
||||
type
|
||||
RunningStat* = object ## an accumulator for statistical data
|
||||
n*: int ## number of pushed data
|
||||
min*, max*, sum*: float ## self-explaining
|
||||
mom1, mom2, mom3, mom4: float ## statistical moments, mom1 is mean
|
||||
|
||||
|
||||
RunningRegress* = object ## an accumulator for regression calculations
|
||||
n*: int ## number of pushed data
|
||||
x_stats*: RunningStat ## stats for first set of data
|
||||
y_stats*: RunningStat ## stats for second set of data
|
||||
s_xy: float ## accumulated data for combined xy
|
||||
|
||||
{.deprecated: [TFloatClass: FloatClass, TRunningStat: RunningStat].}
|
||||
|
||||
# ----------- RunningStat --------------------------
|
||||
proc clear*(s: var RunningStat) =
|
||||
## reset `s`
|
||||
s.n = 0
|
||||
s.min = toBiggestFloat(int.high)
|
||||
s.max = 0.0
|
||||
s.sum = 0.0
|
||||
s.mom1 = 0.0
|
||||
s.mom2 = 0.0
|
||||
s.mom3 = 0.0
|
||||
s.mom4 = 0.0
|
||||
|
||||
proc push*(s: var RunningStat, x: float) =
|
||||
## pushes a value `x` for processing
|
||||
if s.n == 0: s.min = x
|
||||
inc(s.n)
|
||||
# See Knuth TAOCP vol 2, 3rd edition, page 232
|
||||
if s.min > x: s.min = x
|
||||
if s.max < x: s.max = x
|
||||
s.sum += x
|
||||
let n = toFloat(s.n)
|
||||
let delta = x - s.mom1
|
||||
let delta_n = delta / toFloat(s.n)
|
||||
let delta_n2 = delta_n * delta_n
|
||||
let term1 = delta * delta_n * toFloat(s.n - 1)
|
||||
s.mom4 += term1 * delta_n2 * (n*n - 3*n + 3) +
|
||||
6*delta_n2*s.mom2 - 4*delta_n*s.mom3
|
||||
s.mom3 += term1 * delta_n * (n - 2) - 3*delta_n*s.mom2
|
||||
s.mom2 += term1
|
||||
s.mom1 += delta_n
|
||||
|
||||
proc push*(s: var RunningStat, x: int) =
|
||||
## pushes a value `x` for processing.
|
||||
##
|
||||
## `x` is simply converted to ``float``
|
||||
## and the other push operation is called.
|
||||
s.push(toFloat(x))
|
||||
|
||||
proc push*(s: var RunningStat, x: openarray[float|int]) =
|
||||
## pushes all values of `x` for processing.
|
||||
##
|
||||
## Int values of `x` are simply converted to ``float`` and
|
||||
## the other push operation is called.
|
||||
for val in x:
|
||||
s.push(val)
|
||||
|
||||
proc mean*(s: RunningStat): float =
|
||||
## computes the current mean of `s`
|
||||
result = s.mom1
|
||||
|
||||
proc variance*(s: RunningStat): float =
|
||||
## computes the current population variance of `s`
|
||||
result = s.mom2 / toFloat(s.n)
|
||||
|
||||
proc varianceS*(s: RunningStat): float =
|
||||
## computes the current sample variance of `s`
|
||||
if s.n > 1: result = s.mom2 / toFloat(s.n - 1)
|
||||
|
||||
proc standardDeviation*(s: RunningStat): float =
|
||||
## computes the current population standard deviation of `s`
|
||||
result = sqrt(variance(s))
|
||||
|
||||
proc standardDeviationS*(s: RunningStat): float =
|
||||
## computes the current sample standard deviation of `s`
|
||||
result = sqrt(varianceS(s))
|
||||
|
||||
proc skewness*(s: RunningStat): float =
|
||||
## computes the current population skewness of `s`
|
||||
result = sqrt(toFloat(s.n)) * s.mom3 / pow(s.mom2, 1.5)
|
||||
|
||||
proc skewnessS*(s: RunningStat): float =
|
||||
## computes the current sample skewness of `s`
|
||||
let s2 = skewness(s)
|
||||
result = sqrt(toFloat(s.n*(s.n-1)))*s2 / toFloat(s.n-2)
|
||||
|
||||
proc kurtosis*(s: RunningStat): float =
|
||||
## computes the current population kurtosis of `s`
|
||||
result = toFloat(s.n) * s.mom4 / (s.mom2 * s.mom2) - 3.0
|
||||
|
||||
proc kurtosisS*(s: RunningStat): float =
|
||||
## computes the current sample kurtosis of `s`
|
||||
result = toFloat(s.n-1) / toFloat((s.n-2)*(s.n-3)) *
|
||||
(toFloat(s.n+1)*kurtosis(s) + 6)
|
||||
|
||||
proc `+`*(a, b: RunningStat): RunningStat =
|
||||
## combine two RunningStats.
|
||||
##
|
||||
## Useful if performing parallel analysis of data series
|
||||
## and need to re-combine parallel result sets
|
||||
result.clear()
|
||||
result.n = a.n + b.n
|
||||
|
||||
let delta = b.mom1 - a.mom1
|
||||
let delta2 = delta*delta
|
||||
let delta3 = delta*delta2
|
||||
let delta4 = delta2*delta2
|
||||
let n = toFloat(result.n)
|
||||
|
||||
result.mom1 = (a.n.float*a.mom1 + b.n.float*b.mom1) / n
|
||||
result.mom2 = a.mom2 + b.mom2 + delta2 * a.n.float * b.n.float / n
|
||||
result.mom3 = a.mom3 + b.mom3 +
|
||||
delta3 * a.n.float * b.n.float * (a.n.float - b.n.float)/(n*n);
|
||||
result.mom3 += 3.0*delta * (a.n.float*b.mom2 - b.n.float*a.mom2) / n
|
||||
result.mom4 = a.mom4 + b.mom4 +
|
||||
delta4*a.n.float*b.n.float * toFloat(a.n*a.n - a.n*b.n + b.n*b.n) /
|
||||
(n*n*n)
|
||||
result.mom4 += 6.0*delta2 * (a.n.float*a.n.float*b.mom2 + b.n.float*b.n.float*a.mom2) /
|
||||
(n*n) +
|
||||
4.0*delta*(a.n.float*b.mom3 - b.n.float*a.mom3) / n
|
||||
result.max = max(a.max, b.max)
|
||||
result.min = max(a.min, b.min)
|
||||
|
||||
proc `+=`*(a: var RunningStat, b: RunningStat) {.inline.} =
|
||||
## add a second RunningStats `b` to `a`
|
||||
a = a + b
|
||||
# ---------------------- standalone array/seq stats ---------------------
|
||||
proc mean*[T](x: openArray[T]): float =
|
||||
## computes the mean of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.mean()
|
||||
|
||||
proc variance*[T](x: openArray[T]): float =
|
||||
## computes the population variance of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.variance()
|
||||
|
||||
proc varianceS*[T](x: openArray[T]): float =
|
||||
## computes the sample variance of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.varianceS()
|
||||
|
||||
proc standardDeviation*[T](x: openArray[T]): float =
|
||||
## computes the population standardDeviation of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.standardDeviation()
|
||||
|
||||
proc standardDeviationS*[T](x: openArray[T]): float =
|
||||
## computes the sanple standardDeviation of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.standardDeviationS()
|
||||
|
||||
proc skewness*[T](x: openArray[T]): float =
|
||||
## computes the population skewness of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.skewness()
|
||||
|
||||
proc skewnessS*[T](x: openArray[T]): float =
|
||||
## computes the sample skewness of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.skewnessS()
|
||||
|
||||
proc kurtosis*[T](x: openArray[T]): float =
|
||||
## computes the population kurtosis of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.kurtosis()
|
||||
|
||||
proc kurtosisS*[T](x: openArray[T]): float =
|
||||
## computes the sample kurtosis of `x`
|
||||
var rs: RunningStat
|
||||
rs.push(x)
|
||||
result = rs.kurtosisS()
|
||||
|
||||
# ---------------------- Running Regression -----------------------------
|
||||
|
||||
proc clear*(r: var RunningRegress) =
|
||||
## reset `r`
|
||||
r.x_stats.clear()
|
||||
r.y_stats.clear()
|
||||
r.s_xy = 0.0
|
||||
r.n = 0
|
||||
|
||||
proc push*(r: var RunningRegress, x, y: float) =
|
||||
## pushes two values `x` and `y` for processing
|
||||
r.s_xy += (r.x_stats.mean() - x)*(r.y_stats.mean() - y)*
|
||||
toFloat(r.n) / toFloat(r.n + 1)
|
||||
r.x_stats.push(x)
|
||||
r.y_stats.push(y)
|
||||
inc(r.n)
|
||||
|
||||
proc push*(r: var RunningRegress, x, y: int) {.inline.} =
|
||||
## pushes two values `x` and `y` for processing.
|
||||
##
|
||||
## `x` and `y` are converted to ``float``
|
||||
## and the other push operation is called.
|
||||
r.push(toFloat(x), toFloat(y))
|
||||
|
||||
proc push*(r: var RunningRegress, x, y: openarray[float|int]) =
|
||||
## pushes two sets of values `x` and `y` for processing.
|
||||
assert(x.len == y.len)
|
||||
for i in 0..<x.len:
|
||||
r.push(x[i], y[i])
|
||||
|
||||
proc slope*(r: RunningRegress): float =
|
||||
## computes the current slope of `r`
|
||||
let s_xx = r.x_stats.varianceS()*toFloat(r.n - 1)
|
||||
result = r.s_xy / s_xx
|
||||
|
||||
proc intercept*(r: RunningRegress): float =
|
||||
## computes the current intercept of `r`
|
||||
result = r.y_stats.mean() - r.slope()*r.x_stats.mean()
|
||||
|
||||
proc correlation*(r: RunningRegress): float =
|
||||
## computes the current correlation of the two data
|
||||
## sets pushed into `r`
|
||||
let t = r.x_stats.standardDeviation() * r.y_stats.standardDeviation()
|
||||
result = r.s_xy / ( toFloat(r.n) * t )
|
||||
|
||||
proc `+`*(a, b: RunningRegress): RunningRegress =
|
||||
## combine two `RunningRegress` objects.
|
||||
##
|
||||
## Useful if performing parallel analysis of data series
|
||||
## and need to re-combine parallel result sets
|
||||
result.clear()
|
||||
result.x_stats = a.x_stats + b.x_stats
|
||||
result.y_stats = a.y_stats + b.y_stats
|
||||
result.n = a.n + b.n
|
||||
|
||||
let delta_x = b.x_stats.mean() - a.x_stats.mean()
|
||||
let delta_y = b.y_stats.mean() - a.y_stats.mean()
|
||||
result.s_xy = a.s_xy + b.s_xy +
|
||||
toFloat(a.n*b.n)*delta_x*delta_y/toFloat(result.n)
|
||||
|
||||
proc `+=`*(a: var RunningRegress, b: RunningRegress) =
|
||||
## add RunningRegress `b` to `a`
|
||||
a = a + b
|
||||
|
||||
{.pop.}
|
||||
{.pop.}
|
||||
|
||||
when isMainModule:
|
||||
proc clean(x: float): float =
|
||||
result = round(1.0e8*x).float * 1.0e-8
|
||||
|
||||
var rs: RunningStat
|
||||
rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0])
|
||||
doAssert(rs.n == 8)
|
||||
doAssert(clean(rs.mean) == 2.0)
|
||||
doAssert(clean(rs.variance()) == 1.5)
|
||||
doAssert(clean(rs.varianceS()) == 1.71428571)
|
||||
doAssert(clean(rs.skewness()) == 0.81649658)
|
||||
doAssert(clean(rs.skewnessS()) == 1.01835015)
|
||||
doAssert(clean(rs.kurtosis()) == -1.0)
|
||||
doAssert(clean(rs.kurtosisS()) == -0.7000000000000001)
|
||||
|
||||
var rs1, rs2: RunningStat
|
||||
rs1.push(@[1.0, 2.0, 1.0, 4.0])
|
||||
rs2.push(@[1.0, 4.0, 1.0, 2.0])
|
||||
let rs3 = rs1 + rs2
|
||||
doAssert(clean(rs3.mom2) == clean(rs.mom2))
|
||||
doAssert(clean(rs3.mom3) == clean(rs.mom3))
|
||||
doAssert(clean(rs3.mom4) == clean(rs.mom4))
|
||||
rs1 += rs2
|
||||
doAssert(clean(rs1.mom2) == clean(rs.mom2))
|
||||
doAssert(clean(rs1.mom3) == clean(rs.mom3))
|
||||
doAssert(clean(rs1.mom4) == clean(rs.mom4))
|
||||
rs1.clear()
|
||||
rs1.push(@[1.0, 2.2, 1.4, 4.9])
|
||||
doAssert(rs1.sum == 9.5)
|
||||
doAssert(rs1.mean() == 2.375)
|
||||
|
||||
var rr: RunningRegress
|
||||
rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0])
|
||||
doAssert(rr.slope() == 0.9695585996955861)
|
||||
doAssert(rr.intercept() == -0.03424657534246611)
|
||||
doAssert(rr.correlation() == 0.9905100362239381)
|
||||
var rr1, rr2: RunningRegress
|
||||
rr1.push(@[0.0,1.0], @[0.0,1.0])
|
||||
rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0])
|
||||
let rr3 = rr1 + rr2
|
||||
doAssert(rr3.correlation() == rr.correlation())
|
||||
doAssert(clean(rr3.slope()) == clean(rr.slope()))
|
||||
doAssert(clean(rr3.intercept()) == clean(rr.intercept()))
|
||||
|
|
@ -1210,22 +1210,21 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
|
|||
## If `s` does not begin with ``prefix`` and end with ``suffix`` a
|
||||
## ValueError exception will be raised.
|
||||
result = newStringOfCap(s.len)
|
||||
var i = 0
|
||||
var i = prefix.len
|
||||
if not s.startsWith(prefix):
|
||||
raise newException(ValueError,
|
||||
"String does not start with a prefix of: " & prefix)
|
||||
inc(i)
|
||||
while true:
|
||||
if i == s.len-suffix.len: break
|
||||
case s[i]
|
||||
of '\\':
|
||||
case s[i+1]:
|
||||
of 'x':
|
||||
inc i
|
||||
inc i, 2
|
||||
var c: int
|
||||
i += parseutils.parseHex(s, c, i)
|
||||
i += parseutils.parseHex(s, c, i, maxLen=2)
|
||||
result.add(chr(c))
|
||||
inc(i, 2)
|
||||
dec i, 2
|
||||
of '\\':
|
||||
result.add('\\')
|
||||
of '\'':
|
||||
|
|
@ -1281,7 +1280,7 @@ proc editDistance*(a, b: string): int {.noSideEffect,
|
|||
|
||||
# another special case:
|
||||
if len1 == 1:
|
||||
for j in s..len2-1:
|
||||
for j in s..s+len2-1:
|
||||
if a[s] == b[j]: return len2 - 1
|
||||
return len2
|
||||
|
||||
|
|
@ -1344,8 +1343,8 @@ proc editDistance*(a, b: string): int {.noSideEffect,
|
|||
|
||||
|
||||
# floating point formating:
|
||||
|
||||
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
||||
when not defined(js):
|
||||
proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
|
||||
importc: "sprintf", varargs, noSideEffect.}
|
||||
|
||||
type
|
||||
|
|
@ -1370,29 +1369,44 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
|
|||
## after the decimal point for Nim's ``biggestFloat`` type.
|
||||
##
|
||||
## If ``precision == 0``, it tries to format it nicely.
|
||||
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
|
||||
var
|
||||
frmtstr {.noinit.}: array[0..5, char]
|
||||
buf {.noinit.}: array[0..2500, char]
|
||||
L: cint
|
||||
frmtstr[0] = '%'
|
||||
if precision > 0:
|
||||
frmtstr[1] = '#'
|
||||
frmtstr[2] = '.'
|
||||
frmtstr[3] = '*'
|
||||
frmtstr[4] = floatFormatToChar[format]
|
||||
frmtstr[5] = '\0'
|
||||
L = c_sprintf(buf, frmtstr, precision, f)
|
||||
when defined(js):
|
||||
var res: cstring
|
||||
case format
|
||||
of ffDefault:
|
||||
{.emit: "`res` = `f`.toString();".}
|
||||
of ffDecimal:
|
||||
{.emit: "`res` = `f`.toFixed(`precision`);".}
|
||||
of ffScientific:
|
||||
{.emit: "`res` = `f`.toExponential(`precision`);".}
|
||||
result = $res
|
||||
for i in 0 ..< result.len:
|
||||
# Depending on the locale either dot or comma is produced,
|
||||
# but nothing else is possible:
|
||||
if result[i] in {'.', ','}: result[i] = decimalsep
|
||||
else:
|
||||
frmtstr[1] = floatFormatToChar[format]
|
||||
frmtstr[2] = '\0'
|
||||
L = c_sprintf(buf, frmtstr, f)
|
||||
result = newString(L)
|
||||
for i in 0 ..< L:
|
||||
# Depending on the locale either dot or comma is produced,
|
||||
# but nothing else is possible:
|
||||
if buf[i] in {'.', ','}: result[i] = decimalsep
|
||||
else: result[i] = buf[i]
|
||||
const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
|
||||
var
|
||||
frmtstr {.noinit.}: array[0..5, char]
|
||||
buf {.noinit.}: array[0..2500, char]
|
||||
L: cint
|
||||
frmtstr[0] = '%'
|
||||
if precision > 0:
|
||||
frmtstr[1] = '#'
|
||||
frmtstr[2] = '.'
|
||||
frmtstr[3] = '*'
|
||||
frmtstr[4] = floatFormatToChar[format]
|
||||
frmtstr[5] = '\0'
|
||||
L = c_sprintf(buf, frmtstr, precision, f)
|
||||
else:
|
||||
frmtstr[1] = floatFormatToChar[format]
|
||||
frmtstr[2] = '\0'
|
||||
L = c_sprintf(buf, frmtstr, f)
|
||||
result = newString(L)
|
||||
for i in 0 ..< L:
|
||||
# Depending on the locale either dot or comma is produced,
|
||||
# but nothing else is possible:
|
||||
if buf[i] in {'.', ','}: result[i] = decimalsep
|
||||
else: result[i] = buf[i]
|
||||
|
||||
proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
|
||||
precision: range[0..32] = 16; decimalSep = '.'): string {.
|
||||
|
|
@ -1706,3 +1720,4 @@ when isMainModule:
|
|||
doAssert isUpper("ABC")
|
||||
doAssert(not isUpper("AAcc"))
|
||||
doAssert(not isUpper("A#$"))
|
||||
doAssert(unescape(r"\x013", "", "") == "\x013")
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
## echo "epochTime() float value: ", epochTime()
|
||||
## echo "getTime() float value: ", toSeconds(getTime())
|
||||
## echo "cpuTime() float value: ", cpuTime()
|
||||
## echo "An hour from now : ", getLocalTime(getTime()) + initInterval(0,0,0,1)
|
||||
## echo "An hour from now : ", getLocalTime(getTime()) + 1.hours
|
||||
## echo "An hour from (UTC) now: ", getGmTime(getTime()) + initInterval(0,0,0,1)
|
||||
|
||||
{.push debugger:off.} # the user does not want to trace a part
|
||||
|
|
@ -171,11 +171,6 @@ type
|
|||
{.deprecated: [TMonth: Month, TWeekDay: WeekDay, TTime: Time,
|
||||
TTimeInterval: TimeInterval, TTimeInfo: TimeInfo].}
|
||||
|
||||
proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
|
||||
|
||||
proc `miliseconds=`*(t:var TimeInterval, milliseconds: int) {.deprecated.} =
|
||||
t.milliseconds = milliseconds
|
||||
|
||||
proc getTime*(): Time {.tags: [TimeEffect], benign.}
|
||||
## gets the current calendar time as a UNIX epoch value (number of seconds
|
||||
## elapsed since 1970) with integer precission. Use epochTime for higher
|
||||
|
|
@ -245,13 +240,59 @@ proc getStartMilsecs*(): int {.deprecated, tags: [TimeEffect], benign.}
|
|||
proc initInterval*(milliseconds, seconds, minutes, hours, days, months,
|
||||
years: int = 0): TimeInterval =
|
||||
## creates a new ``TimeInterval``.
|
||||
result.milliseconds = milliseconds
|
||||
result.seconds = seconds
|
||||
result.minutes = minutes
|
||||
result.hours = hours
|
||||
result.days = days
|
||||
result.months = months
|
||||
result.years = years
|
||||
##
|
||||
## You can also use the convenience procedures called ``milliseconds``,
|
||||
## ``seconds``, ``minutes``, ``hours``, ``days``, ``months``, and ``years``.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## let day = initInterval(hours=24)
|
||||
## let tomorrow = getTime() + day
|
||||
## echo(tomorrow)
|
||||
var carryO = 0
|
||||
result.milliseconds = `mod`(milliseconds, 1000)
|
||||
carryO = `div`(milliseconds, 1000)
|
||||
result.seconds = `mod`(carryO + seconds, 60)
|
||||
carryO = `div`(seconds, 60)
|
||||
result.minutes = `mod`(carryO + minutes, 60)
|
||||
carryO = `div`(minutes, 60)
|
||||
result.hours = `mod`(carryO + hours, 24)
|
||||
carryO = `div`(hours, 24)
|
||||
result.days = carryO + days
|
||||
carryO = 0
|
||||
result.months = `mod`(months, 12)
|
||||
carryO = `div`(months, 12)
|
||||
result.years = carryO + years
|
||||
|
||||
proc `+`*(ti1, ti2: TimeInterval): TimeInterval =
|
||||
## Adds two ``TimeInterval`` objects together.
|
||||
var carryO = 0
|
||||
result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000)
|
||||
carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000)
|
||||
result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60)
|
||||
carryO = `div`(ti1.seconds + ti2.seconds, 60)
|
||||
result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60)
|
||||
carryO = `div`(ti1.minutes + ti2.minutes, 60)
|
||||
result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24)
|
||||
carryO = `div`(ti1.hours + ti2.hours, 24)
|
||||
result.days = carryO + ti1.days + ti2.days
|
||||
carryO = 0
|
||||
result.months = `mod`(ti1.months + ti2.months, 12)
|
||||
carryO = `div`(ti1.months + ti2.months, 12)
|
||||
result.years = carryO + ti1.years + ti2.years
|
||||
|
||||
proc `-`*(ti1, ti2: TimeInterval): TimeInterval =
|
||||
## Subtracts TimeInterval ``ti1`` from ``ti2``.
|
||||
result = ti1
|
||||
result.milliseconds -= ti2.milliseconds
|
||||
result.seconds -= ti2.seconds
|
||||
result.minutes -= ti2.minutes
|
||||
result.hours -= ti2.hours
|
||||
result.days -= ti2.days
|
||||
result.months -= ti2.months
|
||||
result.years -= ti2.years
|
||||
|
||||
proc isLeapYear*(year: int): bool =
|
||||
## returns true if ``year`` is a leap year
|
||||
|
|
@ -288,13 +329,22 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
|||
|
||||
newinterv.months += interval.years * 12
|
||||
var curMonth = anew.month
|
||||
for mth in 1 .. newinterv.months:
|
||||
result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
||||
if curMonth == mDec:
|
||||
curMonth = mJan
|
||||
anew.year.inc()
|
||||
else:
|
||||
curMonth.inc()
|
||||
if newinterv.months < 0: # subtracting
|
||||
for mth in countDown(-1 * newinterv.months, 1):
|
||||
result -= float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
||||
if curMonth == mJan:
|
||||
curMonth = mDec
|
||||
anew.year.dec()
|
||||
else:
|
||||
curMonth.dec()
|
||||
else: # adding
|
||||
for mth in 1 .. newinterv.months:
|
||||
result += float(getDaysInMonth(curMonth, anew.year) * 24 * 60 * 60)
|
||||
if curMonth == mDec:
|
||||
curMonth = mJan
|
||||
anew.year.inc()
|
||||
else:
|
||||
curMonth.inc()
|
||||
result += float(newinterv.days * 24 * 60 * 60)
|
||||
result += float(newinterv.hours * 60 * 60)
|
||||
result += float(newinterv.minutes * 60)
|
||||
|
|
@ -302,28 +352,39 @@ proc toSeconds(a: TimeInfo, interval: TimeInterval): float =
|
|||
result += newinterv.milliseconds / 1000
|
||||
|
||||
proc `+`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||
## adds ``interval`` time.
|
||||
## adds ``interval`` time from TimeInfo ``a``.
|
||||
##
|
||||
## **Note:** This has been only briefly tested and it may not be
|
||||
## very accurate.
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
#if a.tzname == "UTC":
|
||||
# result = getGMTime(fromSeconds(t + secs))
|
||||
#else:
|
||||
result = getLocalTime(fromSeconds(t + secs))
|
||||
|
||||
proc `-`*(a: TimeInfo, interval: TimeInterval): TimeInfo =
|
||||
## subtracts ``interval`` time.
|
||||
## subtracts ``interval`` time from TimeInfo ``a``.
|
||||
##
|
||||
## **Note:** This has been only briefly tested, it is inaccurate especially
|
||||
## when you subtract so much that you reach the Julian calendar.
|
||||
let t = toSeconds(timeInfoToTime(a))
|
||||
let secs = toSeconds(a, interval)
|
||||
#if a.tzname == "UTC":
|
||||
# result = getGMTime(fromSeconds(t - secs))
|
||||
#else:
|
||||
result = getLocalTime(fromSeconds(t - secs))
|
||||
var intval: TimeInterval
|
||||
intval.milliseconds = - interval.milliseconds
|
||||
intval.seconds = - interval.seconds
|
||||
intval.minutes = - interval.minutes
|
||||
intval.hours = - interval.hours
|
||||
intval.days = - interval.days
|
||||
intval.months = - interval.months
|
||||
intval.years = - interval.years
|
||||
let secs = toSeconds(a, intval)
|
||||
result = getLocalTime(fromSeconds(t + secs))
|
||||
|
||||
proc miliseconds*(t: TimeInterval): int {.deprecated.} = t.milliseconds
|
||||
|
||||
proc `miliseconds=`*(t: var TimeInterval, milliseconds: int) {.deprecated.} =
|
||||
## An alias for a misspelled field in ``TimeInterval``.
|
||||
##
|
||||
## **Warning:** This should not be used! It will be removed in the next
|
||||
## version.
|
||||
t.milliseconds = milliseconds
|
||||
|
||||
when not defined(JS):
|
||||
proc epochTime*(): float {.rtl, extern: "nt$1", tags: [TimeEffect].}
|
||||
|
|
@ -603,6 +664,69 @@ proc `$`*(m: Month): string =
|
|||
"November", "December"]
|
||||
return lookup[m]
|
||||
|
||||
proc milliseconds*(ms: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `ms` milliseconds
|
||||
##
|
||||
## Note: not all time functions have millisecond resolution
|
||||
initInterval(`mod`(ms,1000), `div`(ms,1000))
|
||||
|
||||
proc seconds*(s: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `s` seconds
|
||||
##
|
||||
## ``echo getTime() + 5.second``
|
||||
initInterval(0,`mod`(s,60), `div`(s,60))
|
||||
|
||||
proc minutes*(m: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `m` minutes
|
||||
##
|
||||
## ``echo getTime() + 5.minutes``
|
||||
initInterval(0,0,`mod`(m,60), `div`(m,60))
|
||||
|
||||
proc hours*(h: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `h` hours
|
||||
##
|
||||
## ``echo getTime() + 2.hours``
|
||||
initInterval(0,0,0,`mod`(h,24),`div`(h,24))
|
||||
|
||||
proc days*(d: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `d` days
|
||||
##
|
||||
## ``echo getTime() + 2.days``
|
||||
initInterval(0,0,0,0,d)
|
||||
|
||||
proc months*(m: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `m` months
|
||||
##
|
||||
## ``echo getTime() + 2.months``
|
||||
initInterval(0,0,0,0,0,`mod`(m,12),`div`(m,12))
|
||||
|
||||
proc years*(y: int): TimeInterval {.inline.} =
|
||||
## TimeInterval of `y` years
|
||||
##
|
||||
## ``echo getTime() + 2.years``
|
||||
initInterval(0,0,0,0,0,0,y)
|
||||
|
||||
proc `+=`*(t: var Time, ti: TimeInterval) =
|
||||
## modifies `t` by adding the interval `ti`
|
||||
t = timeInfoToTime(getLocalTime(t) + ti)
|
||||
|
||||
proc `+`*(t: Time, ti: TimeInterval): Time =
|
||||
## adds the interval `ti` to Time `t`
|
||||
## by converting to localTime, adding the interval, and converting back
|
||||
##
|
||||
## ``echo getTime() + 1.day``
|
||||
result = timeInfoToTime(getLocalTime(t) + ti)
|
||||
|
||||
proc `-=`*(t: var Time, ti: TimeInterval) =
|
||||
## modifies `t` by subtracting the interval `ti`
|
||||
t = timeInfoToTime(getLocalTime(t) - ti)
|
||||
|
||||
proc `-`*(t: Time, ti: TimeInterval): Time =
|
||||
## adds the interval `ti` to Time `t`
|
||||
##
|
||||
## ``echo getTime() - 1.day``
|
||||
result = timeInfoToTime(getLocalTime(t) - ti)
|
||||
|
||||
proc formatToken(info: TimeInfo, token: string, buf: var string) =
|
||||
## Helper of the format proc to parse individual tokens.
|
||||
##
|
||||
|
|
@ -1192,112 +1316,10 @@ proc timeToTimeInterval*(t: Time): TimeInterval =
|
|||
# Milliseconds not available from Time
|
||||
|
||||
when isMainModule:
|
||||
# $ date --date='@2147483647'
|
||||
# Tue 19 Jan 03:14:07 GMT 2038
|
||||
|
||||
var t = getGMTime(fromSeconds(2147483647))
|
||||
assert t.format("ddd dd MMM hh:mm:ss ZZZ yyyy") == "Tue 19 Jan 03:14:07 UTC 2038"
|
||||
assert t.format("ddd ddMMMhh:mm:ssZZZyyyy") == "Tue 19Jan03:14:07UTC2038"
|
||||
|
||||
assert t.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||
"19 19 Tue Tuesday 3 03 3 03 14 14 1 01 Jan January 7 07 A AM 8 38 038 2038 02038 0 00 00:00 UTC"
|
||||
|
||||
assert t.format("yyyyMMddhhmmss") == "20380119031407"
|
||||
|
||||
var t2 = getGMTime(fromSeconds(160070789)) # Mon 27 Jan 16:06:29 GMT 1975
|
||||
assert t2.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||
"27 27 Mon Monday 4 04 16 16 6 06 1 01 Jan January 29 29 P PM 5 75 975 1975 01975 0 00 00:00 UTC"
|
||||
|
||||
when not defined(JS):
|
||||
when sizeof(Time) == 8:
|
||||
var t3 = getGMTime(fromSeconds(889067643645)) # Fri 7 Jun 19:20:45 BST 30143
|
||||
assert t3.format("d dd ddd dddd h hh H HH m mm M MM MMM MMMM s" &
|
||||
" ss t tt y yy yyy yyyy yyyyy z zz zzz ZZZ") ==
|
||||
"7 07 Fri Friday 6 06 18 18 20 20 6 06 Jun June 45 45 P PM 3 43 143 0143 30143 0 00 00:00 UTC"
|
||||
assert t3.format(":,[]()-/") == ":,[]()-/"
|
||||
|
||||
var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
||||
assert t4.format("M MM MMM MMMM") == "10 10 Oct October"
|
||||
|
||||
# Interval tests
|
||||
assert((t4 - initInterval(years = 2)).format("yyyy") == "1995")
|
||||
assert((t4 - initInterval(years = 7, minutes = 34, seconds = 24)).format("yyyy mm ss") == "1990 24 10")
|
||||
|
||||
var s = "Tuesday at 09:04am on Dec 15, 2015"
|
||||
var f = "dddd at hh:mmtt on MMM d, yyyy"
|
||||
assert($s.parse(f) == "Tue Dec 15 09:04:00 2015")
|
||||
# ANSIC = "Mon Jan _2 15:04:05 2006"
|
||||
s = "Thu Jan 12 15:04:05 2006"
|
||||
f = "ddd MMM dd HH:mm:ss yyyy"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# UnixDate = "Mon Jan _2 15:04:05 MST 2006"
|
||||
s = "Thu Jan 12 15:04:05 MST 2006"
|
||||
f = "ddd MMM dd HH:mm:ss ZZZ yyyy"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
|
||||
s = "Thu Jan 12 15:04:05 -07:00 2006"
|
||||
f = "ddd MMM dd HH:mm:ss zzz yyyy"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC822 = "02 Jan 06 15:04 MST"
|
||||
s = "12 Jan 16 15:04 MST"
|
||||
f = "dd MMM yy HH:mm ZZZ"
|
||||
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
|
||||
# RFC822Z = "02 Jan 06 15:04 -0700" # RFC822 with numeric zone
|
||||
s = "12 Jan 16 15:04 -07:00"
|
||||
f = "dd MMM yy HH:mm zzz"
|
||||
assert($s.parse(f) == "Tue Jan 12 15:04:00 2016")
|
||||
# RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
|
||||
s = "Monday, 12-Jan-06 15:04:05 MST"
|
||||
f = "dddd, dd-MMM-yy HH:mm:ss ZZZ"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
|
||||
s = "Thu, 12 Jan 2006 15:04:05 MST"
|
||||
f = "ddd, dd MMM yyyy HH:mm:ss ZZZ"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" # RFC1123 with numeric zone
|
||||
s = "Thu, 12 Jan 2006 15:04:05 -07:00"
|
||||
f = "ddd, dd MMM yyyy HH:mm:ss zzz"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC3339 = "2006-01-02T15:04:05Z07:00"
|
||||
s = "2006-01-12T15:04:05Z-07:00"
|
||||
f = "yyyy-MM-ddTHH:mm:ssZzzz"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
f = "yyyy-MM-dd'T'HH:mm:ss'Z'zzz"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
|
||||
s = "2006-01-12T15:04:05.999999999Z-07:00"
|
||||
f = "yyyy-MM-ddTHH:mm:ss.999999999Zzzz"
|
||||
assert($s.parse(f) == "Thu Jan 12 15:04:05 2006")
|
||||
# Kitchen = "3:04PM"
|
||||
s = "3:04PM"
|
||||
f = "h:mmtt"
|
||||
assert "15:04:00" in $s.parse(f)
|
||||
when not defined(testing):
|
||||
echo "Kitchen: " & $s.parse(f)
|
||||
var ti = timeToTimeInfo(getTime())
|
||||
echo "Todays date after decoding: ", ti
|
||||
var tint = timeToTimeInterval(getTime())
|
||||
echo "Todays date after decoding to interval: ", tint
|
||||
# checking dayOfWeek matches known days
|
||||
assert getDayOfWeek(21, 9, 1900) == dFri
|
||||
assert getDayOfWeek(1, 1, 1970) == dThu
|
||||
assert getDayOfWeek(21, 9, 1970) == dMon
|
||||
assert getDayOfWeek(1, 1, 2000) == dSat
|
||||
assert getDayOfWeek(1, 1, 2021) == dFri
|
||||
# Julian tests
|
||||
assert getDayOfWeekJulian(21, 9, 1900) == dFri
|
||||
assert getDayOfWeekJulian(21, 9, 1970) == dMon
|
||||
assert getDayOfWeekJulian(1, 1, 2000) == dSat
|
||||
assert getDayOfWeekJulian(1, 1, 2021) == dFri
|
||||
|
||||
# toSeconds tests with GM and Local timezones
|
||||
#var t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
||||
var t4L = getLocalTime(fromSeconds(876124714))
|
||||
assert toSeconds(timeInfoToTime(t4L)) == 876124714 # fromSeconds is effectively "localTime"
|
||||
assert toSeconds(timeInfoToTime(t4L)) + t4L.timezone.float == toSeconds(timeInfoToTime(t4))
|
||||
|
||||
# this is testing non-exported function
|
||||
var
|
||||
t4 = getGMTime(fromSeconds(876124714)) # Mon 6 Oct 08:58:34 BST 1997
|
||||
t4L = getLocalTime(fromSeconds(876124714))
|
||||
assert toSeconds(t4, initInterval(seconds=0)) == 0.0
|
||||
assert toSeconds(t4L, initInterval(milliseconds=1)) == toSeconds(t4, initInterval(milliseconds=1))
|
||||
assert toSeconds(t4L, initInterval(seconds=1)) == toSeconds(t4, initInterval(seconds=1))
|
||||
|
|
@ -1307,12 +1329,5 @@ when isMainModule:
|
|||
assert toSeconds(t4L, initInterval(months=1)) == toSeconds(t4, initInterval(months=1))
|
||||
assert toSeconds(t4L, initInterval(years=1)) == toSeconds(t4, initInterval(years=1))
|
||||
|
||||
# adding intervals
|
||||
var
|
||||
a1L = toSeconds(timeInfoToTime(t4L + initInterval(hours = 1))) + t4L.timezone.float
|
||||
a1G = toSeconds(timeInfoToTime(t4)) + 60.0 * 60.0
|
||||
assert a1L == a1G
|
||||
# subtracting intervals
|
||||
a1L = toSeconds(timeInfoToTime(t4L - initInterval(hours = 1))) + t4L.timezone.float
|
||||
a1G = toSeconds(timeInfoToTime(t4)) - (60.0 * 60.0)
|
||||
assert a1L == a1G
|
||||
# Further tests are in tests/stdlib/ttime.nim
|
||||
# koch test c stdlib
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ proc validateUtf8*(s: string): int =
|
|||
if ord(s[i]) <=% 127:
|
||||
inc(i)
|
||||
elif ord(s[i]) shr 5 == 0b110:
|
||||
if ord(s[i]) < 0xc2: return i # Catch overlong ascii representations.
|
||||
if i+1 < L and ord(s[i+1]) shr 6 == 0b10: inc(i, 2)
|
||||
else: return i
|
||||
elif ord(s[i]) shr 4 == 0b1110:
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode =
|
|||
next(x)
|
||||
of xmlEntity:
|
||||
## &entity;
|
||||
errors.add(errorMsg(x, "unknown entity: " & x.entityName))
|
||||
result = newEntity(x.entityName)
|
||||
next(x)
|
||||
of xmlEof: discard
|
||||
|
||||
|
|
@ -143,17 +143,24 @@ proc loadXml*(path: string): XmlNode =
|
|||
result = loadXml(path, errors)
|
||||
if errors.len > 0: raiseInvalidXml(errors)
|
||||
|
||||
when not defined(testing) and isMainModule:
|
||||
import os
|
||||
when isMainModule:
|
||||
when not defined(testing):
|
||||
import os
|
||||
|
||||
var errors: seq[string] = @[]
|
||||
var x = loadXml(paramStr(1), errors)
|
||||
for e in items(errors): echo e
|
||||
var errors: seq[string] = @[]
|
||||
var x = loadXml(paramStr(1), errors)
|
||||
for e in items(errors): echo e
|
||||
|
||||
var f: File
|
||||
if open(f, "xmltest.txt", fmWrite):
|
||||
f.write($x)
|
||||
f.close()
|
||||
var f: File
|
||||
if open(f, "xmltest.txt", fmWrite):
|
||||
f.write($x)
|
||||
f.close()
|
||||
else:
|
||||
quit("cannot write test.txt")
|
||||
else:
|
||||
quit("cannot write test.txt")
|
||||
block: # correctly parse ../../tests/testdata/doc1.xml
|
||||
let filePath = "tests/testdata/doc1.xml"
|
||||
var errors: seq[string] = @[]
|
||||
var xml = loadXml(filePath, errors)
|
||||
assert(errors.len == 0, "The file tests/testdata/doc1.xml should be parsed without errors.")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue