Adds utility modules.
authordsc <david.schoonover@gmail.com>
Sun, 19 Feb 2012 22:32:53 +0000 (14:32 -0800)
committerdsc <david.schoonover@gmail.com>
Sun, 19 Feb 2012 22:32:53 +0000 (14:32 -0800)
lib/util/bitstring.co [new file with mode: 0644]
lib/util/crc.co [new file with mode: 0644]
lib/util/hashset.co [new file with mode: 0644]
lib/util/index.co [new file with mode: 0644]
lib/util/op.co [new file with mode: 0644]
www/modules.yaml

diff --git a/lib/util/bitstring.co b/lib/util/bitstring.co
new file mode 100644 (file)
index 0000000..a5be754
--- /dev/null
@@ -0,0 +1,247 @@
+SEEK_ABSOLUTE = 0
+SEEK_RELATIVE = 1
+SEEK_FROM_EOF = 2
+
+
+# Binary representation of the number
+bin = (n) ->
+    do
+        s = (if n % 2 then '1' else '0') + (s or '')
+        n >>= 1
+    while n
+    s
+
+# Number of bits needed to represent the absolute value of n.
+binlen = (n) ->
+    bin Math.abs n .length
+
+# Returns a run of 1s of size n.
+mask = (n) ->
+    (1 << n) - 1
+
+chr = -> String.fromCharCode it
+ord = -> String(it).charCodeAt 0
+
+
+
+
+/**
+ * File-like object for reading/writing bits.
+ * @class
+ */
+class BitString
+    # Array<UInt8>
+    buf : null
+    
+    # Byte position of read/write cursor (-1 for end).
+    _pos : -1
+    
+    # Spill cache for bits smaller than a byte waiting to write.
+    _spill : 0
+    
+    # Number of bits in the spill cache.
+    _spillen : 0
+    
+    # Peek cache for read requests smaller than a byte.
+    _peek : 0
+    
+    # Number of bits in the peek cache.
+    _peeklen : 0
+    
+    
+    
+    (source='', buf=[]) ->
+        @buf = buf.slice()
+        for i til source.length
+            @_bufwrite source.charCodeAt i
+    
+    size: ->
+        @buf.length + if @_spillen then 1 else 0
+    
+    bitsize: ->
+        @buf.length*8 + @_spillen
+    
+    _bufwrite: (b) ->
+        if @_pos is -1
+            @buf.push b
+        else
+            @buf[@_pos] = b
+            @_pos = -1 if ++@_pos >= @buf.length
+        this
+    
+    # Writes bits to the stream; bits must be supplied as a number. Supplying n=0 will write one bit.
+    # Supplying the optional parameter length treats the bits as a field with the given length.
+    writebits: (n, size) ->
+        size = size or binlen n
+        bits = (@_spill << size) | n
+        size += @_spillen # handles _spill=0 but _spillen > 0
+        while size >= 8
+            size -= 8
+            b = bits >> size
+            bits &= mask size
+            @_bufwrite b
+        @_spill = bits
+        @_spillen = size
+        this
+    
+    # Flushes any pending bits to the stream.
+    flush: ->
+        b = @_spill
+        if @_spillen
+            b <<= 8 - @_spillen
+            @_bufwrite b
+        @_spill   = 0
+        @_spillen = 0
+        this
+    
+    # Truncates the stream to zero bits.
+    truncate: ->
+        @buf      = []
+        @_pos     = -1
+        @_spill   = 0
+        @_spillen = 0
+        @_peek    = 0
+        @_peeklen = 0
+        this
+    
+    # Move buffer cursor to given byte-offset. mode: 0 = absolute, 1 = relative, 2 = relative EOF
+    _bufseek: (n, mode=SEEK_ABSOLUTE) ->
+        switch mode
+        case 1 # relative
+            pos = @_pos + n
+        case 2
+            pos = @buf.length + n
+        default # absolute
+            pos = n
+        @_pos = if pos >= @buf.length then -1 else Math.max 0, pos
+        this
+    
+    # Flushes the bit-buffer and moves to the given byte-offset. mode: 0 = absolute, 1 = relative, 2 = relative EOF
+    seek: (n, mode=SEEK_ABSOLUTE) ->
+        @flush()
+        @_peek = 0
+        @_peeklen = 0
+        @_bufseek n, mode
+        this
+    
+    # Returns the current position of the cursor as a *byte* offset from the start of the stream.
+    tell: ->
+        if @_pos is -1 then @buf.length else @_pos
+    
+    
+    _nextbyte: ->
+        return null if @_pos is -1
+        byte = @buf[ @_pos++ ]
+        @_pos = -1 if @_pos >= @buf.length
+        byte
+    
+    
+    # Reads n bits from the stream.
+    readbits: (n) ->
+        return 0 if n == 0
+        
+        size = @_peeklen
+        bits = @_peek
+        
+        while size < n
+            byte = @_nextbyte()
+            break unless byte?
+            size += 8
+            bits = (bits << 8) | byte
+        
+        if size > n
+            @_peeklen = size - n
+            @_peek = bits & mask(@_peeklen)
+            bits >>= @_peeklen
+        else
+            @_peeklen = 0
+            @_peek = 0
+        
+        return if size then bits else null
+    
+    
+    # Reads the next n bits without moving the cursor.
+    peek: (n) ->
+        offset = 0
+        size = @_peeklen
+        bits = @_peek
+        
+        while size < n
+            byte = @_nextbyte()
+            break unless byte?
+            offset += 1
+            size += 8
+            bits = (bits << 8) | byte
+        
+        if size == 0
+            return null
+        
+        if size > n
+            bits >>= size - n
+        
+        if offset
+            @_bufseek -offset, SEEK_RELATIVE
+        bits
+    
+    
+    # True if there is more data to read.
+    hasMore: ->
+        @peek(1)?
+    
+    
+    ### XXX: Should .each(), .map(), .reduce() flush?
+    
+    # forEach of bytes
+    each: (fn, cxt=this) ->
+        @buf.forEach fn, cxt
+    
+    # map over bytes
+    map: (fn, cxt=this) ->
+        @buf.map fn, cxt
+    
+    # reduce over bytes
+    reduce: (fn, acc, cxt=this) ->
+        fn .= bind this
+        @buf.reduce fn, acc
+    
+    
+    # Returns the stream as a bytearray.
+    bytearray: ->
+        @flush().buf.slice()
+    
+    # Dumps the stream as a binary string. Unlike __index__(), bin() will not cause int overflow.
+    bin: (byte_sep='') ->
+        @flush().buf.map(bin).join(byte_sep)
+    
+    # Returns the stream as a hex string.
+    hex: ->
+        @flush().buf.map(hex).join('')
+    
+    # Returns the buffer as a number. Use this with obvious caution. Called by builtins bin(), int(), long(), etc.
+    number: ->
+        @flush()
+        @reduce (n, byte) -> (n << 8) | byte
+    
+    
+    # Dumps the stream as a string; does not flush or change cursor position.
+    dump: ->
+        @buf.map(chr).join('') + if @_spillen then chr @_spill << (8 - @_spillen) else ''
+    
+    repr: (dump_buf=true) ->
+        s = if dump_buf then "buf=#{@dump()}" else "len(buf)=#{@buf.length}"
+        return "BitString(#s, 
+            spill[#{@_spillen}]=#{bin @_spill}, 
+            tell=#{@tell()}, 
+            peek[#{@_peeklen}]=#{bin @_peek})
+        "
+    
+    # Dumps the stream as a string; flushes the bit-buffer but leaves cursor position unchanged.
+    toString: ->
+        @flush().dump()
+
+
+exports = module.exports = BitString
+
+exports.SEEK_ABSOLUTE = SEEK_ABSOLUTE
+exports.SEEK_RELATIVE = SEEK_RELATIVE
+exports.SEEK_FROM_EOF = SEEK_FROM_EOF
diff --git a/lib/util/crc.co b/lib/util/crc.co
new file mode 100644 (file)
index 0000000..056b090
--- /dev/null
@@ -0,0 +1,58 @@
+
+crc32 = exports.crc32 = (s, last_crc=0) ->
+    s = utf8Encode s
+    crc = last_crc ^ (-1)
+    for i til s.length
+        y = (crc ^ s.charCodeAt i) & 0xFF
+        x = "0x" + TABLE.substr y*9, 8
+        crc = (crc >>> 8) ^ x
+    crc ^ (-1)
+
+
+utf8Encode = exports.utf8Encode = (s) ->
+    s = s.replace /\r\n/g, '\n'
+    u = ''
+    for n til s.length
+        c = s.charCodeAt n
+        if c < 128
+            u += String.fromCharCode c
+        else if 127 < c < 2048
+            u += String.fromCharCode (c >> 6) | 192
+            u += String.fromCharCode (c & 63) | 128
+        else
+            u += String.fromCharCode (c >> 12) | 224
+            u += String.fromCharCode ((c >> 6) & 63) | 128
+            u += String.fromCharCode (c & 63) | 128
+    u
+
+
+# static precompiled hashes
+TABLE = '''
+    00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 
+    E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 
+    1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 
+    FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 
+    35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A 
+    C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 
+    2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 
+    9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 
+    6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 
+    8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 
+    4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 
+    AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 
+    5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 
+    03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 
+    E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 
+    196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 
+    D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 
+    36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 
+    CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 
+    2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 
+    95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 
+    68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 
+    8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 
+    4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 
+    BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 
+    5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D
+'''
+
diff --git a/lib/util/hashset.co b/lib/util/hashset.co
new file mode 100644 (file)
index 0000000..0c99bef
--- /dev/null
@@ -0,0 +1,418 @@
+_ = require 'underscore'
+
+
+/**
+ * A Set class, implemented using the `__id__` property on non-primitive objects it is passed. 
+ * Arrays are hashed based on their contents. If an object lacks `__id__`, an exception will be
+ * thrown. This class does not keep values in sorted order.
+ * 
+ * Underscore provides an easy way to generate unique IDs with the (surprise!) `_.uniqueId()`
+ * function.
+ * @see http://documentcloud.github.com/underscore/#uniqueId
+ * 
+ * @class
+ */
+class HashSet
+    /**
+     * Objects by Id.
+     * @private
+     */
+    _byId : {}
+    
+    /**
+     * Set contents.
+     * @private
+     */
+    _o : []
+    
+    /**
+     * Number of elements in the set.
+     * @property {Number}
+     */
+    length : 0
+    
+    
+    /**
+     * Accepts any number of collections to be added to the set.
+     * @constructor
+     */
+    ->
+        @_byId = {}
+        @_o    = []
+        @update ...arguments if arguments.length
+    
+    
+    
+    /**
+     * Determine unique identifier for the given value.
+     * @private
+     * @returns {String} Id for this value.
+     */
+    _getIdSafe : (v) ->
+        t  = typeof v
+        
+        switch t
+        case 'undefined'
+            return 'u'
+        case 'boolean' 'string' 'number'
+            return "#{t.charAt 0}:#v"
+        if v is null
+            return 'n'
+        if '__id__' in v
+            return 'o:' + v.__id__
+        if _.isArray v
+            return 'a:' + v.map @_getIdSafe, this .join ','
+    
+    /**
+     * Determine unique identifier for the given value, throwing an exception otherwise.
+     * @private
+     * @returns {String} Id for this value.
+     */
+    _getId : (v) ->
+        id = @_getIdSafe v
+        unless id?
+            throw new Error "HashSet elements must be hashable (#v)"
+        id
+    
+    
+    
+    /**
+     * Aliases: HashSet#has
+     * @param {Any} v Value to test.
+     * @returns {Boolean} Whether HashSet contains value.
+     */
+    contains : (v) ->
+        @_getIdSafe(v) in @_byId
+    
+    
+    /**
+     * @private
+     * @returns {this}
+     */
+    _addOne : (v) ->
+        id = @_getId v
+        unless id in @_byId
+            @_byId[id] = v
+            @_o.push(v)
+            @length = @_o.length
+        this
+    
+    
+    /**
+     * Add values to the HashSet.
+     * Aliases: HashSet#push HashSet#unshift
+     * @param {Any} values... Values to add.
+     * @returns {this}
+     */
+    add : (...values) ->
+        _.each arguments, @_addOne, this
+        this
+    
+    /**
+     * @private
+     * @returns {this}
+     */
+    _removeOne : (v) ->
+        id = @_getId v
+        if id in @_byId
+            delete @_byId[id]
+            @_o.splice @_o.indexOf(v), 1
+            @length = @_o.length
+        this
+    
+    
+    /**
+     * Remove values from the HashSet.
+     * Aliases: HashSet#without
+     * @param {Any} values... Values to remove.
+     * @returns {this}
+     */
+    remove : (...values) ->
+        _.each arguments, @_removeOne, this
+        this
+    
+    
+    /**
+     * Update this HashSet (in-place) with other collections.
+     * Aliases: HashSet#extend HashSet#concat
+     * @param {Array|Object} it... Collection to add.
+     * @returns {this}
+     */
+    update : (vs) ->
+        _.each arguments, ~> _.each it, @_addOne, this
+        this
+    
+    
+    /**
+     * Remove and return an element from the set.
+     * Aliases: HashSet#shift
+     * @returns {Any} An element from the set.
+     */
+    pop : ->
+        return unless @_o.length
+        v  = @_o.shift()
+        id = @_getIdSafe v
+        delete @_byId[id]
+        return v
+    
+    
+    /**
+     * Returns but does not remove the an element from the set.
+     * @returns {Any} An element from the set.
+     */
+    element : ->
+        @_o[0]
+    
+    
+    /**
+     * Clones the set, returning a new object.
+     * @returns {HashSet}
+     */
+    clone : ->
+        new HashSet @_o
+    
+    
+    /**
+     * Removes all elements from the set.
+     * Aliases: HashSet#empty
+     * @returns {this}
+     */
+    clear: ->
+        @_byId  = {}
+        @_o     = []
+        @length = 0
+        this
+    
+    
+    
+    ### Collection Operations
+    
+    /**
+     * Transforms the collection into a single value, front-to-back.
+     * Aliases: HashSet#inject HashSet#fold HashSet#foldl HashSet#foldr
+     * @param {Function} fn Reducer function.
+     * @param {Any} [acc] Starting accumulator value.
+     * @param {Object} [cxt=this] Context; defaults to this HashSet.
+     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce
+     * @returns {Any}
+     */
+    reduce: (fn, acc, cxt) ->
+        _.reduce @_o, fn, acc, cxt or this
+    
+    /**
+     * Applies a function to each element.
+     * Aliases: HashSet#each
+     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach
+     * @returns {this}
+     */
+    forEach: (fn, cxt) ->
+        _.forEach @_o, fn, cxt or this
+        this
+    
+    /**
+     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map
+     * @return {HashSet} A new HashSet of elements produced by applying the transform across each element.
+     */
+    map: (fn, cxt) ->
+        new HashSet _.map @_o, fn, cxt or this
+    
+    
+    /**
+     * Aliases: HashSet#select
+     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
+     * @return {HashSet} A new HashSet of only the elements passing the filter.
+     */
+    filter: (fn, cxt) ->
+        new HashSet _.filter @_o, fn, cxt or this
+    
+    /**
+     * Like `HashSet.filter()`, but instead keeps values for which the filter returns false.
+     * @see HashSet#filter
+     * @return {HashSet} A new HashSet of only the elements for which the filter returns false.
+     */
+    reject: (fn, cxt) ->
+        new HashSet _.reject @_o, fn, cxt or this
+    
+    
+    /**
+     * Aliases: HashSet#any
+     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/some
+     * @return {Boolean}
+     */
+    some: (fn, cxt) ->
+        _.some @_o, fn, cxt or this
+    
+    /**
+     * Aliases: HashSet#all
+     * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every
+     * @return {Boolean}
+     */
+    every: (fn, cxt) ->
+        _.every @_o, fn, cxt or this
+    
+    
+    /**
+     * Iterates through the HashSet, returning the first value for which `fn` returns truth-y.
+     * Aliases: HashSet#detect
+     * @returns {Any}
+     */
+    find: (fn, cxt) ->
+        _.find @_o, fn, cxt or this
+    
+    
+    /**
+     * @returns {Array} List of all values at property `prop`.
+     */
+    pluck : (prop) ->
+        _.pluck @_o, prop
+    
+    
+    /**
+     * Invokes the named method on each element in the set, returning a list of the results.
+     * @param {String} methodName Name of the method on each element to call.
+     * @param {Any...} [args...] Optional arguments to call pass to method call.
+     * @returns {Array} List of results.
+     */
+    invoke : (methodName) ->
+        _.invoke @_o, ...arguments
+    
+    
+    /**
+     * @returns {Array} List of the unique identifiers for each element of the set.
+     */
+    keys : ->
+        _.keys @_byId
+    
+    /**
+     * Converts this HashSet to an Array.
+     * Aliases: HashSet#toArray
+     * @returns {Array}
+     */
+    values : ->
+        @_o.slice()
+    
+    
+    
+    ### Comparators and HashSet Operations
+    
+    /**
+     * Tests if `a` is a Collection and has all elements in common with the set.
+     * Sets are equal if and only if their intersection has the same size as both sets.
+     * @param {Collection} a
+     * @returns {Boolean}
+     */
+    equals : (a) ->
+        return false unless a
+        L = @_o.length
+        return L is a.length and L is @intersect(a).length
+    
+    
+    /**
+     * Tests if the set has no elements in common with `a`.
+     * Sets are disjoint if and only if their intersection is the empty set.
+     * @param {Collection} a
+     * @returns {Boolean}
+     */
+    isDisjoint : (a) ->
+        return true unless a
+        return not _.some a, @contains, this
+    
+    
+    /**
+     * Test whether every element in the set is in `a`.
+     * @param {Collection} a
+     * @returns {Boolean}
+     */
+    isSubset : (a) ->
+        return false unless a
+        A = _ if _.isArray a then a else _.values a
+        @every A.contains, A
+    
+    
+    /**
+     * Test whether every element in `a` is in the set.
+     * @param {Array|Object} a
+     * @returns {Boolean}
+     */
+    isSuperset : (a) ->
+        return false unless a
+        _.every a, @contains, this
+    
+    
+    /**
+     * HashSet Intersection (A ^ B)
+     * Intersects this YArray with another collection, returning a new YArray.
+     * The membership test uses _(a).contains(), so it is possible to intersect collections of different types.
+     * For YArray and YObject, .contains() uses strict equality (is) via .indexOf().
+     * 
+     * @param {Array|Object} a Comparison collection.
+     * @returns {HashSet} A new YArray of all elements of {this} found in the supplied collection.
+     * 
+     * @example
+     *      foo = /foo/
+     *      A = [foo, 'A', 1, 2, 3, 'C', /foo/]
+     *      B = [foo, 'B', 3, 'A', 1, /foo/]
+     *      ins = _(A).intersect(B)
+     *      ins.toString() is "HashSet([/foo/,A,1,3])"; # true
+     *      ins.get(0) is foo; # true
+     */
+    intersect : (a) ->
+        new HashSet _.intersect @_o, _.map arguments, _.values
+    
+    
+    /**
+     * HashSet Union (A v B)
+     * Aliases: HashSet#extend HashSet#concat
+     * @param {Array|Object} a Other collection(s).
+     * @returns {HashSet} A new HashSet of all elements of both collections, without duplicates.
+     */
+    union : (a) ->
+        _.reduce arguments, ((out, it) -> out.update it), @clone()
+    
+    
+    /**
+     * HashSet Difference (A - B)
+     * @param {Array|Object} a Comparison collection(s).
+     * @returns {HashSet} A new HashSet of only elements of this HashSet not in supplied collection(s).
+     */
+    difference : (a) ->
+        new HashSet _.difference @_o, _.map arguments, _.values
+    
+    
+    /**
+     * Symmetric Difference (A - B) v (B - A)
+     * @returns {HashSet} 
+     */
+    xor : (a) ->
+        a = _.values a
+        @difference a .union _.difference a, @_o
+    
+    
+    toString : ->
+        "HashSet([#{@_o}])"
+    
+
+
+### Aliases
+
+pt = HashSet::
+
+pt.push    = pt.unshift = pt.add
+pt.shift   = pt.pop
+pt.without = pt.remove
+pt.empty   = pt.clear
+pt.has     = pt.include = pt.contains
+
+pt.fold    = pt.foldl = pt.foldr = pt.inject = pt.reduce
+pt.each    = pt.forEach
+pt.select  = pt.filter
+pt.all     = pt.every
+pt.any     = pt.some
+
+pt.detect  = pt.find
+pt.toArray = pt.values
+pt.extend  = pt.concat = pt.union
+
+
+exports = module.exports = HashSet
+
diff --git a/lib/util/index.co b/lib/util/index.co
new file mode 100644 (file)
index 0000000..30d12d9
--- /dev/null
@@ -0,0 +1,18 @@
+_ = require 'kraken/underscore'
+
+root = do -> this
+root.console or= _ <[ log info warn error dir table group groupCollapsed groupEnd ]> .synthesize -> [it, nop]
+
+root.jQuery?.fn.invoke = (method, ...args) ->
+    for el, idx of this
+        el = jQuery(el)
+        el[method] ...args
+
+op        = require 'kraken/util/op'
+# HashSet   = require 'kraken/util/hashset'
+# BitString = require 'kraken/util/bitstring'
+# {crc32}   = require 'kraken/util/crc'
+
+
+exports import { root, _, op, }
+# exports import { root, _, op, HashSet, BitString, crc32, }
diff --git a/lib/util/op.co b/lib/util/op.co
new file mode 100644 (file)
index 0000000..83a75bc
--- /dev/null
@@ -0,0 +1,122 @@
+
+FALSEY = /^\s*(?:no|off|false)\s*$/i
+parseBool = (s) ->
+    i = parseInt(s or 0)
+    !! if isNaN(i) then not FALSEY.test(s) else i
+
+module.exports = op =
+    
+    I       : (x) -> x
+    K       : (k) -> -> k
+    nop     : ->
+    kThis   : -> this
+    kObject : -> {}
+    kArray  : -> []
+    
+    # values
+    val     : (def,o) -> o ? def
+    ok      : (o)     -> o?
+    
+    first   : (a)     -> a
+    second  : (_,a)   -> a
+    nth     : (n) ->
+        switch n
+            case 0 then op.first
+            case 1 then op.second
+            default     -> arguments[n]
+    
+    flip    : (fn) ->
+        (a, b) ->
+            arguments[0] = b
+            arguments[1] = a
+            fn.apply this, arguments
+    
+    # reduce-ordered values & accessors
+    khas      : (k,o)       ->  k in o
+    kget      : (k,o)       ->  o[k]
+    defkget   : (def,k,o)   ->  if k in o then o[k] else def
+    thisget   : (k)         ->  this[k]
+    vkset     : (o,v,k)     ->  o[k] = v if o and k?; o
+    
+    # curry-ordered values & accessors
+    has       : (o,k)       ->  k in o
+    get       : (o,k)       ->  o[k]
+    getdef    : (o,k,def)   ->  if k in o then o[k] else def
+    kvset     : (o,k,v)     ->  o[k] = v if o and k?; o
+    thiskvset : (k,v)       ->  @[k] = v if k?; this
+    
+    prop      : (k) -> (o) -> o[k]
+    method    : (name, ...args) ->
+        (obj, ..._args) ->
+            obj[name] ...args.concat(_args) if obj?[name]
+    isK       : (k) -> (v) -> v is k
+    
+    # type coercion (w/ limited parameters for mapping)
+    parseBool   : parseBool
+    toBool      : (v) -> !! v
+    toInt       : (v) -> parseInt v
+    toFloat     : (v) -> parseFloat v
+    toStr       : (v) -> String v
+    
+    # comparison
+    cmp       : (x,y)  ->  if x < y then -1 else (if x > y then 1 else 0)
+    eq        : (x,y)  ->  x == y
+    ne        : (x,y)  ->  x != y
+    gt        : (x,y)  ->  x  > y
+    ge        : (x,y)  ->  x >= y
+    lt        : (x,y)  ->  x  < y
+    le        : (x,y)  ->  x <= y
+    
+    # math
+    add       : (x,y)  ->  x  + y
+    sub       : (x,y)  ->  x  - y
+    mul       : (x,y)  ->  x  * y
+    div       : (x,y)  ->  x  / y
+    flrdiv    : (x,y)  ->  Math.floor(x / y)
+    mod       : (x,y)  ->  x  % y
+    neg       : (x)    ->  -x
+    log2      : (n)    -> Math.log n / Math.LN2
+    
+    
+    # logic
+    is        : (x,y)  ->  x is y
+    isnt      : (x,y)  ->  x is not y
+    and       : (x,y)  ->  x and y
+    or        : (x,y)  ->  x or y
+    not       : (x)    ->  not x
+    
+    # bitwise
+    bitnot    : (x)    ->  ~x
+    bitand    : (x,y)  ->  x  & y
+    bitor     : (x,y)  ->  x  | y
+    bitxor    : (x,y)  ->  x  ^ y
+    lshift    : (x,y)  ->  x << y
+    rshift    : (x,y)  ->  x >> y
+    # zrshift   : (x,y)     ->  x >>> y
+    
+    # binary
+    
+    # Binary representation of the number.
+    bin : (n) ->
+        do
+            s = (if n % 2 then '1' else '0') + (s or '')
+            n >>= 1
+        while n
+        s
+    
+    # Number of bits needed to represent the absolute value of n.
+    binlen : (n) ->
+        bin Math.abs n .length
+    
+    # Returns a run of 1s of size n.
+    mask : (n) ->
+        (1 << n) - 1
+    
+    # strings
+    chr    : -> String.fromCharCode it
+    ord    : -> String(it).charCodeAt 0
+    encode : -> it and $ "<div>#it</div>" .html().replace /"/g, '&quot;'
+    decode : -> it and $ "<div>#it</div>" .text()
+    
+    
+
index 1027fa8..618dc36 100644 (file)
@@ -28,8 +28,11 @@ all:
                 - underscore.array
                 - underscore.object
                 - index
-            # - scaffold
-            # - graph
+            - util:
+                - op
+                - index
+            - scaffold
+            - graph
 
 # -   suffix: .js
 #     paths: