anchor.diff:  is_source(Entry) -> entry_is_of_type(Entry, source).
   is_sticky(Entry) -> entry_is_of_type(Entry, sticky).
   
  -entry_nodeid(#rt_entry{node=Node}) -> nodelist:id(Node).
  -
  --spec entry_learn(rt_entry(), gb_tree()) -> gb_tree().
  -entry_learn(Entry, RT) -> 
  -    gb_trees:enter(node:id(Entry#rt_entry.node), Entry, RT)
  -    .
  -
  --spec entry_delete(EntryKey :: key(), rt()) -> rt().
  -entry_delete(EntryKey, RT) ->
  -    gb_trees:delete(EntryKey, RT).
  -
  +entry_nodeid(#rt_entry{node=Node}) -> node:id(Node).
  +
  +adjacent_pred(#rt_entry{adjacent_fingers={Pred,_Succ}}) -> Pred.
  +
  +predecessor_node(RT, Node) ->
  +    gb_trees:get(adjacent_pred(Node), get_rt_tree(RT)).
  +
  +spacing(Node) -> Node#rt_entry.spacing.
  
positive.code: %% userdevguide-begin rt_frtchord:check
  %% @doc Notifies the dht_node and failure detector if the routing table changed.
  %%      Provided for convenience (see check/5).
  -spec check(OldRT::rt(), NewRT::rt(), Neighbors::nodelist:neighborhood(),
              ReportToFD::boolean()) -> ok.
  check(OldRT, NewRT, Neighbors, ReportToFD) ->
      check(OldRT, NewRT, Neighbors, Neighbors, ReportToFD).
  
  
negative.code: %% @doc Converts the (external) representation of the routing table to a list
  %%      in the order of the fingers, i.e. first=succ, second=shortest finger,
  %%      third=next longer finger,...
  to_list(#rt_t{} = RT) ->
      SourceNode = get_source_node(RT),
      % sort
      Sorted = lists:sort(fun(A, B) -> entry_nodeid(A) =< entry_nodeid(B) end,
          [X || X <- gb_trees:values(get_rt_tree(RT))]),
      % rearrange elements: all until the source node must be attached at the end
      {Front, Tail} = lists:splitwith(fun(N) ->
                  entry_nodeid(N) =:= SourceNode end, Sorted),
      TailNoSourceNode = case Tail of
          [SourceNode] -> [];
          [SourceNode|T] -> T
      end,
      % TODO inefficient -> reimplement with foldl
      TailNoSourceNode ++ Front
      ;
  to_list(State) -> % match external RT
      RT = dht_node_state:get(State, rt),
      to_list(RT)
      .
  %% userdevguide-end rt_frtchord:to_list
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % FRT specific algorithms and functions
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  
  % @doc Filter one element from a set of nodes. Do it in a way that filters such a node
  % that the resulting routing table is the best one under all _possible_ routing tables.
  entry_filtering(RT, AllowedNodes) ->
      Spacings = [
          begin
              PredNode = predecessor_node(RT,Node),
              {Node, spacing(Node) + spacing(PredNode)}
          end || Node <- AllowedNodes],
      % remove the element with the smallest canonical spacing range between its predecessor
      % and its successor. TODO beware of numerical errors!
      {FilterEntry, _Spacing} = hd(lists:sort(
              fun ({_,SpacingA}, {_, SpacingB})
                          -> SpacingA =< SpacingB
              end, Spacings)
        ),
      entry_delete(entry_nodeid(FilterEntry), RT)
      .
  
  % @doc Delete an entry from the routing table
  entry_delete(EntryKey, RT) ->
      Tree = gb_trees:delete(EntryKey, get_rt_tree(RT)),
      RT#rt_t{nodes=Tree}.
  
  % @doc Add a new entry to the routing table. A source node is only allowed to be added
  % once.
  entry_learn(#rt_entry{type=source} = Entry, #rt_t{source=undefined} = RT) -> 
      entry_learn(Entry, RT#rt_t{source=entry_nodeid(Entry)})
      ;
  entry_learn(Entry, RT) -> 
      Nodes = gb_trees:enter(entry_nodeid(Entry), Entry, get_rt_tree(RT)),
      rt_set_nodes(RT, Nodes)
      .
  
  % @doc Combines entry learning and entry filtering.
  entry_learning_and_filtering(Entry, RT) ->
      IntermediateRT = entry_learn(Entry, RT),
      SizeOfRT = get_size(IntermediateRT),
      MaxEntries = maximum_entries(),
      if SizeOfRT >= MaxEntries ->
              AllowedNodes = [N || N <- to_list(RT),
                  not is_sticky(N) and not is_source(N)],
              entry_filtering(IntermediateRT, AllowedNodes);
          true -> IntermediateRT
      end.
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % RT and RT entry record accessors
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % @private
  
  % @doc Get the source node of a routing table
  % XXX using any() as return as dialyzer complains when using rt_entry()
  get_source_node(#rt_t{source=undefined}) ->
      exit("routing table source unknown");
  get_source_node(#rt_t{source=Node} = RT) -> gb_trees:get(Node, RT#rt_t.nodes).
  
  % @doc Get the gb_tree of the routing table containing its nodes
  get_rt_tree(#rt_t{nodes=Nodes}) -> Nodes.
  
  % @doc Check if a node exists in the routing table
  entry_exists(EntryKey, #rt_t{nodes=Nodes}) ->
      case gb_trees:lookup(EntryKey, Nodes) of
          none -> false;
          _Else -> true
      end.
  
  -spec add_entry(Entry :: rt_entry() | node:node_type(),
      entry_type(),
      RT :: rt()) -> rt().
  add_entry(#rt_entry{node=Entry}, Type, RT) ->
      add_entry(Entry, Type, RT);
  add_entry(Entry, Type, RT) ->
      NewNode = #rt_entry{
          node = Entry,
          id   = node:id(Entry),
          type = Type
      },
      entry_learning_and_filtering(NewNode, RT)
      .
  
  % @doc Add a sticky entry to the routing table
  % TODO add adjacent fingers if possible
  add_sticky_entry(Entry, RT) -> add_entry(Entry, sticky, RT).
  
  % @doc Add the source entry to the routing table
  % TODO unit test that only one source entry is allowed
  % TODO add adjacent fingers if possible
  add_source_entry(Entry, RT) -> add_entry(Entry, source, RT).
  
  % @doc Add a normal entry to the routing table
  % TODO add adjacent fingers if possible
  add_normal_entry(Entry, RT) -> add_entry(Entry, normal, RT).
  
  rt_entry_node(#rt_entry{node=N}) -> N.
  
  rt_set_nodes(RT, Nodes) ->
      RT#rt_t{nodes=Nodes}.
  
  %% is the given node the source entry?
  entry_is_of_type(#rt_entry{type=Type}, Type) -> true;
  