diff --git a/lib/Testing.fram b/lib/Testing.fram index b1a5b6b9..fabc7354 100644 --- a/lib/Testing.fram +++ b/lib/Testing.fram @@ -574,4 +574,4 @@ section pub let testExit {~__file__, ~__line__, ?msg : String} () = log' LT_Exit (msg.unwrapOr "Test terminated early."); testExit () -end +end \ No newline at end of file diff --git a/lib/Vector.fram b/lib/Vector.fram new file mode 100644 index 00000000..12ec047a --- /dev/null +++ b/lib/Vector.fram @@ -0,0 +1,150 @@ +## ## Vector library +{# +This file is part of DBL, released under MIT license. +See LICENSE for details. +#} + +import open Mutable +import open /Base/Bool +import open /List + +abstr data Vector E T = Vector of + { content : Ref E (Array E T) + , size : Ref E Int + , capacity : Ref E Int + , mut : Mut E + } + +parameter E +parameter T + +# Helper methods for vector implementation +method getContentAt (Vector {content} : Vector E T) (n : Int) = + content.get.get n + +method setContentAt (Vector {content} : Vector E T) (n : Int) (x : T) = + content.get.at n := x + +method setContent (Vector {content} : Vector E T) newContent = + content := newContent + +method mut (Vector {mut} : Vector E T) = mut + +let getSmallestUpperPow2 (n : Int) = + let n = if n == 0 then 1 else n in + let n = n-1 in + let n = n ||| (n>>1) in + let n = n ||| (n>>2) in + let n = n ||| (n>>4) in + let n = n ||| (n>>8) in + let n = n ||| (n>>16) in + let n = n ||| (n>>32) in + n+1 + +{## Creates vector of given size. ##} +pub let makeEmptyVector {T, ~mut : Mut E} (size : Int) = + let capacity = (getSmallestUpperPow2 size) in + Vector + { content = ~mut.ref (~mut.makeArray capacity ((extern dbl_magic : Int -> T) 0)) + , size = ~mut.ref size + , capacity = ~mut.ref capacity + , mut = ~mut + } + +{## Creates a vector of a given size filled with a given value v. ##} +pub let makeVector {T, ~mut : Mut E} (size : Int) (v : T) = + let capacity = (getSmallestUpperPow2 size) in + Vector + { content = ~mut.ref (~mut.makeArray capacity v) + , size = ~mut.ref size + , capacity = ~mut.ref capacity + , mut = ~mut + } + +{## Returns vector of given size. ##} +pub method capacity (Vector {capacity} : Vector E T) = capacity.get + +method setCapacity (Vector {capacity} : Vector E T) (n : Int) = capacity := n + +{## Returns the number of elements. ##} +pub method size (Vector {size} : Vector E T) = size.get + +method setSize (Vector {size} : Vector E T) (n : Int) = size := n + +{## Returns true if the vector is empty. ##} +pub method empty (Vector {size} : Vector E T) = size.get == 0 + +{## + Resizes the vector to contain n elements. + If n is smaller than the current size, the vector is truncated. + If n is greater than size, the vector is expanded. +##} +pub method resize {T} (self : Vector E T) (n : Int) = + assert {msg="Capacity overflow"} (n >= 0 && n <= maxArrayLength); + self.setSize n; + if n > self.capacity then ( + let newCapacity = min (self.capacity * 2) maxArrayLength + let newContent = self.mut.initArray newCapacity (fn i => + if i < self.capacity then self.getContentAt i + else ((extern dbl_magic : Int -> T) 0)) in + self.setContent newContent; + self.setCapacity newCapacity + ) else if n * 2 < self.capacity then ( + let newCapacity = self.capacity / 2 + let newContent = self.mut.initArray newCapacity (fn i => + if i <= self.size then self.getContentAt i + else ((extern dbl_magic : Int -> T) 0)) in + self.setContent newContent; + self.setCapacity newCapacity + ) else () + +{## Get the nth element of a vector. ##} +pub method get (self : Vector E T) (n : Int) = + assert {msg="Index out of range"} (n >= 0 && n < self.size); + self.getContentAt n + +{## Set the nth element of a vector. ##} +pub method set (self : Vector E T) (n : Int) (x : T) = + assert {msg="Index out of range"} (n >= 0 && n < self.size); + self.setContentAt n x + +{## Access the first element. ##} +pub method front (self : Vector E T) = self.get 0 + +{## Access the last element. ##} +pub method back (self : Vector E T) = self.get (self.size - 1) + +{## Add element at the end. ##} +pub method pushBack (self : Vector E T) (x : T) = + self.resize (self.size + 1); + self.set (self.size - 1) x + +{## Removes the last element. ##} +pub method popBack (self : Vector E T) = + assert {msg="Vector is empty"} (self.empty.neg); + self.resize (self.size - 1) + +{## Converts a list into a vector. ##} +pub let fromList {~mut : Mut E} (l : List T) = + let vec = makeEmptyVector {T} l.length in + let rec aux (n : Int) acc = + match acc with + | [] => vec + | x :: xs => + vec.set n x; + aux (n + 1) xs + end + in aux 0 l + +{## Converts a vector into a list. ##} +pub method toList (self : Vector E T) = + let rec aux (n : Int) acc = + if n < 0 then acc + else aux (n - 1) (self.get n :: acc) + in aux (self.size - 1) [] + +{## Clears the vector. ##} +pub method clear (self : Vector E T) = + self.setSize 0; + self.setCapacity 0; + self.setContent (self.mut.makeArray 0 ((extern dbl_magic : Int -> T) 0)) \ No newline at end of file diff --git a/test/stdlib/Vector.fram b/test/stdlib/Vector.fram new file mode 100644 index 00000000..241ad933 --- /dev/null +++ b/test/stdlib/Vector.fram @@ -0,0 +1,69 @@ +import open Mutable +import open /Vector +import open /String +import open Testing + +let _ = testSuite "vector" (fn _ => + + testCase "size and capacity" (fn _ => + let vec = makeEmptyVector {T=Int} 4 in + let sz = vec.size in + let cap = vec.capacity in + assertEqS sz 4; + assertEqS cap 4); + + testCase "front and back" (fn _ => + let vec = makeEmptyVector {T=Int} 4 in + vec.set 0 1; + vec.set 1 2; + vec.set 2 3; + vec.set 3 4; + vec.pushBack 5; + let fst = vec.front in + let lst = vec.back in + assertEqS fst 1; + assertEqS lst 5); + + testCase "get" (fn _ => + let vec = makeEmptyVector {T=Int} 0 in + vec.pushBack 6; + vec.pushBack 7; + vec.pushBack 8; + vec.pushBack 9; + let x = vec.get 1 in + let y = vec.get 3 in + assertEqS x 7; + assertEqS y 9); + + testCase "clear" (fn _ => + let vec = makeEmptyVector {T=Int} 4 in + vec.clear; + let sz = vec.size in + let cap = vec.capacity in + assertEqS sz 0; + assertEqS cap 0); + + testCase "from/to list" (fn _ => + let xs = [1,2,3,4] in + let vec = fromList xs in + let x = vec.get 0 in + let y = vec.get 2 in + let ys = vec.toList in + let z = ys.hd in + assertEqS x 1; + assertEqS y 3; + assertEqS z (Some 1)); + + testCase "vector of strings" (fn _ => + let vec = makeEmptyVector {T=String} 2 in + vec.set 0 "abc"; + vec.set 1 "ab"; + vec.pushBack "a"; + let fst = vec.front in + let lst = vec.back in + assertEqS fst "abc"; + assertEqS lst "a"); + + testCase "filled vector" (fn _ => + let vec = makeVector {T=String} 42 "" in + assertEqS (vec.get 0 + "A") "A")) \ No newline at end of file