# new in 2.3: object oriented view interface

source defs_mk.tcl

  proc fill {v prop args} {
    foreach x $args {
      $v insert end $prop $x
    }
  }

  proc dump {v} {
    set n [$v size]
    set r {}
    for {set i 0} {$i < $n} {incr i} {
      set l {}
      foreach {x y} [$v get $i] {
        lappend l $y
      }
      lappend r $l
    }
    return $r
  }

T 1 {basic view operations} {
  mk::file open db [F].mk
  mk::view layout db.a {s i:I}
  set v [mk::view open db.a v]
  C $v v

  C [v info] {s i:I}

  for {set i 0} {$i < 100} {incr i} {
    v insert end s s$i/$i i $i
  }

  C [v size] 100
  C [v get 0] {s s0/0 i 0}
  C [v get 1] {s s1/1 i 1}
  C [v get 99] {s s99/99 i 99}

  C [v get 9] {s s9/9 i 9}
  C [v get 9 s] s9/9
  C [v get 9 i] 9

  C [v find s s12/12] 12
  catch {v find s blah} e
  C $e {not found}

  C [v find i 23] 23
  C [catch {v find i -1}] 1

    # views opened more than once are shared
  set w [mk::view open db.a]
  C [$w size] 100
  $w insert end s wow i -1

  C [v size] 101
  C [v get end] {s wow i -1}

  $w close
  v close
}

T 2 {view modifications} {
  mk::file open db [F].mk
  mk::view layout db.a i:I
  mk::view open db.a v

  v insert end i 1
  v insert end i 2
  v insert 1 i 3
  v insert end i 4
  
  C [v size] 4
  foreach {x y} {0 1  1 3  2 2  3 4} {
    C [v get $x i] $y
  }

  v delete 1
  v delete [expr {[v size]-1}]
  
  C [v size] 2
  foreach {x y} {0 1  1 2} {
    C [v get $x i] $y
  }

  v set end i 5
  v set 0 i 6

  foreach {x y} {0 6  1 5} {
    C [v get $x i] $y
  }

  v close
}

T 3 {view operators} {
  mk::file open db [F].mk
  mk::view layout db.a i:I
  mk::view layout db.b i:I
  mk::view layout db.c j:I
  mk::view layout db.d k:I
  mk::view open db.a v1
  mk::view open db.b v2
  mk::view open db.c v3
  mk::view open db.d v4
  
  fill v1 i 1 2 3 4 5
  fill v2 i 3 4 5 6 7
  fill v3 j 3 4 5 6 7
  fill v4 k 1 2 1

  set v [v1 view clone]
  C [$v size] 0
  C [$v info] i:I
  $v close

  set v [v1 view copy]
  C [dump $v] {1 2 3 4 5}
  $v close

  set v [v1 view concat v2]
  C [dump $v] {1 2 3 4 5 3 4 5 6 7}
  $v close

  set v [v1 view different v2]
  C [dump $v] {1 2 6 7}
  $v close

  set v [v1 view minus v2]
  C [dump $v] {1 2}
  $v close

  set v [v1 view intersect v2]
  C [dump $v] {3 4 5}
  $v close

  set v [v1 view union v2]
  C [dump $v] {1 2 3 4 5 6 7}
  $v close

  set v [v1 view pair v3]
  C [dump $v] {{1 3} {2 4} {3 5} {4 6} {5 7}}
  $v close

  set v [v2 view range 2 3]
  C [dump $v] {5 6}
  $v close

  set v [v2 view range 3 end]
  C [dump $v] {6 7}
  $v close

  set v [v2 view range 2 4 -1]
  C [dump $v] {7 6 5}
  $v close

  set v [v2 view map v4]
  C [dump $v] {4 5 4}
  $v close

  set v [v4 view unique]
  C [dump $v] {1 2}
  $v close

  v1 close
  v2 close
  v3 close
  v4 close
}

T 4 {hash view} {
  mk::file open db [F].mk
  mk::view layout db.data {k:I v:S}
  mk::view layout db.map {_H:I _R:I}
  mk::view open db.data v1
  mk::view open db.map v2
  rename [v1 view hash v2 1] v3

  v3 insert end k 1 v one
  v3 insert end k 2 v two
  v3 insert 1 k 3 v three
  v3 insert 1 k 4 v four
  v3 insert 1 k 5 v five

  C [dump v3] {{1 one} {5 five} {4 four} {3 three} {2 two}}

  C [v3 find k 3] 3
  C [v3 find v three] 3

  v3 delete 1

  C [v3 find k 3] 2
  C [v3 find v three] 2

  v3 delete 2

  C [catch {v3 find k 3}] 1
  C [catch {v3 find v three}] 1

  C [dump v3] {{1 one} {4 four} {2 two}}

  C [v3 find k 1] 0
  C [v3 find k 4] 1
  C [v3 find k 2] 2

  v3 set 1 v nine

  C [dump v3] {{1 one} {4 nine} {2 two}}

  v3 set 1 k 9

  C [dump v3] {{1 one} {9 nine} {2 two}}

  C [v3 find k 1] 0
  C [v3 find k 9] 1
  C [v3 find k 2] 2

  C [catch {v3 find k 4}] 1

  v3 set 1 k 1 v clobber

  C [dump v3] {{1 nine} {2 clobber}}

  # WARNING, the above is a bug, the result *should* have been:
  # C [dump v3] {{1 clobber} {2 two}}
  # but due to a nasty problem, a different row gets clobbered
  # 
  # There are more such problems, i.e. with a 2-column key, when
  # changing the key, the intermediate state will be stored and
  # can delete an unrelated row due to the above deleting effect
  #
  # The advice for now is: DO NOT change keys, or ONLY the key
  # property in a single-prop hash mapping.  In the above case,
  # "v3 set 1 k 1" would've worked fine (deleting the other row).
  
  v3 close
  v2 close
  v1 close
}

