Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/Testing.fram
Original file line number Diff line number Diff line change
Expand Up @@ -574,4 +574,4 @@ section
pub let testExit {~__file__, ~__line__, ?msg : String} () =
log' LT_Exit (msg.unwrapOr "Test terminated early.");
testExit ()
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line character was removed from non changed file. First, we should avoid unnecessary changes like this, second, we use UNIX convention, where each nonempty text file ends with newline character.

150 changes: 150 additions & 0 deletions lib/Vector.fram
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see, why the capacity of a vector must be a power of 2 (except for the empty vector). Maybe it would be better to allow any capacity, and multiply it by 2 (or any other constant), when the vector must be resized. This would allow to convert lists to vectors without unnecessary memory overhead, and I thing would slightly simplify the code. Moreover, I don't see why the vector is never resized down.

Original file line number Diff line number Diff line change
@@ -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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extern dbl_magic is used to create an element of arbitrary type. This would be correct unless the public interface allows reading such elements. For example,

let v = makeVector {T=String, ~mut=ioMut} 42 in v.get 0 + "A"

results in runtime error. To avoid such problems we should forbid creating non-empty vectors without providing the default value. Creating empty vectors is still ok.

, 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))
69 changes: 69 additions & 0 deletions test/stdlib/Vector.fram
Original file line number Diff line number Diff line change
@@ -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"))