A bit array (or bitset) library in F# where bits are stored in bytes to reduce the allocation size.

Source

open System

(** Notes

8 bits per byte
4 bytes to represent a signed integer

0xf = 15 * (2. ** 4.) = 16 ^- 1
0xff = 15 * (2. ** 4.) + 15 = 16 ^ 2 - 1
...
0xffff = 16 ^ 4 - 1
...
0xffffffff = 16 ^ 8 - 1

n <<< i = b * (2 ^ i)

n >>> i = b / (2 ^ i) as an integer division, i.e. 17 / 2 = 8

n &&& -b returns the value of the first bit Set to 1 in n,
i.e. [2 ^ pos] where [pos] is the index of the minimum Set bit.

b &&& i =  0 if the ith bit is not Set in n, ith's value otherwise
*)

type Bitarray =
  { Data : byte[]
    Length : int
  }
  static member (~~~) ba =
    { ba with Data = Array.map (fun e -> ~~~e) ba.Data }

  static member (^^^) (ba, ba') =
    if ba.Length <> ba'.Length then
      invalidArg "ba" "input Bitarrays have different length"
    { ba with Data = Array.map2 (fun e e' -> e ^^^ e') ba.Data ba'.Data }

  static member (|||) (ba, ba') =
    if ba.Length <> ba'.Length then
      invalidArg "ba" "input Bitarrays have different length"
    { ba with Data = Array.map2 (fun e e' -> e ||| e') ba.Data ba'.Data }

  static member (&&&) (ba, ba') =
    if ba.Length <> ba'.Length then
      invalidArg "ba" "input Bitarrays have different length"
    { ba with Data = Array.map2 (fun e e' -> e &&& e') ba.Data ba'.Data }

  static member (+) (b:Bitarray, b') = b ||| b'

  static member (-) (b:Bitarray, b') = b &&& ~~~b'  

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Bitarray =

  type t = Bitarray

  let byte_size = 7
  let shift_size = 3 

  let bitToByteShift nbits = nbits >>> shift_size
  let staticBits = Array.init (byte_size+1) (fun i -> byte(1 <<< i))

  let length ba = ba.Length

  let create nbits =
    if nbits < 0 then invalidArg "nbits" "create : negative number of elements"
    { Data = (nbits + 7) |> bitToByteShift |> Array.zeroCreate
      Length = nbits
    }

  let copy ba =
    { ba with Data = Array.copy ba.Data }

  let isSet pos ba =
    if pos < 0 || pos >= ba.Length then raise <| IndexOutOfRangeException()
    ba.Data.[bitToByteShift pos] &&& staticBits.[pos &&& byte_size] <> 0uy

  let set pos ba =
    if not <| isSet pos ba then
      let newValue = ba.Data.[bitToByteShift pos] ||| staticBits.[pos &&& byte_size]
      ba.Data.[bitToByteShift pos] <- newValue

  let setAll ba =
    ba.Data |> Array.iteri (fun i _ -> ba.Data.[i] <- 255uy)

  let unset pos ba =
    if isSet pos ba then
      let newValue = ba.Data.[bitToByteShift pos] ^^^ staticBits.[pos &&& byte_size]
      ba.Data.[bitToByteShift pos] <- newValue

  let unsetAll ba =
    ba.Data |> Array.iteri (fun i _ -> ba.Data.[i] <- 0uy)

  let put pos flag ba  =
    (if flag then set else unset) pos ba

  let init nbits f =
    let res = create nbits
    for i in 0 .. nbits - 1 do
      put i (f i) res
    res

  let clear ba =
    unsetAll ba 

  let inter (b:Bitarray) b' = b &&& b'

  let union (b:Bitarray) b' = b ||| b'

  let diff (negged:Bitarray) baseBitarray = baseBitarray ^^^ negged

  let iter f ba =
    let res = create (length ba)
    for i in 0 .. (length ba) - 1 do
      f <| isSet i ba   

  let iteri f ba =
    let res = create (length ba)
    for i in 0 .. (length ba) - 1 do
      f i <| isSet i ba      

  let mapi f ba =
    let res = create (length ba)
    for i in 0 .. length ba - 1 do
      put i (f i (isSet i ba)) res
    res     

  let map f ba =
    let res = create (length ba)
    for i in 0 .. length ba - 1 do
      put i (f (isSet i ba)) res
    res  

  let setBits ba =
    [ for i in 0 .. length ba - 1 do if isSet i ba then yield i ]

  let unsetBits ba =
    [ for i in 0 .. length ba - 1 do if not <| isSet i ba then yield i ]

  let toBool ba =
    [|for i in 0 .. length ba - 1 -> isSet i ba |]

  let fromBool xs =
    init (Array.length xs) (fun i -> xs.[i])    

  let toBytes ba =
    Array.copy ba.Data

  let fromBytes xs numBits =
    if (numBits + byte_size) / 8 <> (Array.length xs) then
      invalidArg "numBits" "fromBytes : data inconsistent with number of bits"
    { Data = Array.copy xs
      Length = numBits
    }

  let areAllSet ba =
    let nFull = ba.Length / 8
    let i = ref 0
    let ok = ref true
    //check bytes that could be full
    while !i < nFull && !ok do
      if ba.Data.[!i] <> 255uy then ok := false
      incr i
    //check remaining bytes
    if !ok then
      i := nFull * 8
      while !i < length ba && !ok do
        if not <| isSet !i ba then ok := false
        incr i
    !ok

  let areAllUnset ba =
    let nFull = ba.Length / 8
    let i = ref 0
    let ok = ref true
    //check bytes that could be full
    while !i < nFull && !ok do
      if ba.Data.[!i] <> 0uy then ok := false
      incr i
    //check remaining bytes
    if !ok then
      i := nFull * 8
      while !i < length ba && !ok do
        if isSet !i ba then ok := false
        incr i
    !ok

Signature

open System

type Bitarray =
  { Data : byte[]
    Length : int
  }
  //The fields are not private in the implementation file so that
  //only the functions from this module can access them.
  static member ( ~~~ ) : ba:Bitarray -> Bitarray
  static member ( ^^^ ) : ba:Bitarray * ba':Bitarray -> Bitarray
  static member ( ||| ) : ba:Bitarray * ba':Bitarray -> Bitarray
  static member ( &&& ) : ba:Bitarray * ba':Bitarray -> Bitarray

  static member ( + ) : ba:Bitarray * ba':Bitarray -> Bitarray
  static member ( - ) : ba:Bitarray * ba':Bitarray -> Bitarray

[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Bitarray =
  type t = Bitarray

  ///[create n] returns a [Bitarray] of [n] bits.
  val create : int -> t

  ///[copy barr] returns a fresh copy of [barr].
  val copy : t -> t

  ///[length barr] returns the number of bits of [barr].
  val length : t -> int

  ///[isSet nth barr] returns true if the [nth] bit of [barr] is set.
  val isSet : pos:int -> t -> bool

  ///[put nth flag barr] puts [flag] at the [nth] bit of [barr].
  val put : int -> bool -> t -> unit

  ///[init n f] returns a new [Bitarray] of [n] bits where the
  ///[n]th bit is computed as [f i].
  val init : n:int -> f:(int -> bool) -> t

  ///[set nth barr] puts [true] at the [nth] bit of [barr].
  val set : int -> t -> unit

  ///[setAll barr] puts [true] at all bits of [barr].
  val setAll : t -> unit

  ///[unset nth barr] puts [false] at the [nth] bit of [barr].
  val unset : int -> t -> unit

  ///[unsetAll barr] puts [false] at all bits of [barr].
  val unsetAll : t -> unit

  ///[clear barr] puts [false] at all bits of [barr].
  val clear : t -> unit

  ///[inter a b] returns a [Bitarray] where the bits are set at the positions where
  ///they are set both in [a] and [b].
  val inter : t -> t -> t

  ///[Inter a b] returns a [Bitarray] where the bits are set at the positions where
  ///they are set in [a] and / or in [b].
  val union : t ->t -> t

  ///[diff negged base] returns a [Bitarray] where the bits are set at the positions where
  ///they are set in [base] but not in [negged].
  val diff : negged:t -> baseBitarray:t -> t

  ///[iter f barr] applies [f bit] to every bit of [barr] where each bit
  ///is represented by a boolean value.
  val iter : (bool -> unit) -> t -> unit

  ///[iteri f barr] applies [f i bit] to every bit of [barr] where each bit
  ///is represented by a boolean value.
  ///[f] varies with the position of the bit. That is, [f i] is applied to the [i]th bit.
  val iteri : (int -> bool -> unit) -> t -> unit

  ///[map f barr] returns a new [Bitarray] by applying [f] to every bit of [barr]
  ///where each bit is represented by a boolean value.
  val map : (bool -> bool) -> t -> t

  ///[mapi f barr] returns a new [Bitarray] by applying [f i] to the [i]th bit of [barr]
  ///for [i] in [0 .. Length barr]. Each bit is represented by a boolean value.
  val mapi : (int -> bool -> bool) -> t -> t

  ///[setBits barr] returns the indexes of the set bits of [barr].
  val setBits : t -> list<int>

  ///[unsetBits barr] returns the indexes of the set bits of [barr].
  val unsetBits : t -> list<int>

  ///[toBool barr] returns an array of boolean values where the [i]th value is true
  ///if the [i]th bit of [barr] is set.
  val toBool : t -> bool[]

  ///[fromBool boolarr] returns a [Bitarray] where the [i]th bit is set
  ///if the [i]th boolean of [boolarr] is true.
  val fromBool : bool[] -> t

  ///[toBytes barr] returns a byte[] where [true; false; ... false] translates to [1uy].
  val toBytes : t -> byte[]

  ///[fromBytes barr numBits] returns a [Bitarray] where [1uy] translates to
  ///[true; false; ... false].
  ///It ensures the number of bytes in [barr] is consistent with the expected number of bits.
  val fromBytes : byte [] -> len:int -> t

  ///[areAllSet barr] returns true if all bits of [barr] are unset.
  val areAllUnset : t -> bool

  ///[areAllSet barr] returns true if all bits of [barr] are set.
  val areAllSet : t -> bool
Share :
  • email
  • Facebook
  • Google Buzz
  • Yahoo! Buzz
  • Digg
  • del.icio.us
  • Add to favorites
  • Print

Leave a Reply

You must be logged in to post a comment.