T 5 {blocked view} {
  mk::file open db [F].mk
  mk::view layout db.data {{_B {k:I v:D}}}
  mk::view open db.data v1
  rename [v1 view blocked] v2

  set n 2500
  set nb 4  ;# number of buckets, depends on blocking algorithm

  for {set i 0} {$i < $n} {incr i} {
    v2 insert end k $i v $i.$i
  }

  C [v2 size] $n
  C [v1 size] $nb

    # check that all blocks combined have the same number of rows
  set t 0
  for {set j 0} {$j < $nb} {incr j} {
    #puts [mk::view size db.data!$j._B]
    incr t [mk::view size db.data!$j._B]
  }
  C $t $n

  C [v2 find k 3] 3
  C [v2 find v 3.3] 3

  v2 delete 1

  C [v2 find k 3] 2
  C [v2 find v 3.3] 2

  v2 delete 2

  C [catch {v2 find k 3}] 1
  C [catch {v2 find v 3.3}] 1

  set w [v2 view range 0 3]
  C [dump $w] {{0 0.0} {2 2.2} {4 4.4} {5 5.5}}
  $w close

  v2 insert 2 k 3 v 3.3
  v2 insert 1 k 1 v 1.1

  for {set i 0} {$i < [v2 size]} {incr i} {
    v2 delete $i
  }

  for {set i 0} {$i < [v2 size]} {incr i} {
    C [v2 get $i k] [expr {2*$i+1}]
  }

  A {[v1 size] < $nb}

  set t 0
  for {set j 0} {$j < [v1 size]} {incr j} {
    #puts [mk::view size db.data!$j._B]
    incr t [mk::view size db.data!$j._B]
  }
  C $t [v2 size]
  
  v2 close
  v1 close
}

T 6 {ordered view} {
  mk::file open db [F].mk
  mk::view layout db.data {k:I v:S}
  mk::view open db.data v1
  rename [v1 view ordered 1] v2

  v2 insert end k 1 v one
  v2 insert end k 5 v five
  v2 insert end k 4 v four
  v2 insert end k 3 v three
  v2 insert end k 2 v two

  C [dump v2] {{1 one} {2 two} {3 three} {4 four} {5 five}}

  C [v2 find k 3] 2
  C [v2 find v three] 2

  v2 delete 1

  C [v2 find k 3] 1
  C [v2 find v three] 1

  v2 delete 1

  C [catch {v2 find k 3}] 1
  C [catch {v2 find v three}] 1

  C [dump v2] {{1 one} {4 four} {5 five}}

  C [v2 find k 1] 0
  C [v2 find k 4] 1
  C [v2 find k 5] 2

  v2 set 1 v nine

  C [dump v2] {{1 one} {4 nine} {5 five}}

  v2 set 1 k 9

  C [dump v2] {{1 one} {5 five} {9 nine}}

  C [v2 find k 1] 0
  C [v2 find k 5] 1
  C [v2 find k 9] 2

  C [catch {v2 find k 4}] 1

  v2 set 1 k 1 v clobber

  C [dump v2] {{1 five} {9 clobber}}

  # WARNING, same problem as described above, should have been:
  # C [dump v2] {{1 clobber} {9 nine}}
  
  v2 close
  v1 close
}

T 7 {blocked hash view} {
  mk::file open db [F].mk
  mk::view layout db.data {k:I v:S}
  mk::view layout db.map {{_B {_H:I _R:I}}}
  mk::view open db.data v1
  mk::view open db.map v2
  rename [v2 view blocked] v3
  rename [v1 view hash v3 1] v4

  v4 insert end k 1 v one
  v4 insert end k 2 v two
  v4 insert 1 k 3 v three
  v4 insert 1 k 4 v four
  v4 insert 1 k 5 v five

  C [dump v4] {{1 one} {5 five} {4 four} {3 three} {2 two}}
  C [dump v1] {{1 one} {5 five} {4 four} {3 three} {2 two}}

  C [v2 size] 2
  C [v3 size] 9

  C [v4 find k 3] 3
  C [v4 find v three] 3

  v4 delete 1

  C [v4 find k 3] 2
  C [v4 find v three] 2

  v4 delete 2

  C [catch {v4 find k 3}] 1
  C [catch {v4 find v three}] 1

  C [dump v4] {{1 one} {4 four} {2 two}}

  C [v4 find k 1] 0
  C [v4 find k 4] 1
  C [v4 find k 2] 2

  v4 set 1 v nine

  C [dump v4] {{1 one} {4 nine} {2 two}}

  v4 set 1 k 9

  C [dump v4] {{1 one} {9 nine} {2 two}}

  C [v4 find k 1] 0
  C [v4 find k 9] 1
  C [v4 find k 2] 2

  C [catch {v4 find k 4}] 1

  v4 set 1 k 1 v clobber

  C [dump v4] {{1 nine} {2 clobber}}

  # WARNING, same problem as described above, should have been:
  # C [dump v4] {{1 clobber} {2 two}}

  for {set x 100} {$x <= 999} {incr x} {
    v4 insert end k $x v v$x
  }

  # check that the entries all get found
  for {set x 100} {$x <= 999} {incr x} {
    C [expr {$x-98}] [v4 find k $x]
  }

  C [v2 size] 4
  C [v3 size] 2049
  
  v4 close
  v3 close
  v2 close
  v1 close
}

Q 8
