import Base type Error is Data: Bounds{} Empty{} Limit{} InvalidRange{} type Vec<-T: Data> is Type: Buffer{len: U32, cap: U32, depth: Nat, max: U32, slots: Array>} def Vec.maximum() -> U32: 16777216 def Vec.bounded(-T: Data, limit: U32) -> Vec: Buffer{0, 1, 0n, U32.min(limit, Vec.maximum()), Array.new(Maybe<&2,T>, 0n, None{})} def Vec.new(-T: Data) -> Vec: Vec.bounded(T, Vec.maximum()) def Vec.length(-T: Data, v: Vec) -> Vec & U32: match v: case Buffer{+n,c,d,m,a}: (Buffer{n,c,d,m,a}, n) def Vec.capacity(-T: Data, v: Vec) -> Vec & U32: match v: case Buffer{n,+c,d,m,a}: (Buffer{n,c,d,m,a}, c) def Vec.limit(-T: Data, v: Vec) -> Vec & U32: match v: case Buffer{n,c,d,+m,a}: (Buffer{n,c,d,m,a}, m) def slot_result(-T: Data, x: Maybe<&2,T>) -> Result: match x: case None{}: Fail{Bounds{}} case Some{v}: Done{v} def observed(-T: Data, n: U32, c: U32, d: Nat, m: U32, r: Array> & Maybe<&2,T>) -> Vec & Result: (a,x) = r (Buffer{n,c,d,m,a},slot_result(T,x)) def get_if(-T: Data, n: U32, c: U32, d: Nat, m: U32, a: Array>, i: U32, valid: Bool) -> Vec & Result: match valid: case False{}: (Buffer{n,c,d,m,a}, Fail{Bounds{}}) case True{}: observed(T,n,c,d,m,Array.get(Maybe<&2,T>,a,i)) def Vec.get(-T: Data, v: Vec, +index: U32) -> Vec & Result: match v: case Buffer{+n,c,d,m,a}: get_if(T,n,c,d,m,a,index,U32.is_lt(index,n)) def swap_if(-T: Data, n: U32, c: U32, d: Nat, m: U32, a: Array>, i: U32, value: T, valid: Bool) -> Vec & Result: match valid: case False{}: (Buffer{n,c,d,m,a}, Fail{Bounds{}}) case True{}: observed(T,n,c,d,m,Array.swap(Maybe<&2,T>,a,i,Some{value})) def Vec.swap(-T: Data, v: Vec, +index: U32, value: T) -> Vec & Result: match v: case Buffer{+n,c,d,m,a}: swap_if(T,n,c,d,m,a,index,value,U32.is_lt(index,n)) def discard_value(-T: Data, r: Result) -> Result: match r: case Fail{e}: Fail{e} case Done{x}: Done{Unit{}} def set_result(-T: Data, pair: Vec & Result) -> Vec & Result: (v,r) = pair (v,discard_value(T,r)) def Vec.set(-T: Data, v: Vec, index: U32, value: T) -> Vec & Result: set_result(T,Vec.swap(T,v,index,value)) # This plan is bounded by 24 doublings; caller checks minimum <= maximum. def plan_step(fuel: Nat, +cap: U32, depth: Nat, +minimum: U32, enough: Bool) -> U32 & Nat: match fuel: case 0n: (cap,depth) case 1n+p: match enough: case True{}: (cap,depth) case False{}: +next = U32.add(cap,cap) plan_step(p,next,1n+depth,minimum,U32.is_le(minimum,next)) def unpack(-A: Type, -B: Type, -R: Type, pair: A & B, f: A -> B -> R) -> R: (a,b) = pair f(a,b) def copy_slots(-T: Data, count: Nat, +i: U32, old: Array>, fresh: Array>) -> Array>: match count: case 0n: fresh case 1n+p: unpack(Array>,Maybe<&2,T>,Array>, Array.get(Maybe<&2,T>,old,i), a => x => copy_slots(T,p,U32.add(i,1),a,Array.set(Maybe<&2,T>,fresh,i,x))) def allocate(-T: Data, +n: U32, m: U32, a: Array>, plan: U32 & Nat) -> Vec & Result: (cap,+depth) = plan fresh = Array.new(Maybe<&2,T>,depth,None{}) slots = copy_slots(T,U32.to_nat(n),0,a,fresh) (Buffer{n,cap,depth,m,slots},Done{Unit{}}) def reserve_grow(-T: Data, +n: U32, c: U32, d: Nat, m: U32, a: Array>, +minimum: U32, enough: Bool) -> Vec & Result: match enough: case True{}: (Buffer{n,c,d,m,a},Done{Unit{}}) case False{}: allocate(T,n,m,a,plan_step(24n,1,0n,minimum,U32.is_le(minimum,1))) def reserve_limit(-T: Data, n: U32, +c: U32, d: Nat, m: U32, a: Array>, +minimum: U32, valid: Bool) -> Vec & Result: match valid: case False{}: (Buffer{n,c,d,m,a},Fail{Limit{}}) case True{}: reserve_grow(T,n,c,d,m,a,minimum,U32.is_le(minimum,c)) def Vec.reserve(-T: Data, v: Vec, +minimum: U32) -> Vec & Result: match v: case Buffer{n,c,d,+m,a}: reserve_limit(T,n,c,d,m,a,minimum,U32.is_le(minimum,m)) def push_ready(-T: Data, v: Vec, value: T, r: Result) -> Vec & Result: match v: case Buffer{+n,c,d,m,a}: match r: case Fail{e}: (Buffer{n,c,d,m,a},Fail{e}) case Done{u}: (Buffer{U32.add(n,1),c,d,m,Array.set(Maybe<&2,T>,a,n,Some{value})},Done{Unit{}}) def push_reserved(-T: Data, value: T, pair: Vec & Result) -> Vec & Result: (v,r) = pair push_ready(T,v,value,r) def push_if(-T: Data, +n: U32, c: U32, d: Nat, m: U32, a: Array>, value: T, valid: Bool) -> Vec & Result: match valid: case False{}: (Buffer{n,c,d,m,a},Fail{Limit{}}) case True{}: push_reserved(T,value,Vec.reserve(T,Buffer{n,c,d,m,a},U32.add(n,1))) def Vec.push(-T: Data, v: Vec, value: T) -> Vec & Result: match v: case Buffer{+n,c,d,+m,a}: push_if(T,n,c,d,m,a,value,U32.is_lt(n,m)) def pop_if(-T: Data, n: U32, c: U32, d: Nat, m: U32, a: Array>, empty: Bool) -> Vec & Result: match empty: case True{}: (Buffer{n,c,d,m,a},Fail{Empty{}}) case False{}: +last = U32.sub(n,1) observed(T,last,c,d,m,Array.swap(Maybe<&2,T>,a,last,None{})) def Vec.pop(-T: Data, v: Vec) -> Vec & Result: match v: case Buffer{+n,c,d,m,a}: pop_if(T,n,c,d,m,a,U32.is_eq(n,0)) def prepend_slot(-T: Data, acc: List, x: Maybe<&2,T>) -> List: match x: case None{}: acc case Some{v}: Con{v,acc} # Build from the right so the result is ordered without quadratic append. def collect(-T: Data, count: Nat, last: U32, a: Array>, acc: List) -> Array> & List: match count: case 0n: (a,acc) case 1n+p: +i = U32.sub(last,1) unpack(Array>,Maybe<&2,T>,Array> & List, Array.get(Maybe<&2,T>,a,i), b => x => collect(T,p,i,b,prepend_slot(T,acc,x))) def slice_collected(-T: Data, n: U32, c: U32, d: Nat, m: U32, pair: Array> & List) -> Vec & Result>: (a,xs) = pair (Buffer{n,c,d,m,a},Done{xs}) def list_collected(-T: Data, n: U32, c: U32, d: Nat, m: U32, pair: Array> & List) -> Vec & List: (a,xs) = pair (Buffer{n,c,d,m,a},xs) def slice_if(-T: Data, n: U32, c: U32, d: Nat, m: U32, a: Array>, start: U32, +end: U32, valid: Bool) -> Vec & Result>: match valid: case False{}: (Buffer{n,c,d,m,a},Fail{InvalidRange{}}) case True{}: slice_collected(T,n,c,d,m,collect(T,U32.to_nat(U32.sub(end,start)),end,a,Nil{})) def Vec.slice(-T: Data, v: Vec, +start: U32, +end: U32) -> Vec & Result>: match v: case Buffer{+n,c,d,m,a}: slice_if(T,n,c,d,m,a,start,end,Bool.and(U32.is_le(start,end),U32.is_le(end,n))) def Vec.to_list(-T: Data, v: Vec) -> Vec & List: match v: case Buffer{+n,c,d,m,a}: list_collected(T,n,c,d,m,collect(T,U32.to_nat(n),n,a,Nil{}))