anchor.diff:      list_to_binary(integer_to_list(X));
   to_json_type(_, void) -> <<"ok">>;
   to_json_type(X, {type,_,<<"uint", _/binary>>,_}) when is_integer(X), X >= 0 ->
  -    list_to_binary(integer_to_list(X));
  -to_json_type(false, {type,_,<<"boolean">>,_}) -> <<"0">>;
  -to_json_type(true, {type,_,<<"boolean">>,_}) -> <<"1">>;
  -to_json_type(X, {type,_,<<"enumeration">>, En} = T) ->
  -    case [lists:keyfind(value,1,I1) || {enum,_,E1,I1} <- En,
  -				       E1 == X] of
  -	[{value,_,V,_}] ->
  -	    V;
  -	[] ->
  -	    error({type_error, [X, T]})
  +    X;
  +to_json_type(false, {type,_,<<"boolean">>,_}) -> false;
  +to_json_type(true, {type,_,<<"boolean">>,_}) -> true;
  +to_json_type(X0, {type,_,<<"enumeration">>, En} = T) ->
  +    %% We always return the key (binary) - not the value (integer).
  +    %% This seems to be in keeping with how NETCONF does it.
  +    X = if is_integer(X0) -> list_to_binary(integer_to_list(X0));
  +           is_binary(X0)  -> X0;
  +           is_list(X0) -> list_to_binary(X0)
  +        end,
  +    case find_enum(En, X) of
  +        [] -> error({type_error, [X0, T]});
  +        [{Key, _}] ->
  +            Key;
  +        [{K1, V1}, {K2, V2}] ->
  +            %% Either V1 or V2 must be equal to X, or it's an error
  +            if V1 == X -> K1;
  +               V2 == X -> K2;
  +               true -> error({type_error, [X0, T]})
  +            end
       end;
   to_json_type(X, {type, _, <<"union">>, Ts} = Type) ->
       to_json_type_union(Ts, X, Type);
  +to_json_type(_, {type, _, <<"empty">>, _}) -> <<>>;
   to_json_type(X, _) ->
       X.
   
  
positive.code: 
  i(I) ->
      lists:duplicate(I*4+4,$\s) .
  
negative.code: 
  record_defs(_RPCs, _M) ->
      [].
  
  remove_yang_info([{K, [], _}|T]) ->
      [{K, []} | remove_yang_info(T)];
  remove_yang_info([{K, [H1|_] = Ch, _}|T]) when is_tuple(H1) ->
      [{K, remove_yang_info(Ch)} | remove_yang_info(T)];
  remove_yang_info([{K, V, _}|T]) ->
      [{K, V} | remove_yang_info(T)];
  remove_yang_info([{_,_} = H|T]) ->
      [H | remove_yang_info(T)];
  remove_yang_info([]) ->
      [].
  
  validate_request({call, Method, {struct, Request}}, [{module,_,M,Elems}]) ->
      ShortMethod = case re:split(Method, ":", [{return, binary}]) of
                        [M, Meth] ->
                            Meth;
                        [Meth]    -> Meth;
                        [_OtherM, _] ->
                            error({unknown_method, Method})
                    end,
      %% we have the right module
      case find_rpc(Elems, ShortMethod) of
          error ->
              {error, {unknown_method, Method}};
          {rpc, _, _ActualMethod, Data} ->
              {input,_,_,InputElems} = lists:keyfind(input, 1, Data),
              validate_rpc_request(InputElems, Request)
      end.
  
  find_rpc([{rpc, _, Meth, _} = RPC|_], Meth) ->
      RPC;
  find_rpc([_|Elems], Meth) ->
      find_rpc(Elems, Meth);
  find_rpc([], _) ->
      error.
  
  validate_rpc_request(Req, Data) ->
      try begin
              {ValidReq, Meta} = validate_rpc_request(Req, Data, [], []),
              {ok, ValidReq, Meta}
          end
      catch
          throw:Error ->
              {error, Error}
      end.
  
  validate_rpc_request([{Leaf,_,Key,Info}|Elems], Data, Acc, Meta)
    when Leaf == leaf; Leaf == anyxml ->
      case keytake(Key, Data) of
          {{_, Value}, Data1} ->
              Value1 = convert_elem(Leaf, Key, Value, Info),
              validate_rpc_request(
                Elems, Data1,
                [{to_atom(Key), Value1, Info}|Acc], Meta);
          false ->
              validate_rpc_request_missing(Leaf, Elems, Key, Info, Data, Acc, Meta)
      end;
  validate_rpc_request([{Stmt, _, Key, Info}|Elems], Data, Acc, Meta)
    when Stmt==list; Stmt=='leaf-list' ->
      case keytake(Key, Data) of
          false ->
              validate_rpc_request_missing(Stmt, Elems, Key, Info, Data, Acc, Meta);
          {Found, Data1} ->
              validate_rpc_request(
                Elems, Data1,
                [validate_rpc_elem(Stmt, Found, Info)|Acc], Meta)
      end;
  validate_rpc_request([Other|Elems], Data, Acc, Meta) ->
      validate_rpc_request(Elems, Data, Acc, [Other|Meta]);
  validate_rpc_request([], [], Acc, Meta) ->
      {lists:reverse(Acc), lists:reverse(Meta)};
  validate_rpc_request([], [_|_] = Unknown, _, _) ->
      throw({invalid_params, {unknown_params, [element(1, U) || U <- Unknown]}}).
  
  validate_rpc_elem(list, {Key, {array, ListElems}}, Info) ->
      case lists:map(fun({struct, StructElems}) ->
                             validate_rpc_request(Info, StructElems, [], [])
                     end, ListElems) of
          [] ->
              check_min_elements([], Key, Info),
              %% Meta = [I || I <- Info,
              %%           not lists:member(element(1,I),
              %%                            [leaf, anyxml, list, 'leaf-list'])],
              {to_atom(Key), [], Info};
          [_|_] = List ->
              Len = length(List),
              check_min_elements_(Len, Key, lists:keyfind('min-elements',1,Info)),
              check_max_elements_(Len, Key, lists:keyfind('max-elements',1,Info)),
              {to_atom(Key), [[{X,V} || {X,V,_} <- L]
                                             || {L,_} <- List], Info}
      end;
  validate_rpc_elem('leaf-list', {Key, {array, ListElems}}, Info) ->
      {type,_,_,_} = Type = lists:keyfind(type, 1, Info),
      List =  [convert_elem(leaf, Key, Value, Type, Info) || Value <- ListElems],
      Length = length(List),
      check_min_elements_(Length, Key, lists:keyfind('min-elements',1,Info)),
      check_max_elements_(Length, Key, lists:keyfind('max-elements',1,Info)),
      {to_atom(Key), List, Info};
  validate_rpc_elem(Stmt, {Key, Other}, _Info)
    when Stmt==list; Stmt=='leaf-list' ->
      throw({invalid_params, {mismatch, [Key, Other, array]}}).
  
  check_max_elements(List, Key, Info) ->
      case lists:keyfind('max-elements', 1 , Info) of
          {_, _, _, _} = Max ->
              Length = length(List),
              check_max_elements_(Length, Key, Max);
          false ->
              ok
      end.
  
  check_max_elements_(_, _Key, {'max-elements',_,<<"unbounded">>,_}) ->
      ok;
  check_max_elements_(Length, Key, {'max-elements',_,Max,_}) ->
      case list_to_integer(binary_to_list(Max)) of
          N when N < Length ->
              throw({invalid_params, {'max-elements', [Key, N, Length]}});
          _ ->
              ok
      end;
  check_max_elements_(_, _, false) ->
      ok.
  
  check_min_elements(List, Key, Info) ->
      case lists:keyfind('min-elements', 1 , Info) of
          {_, _, _, _} = Max ->
              Length = length(List),
              check_min_elements_(Length, Key, Max);
          false ->
              ok
      end.
  
  check_min_elements_(Length, Key, {'min-elements',_,Max,_}) ->
      case list_to_integer(binary_to_list(Max)) of
          N when N > Length ->
              throw({invalid_params, {'min-elements', [Key, N, Length]}});
          _ ->
              ok
      end;
  check_min_elements_(_, _, false) ->
      ok.
  
  validate_rpc_request_missing(Stmt, Elems, Key, Info, Data, Acc, Meta)
    when Stmt==list; Stmt=='leaf-list' ->
      case lists:keyfind('min-elements', 1, Info) of
          {_, _, <<"0">>, _} ->
              validate_rpc_request(Elems, Data, Acc, Meta);
          false ->
              validate_rpc_request(Elems, Data, Acc, Meta);
          _ ->
              throw({invalid_params, {required, [Key|also_missing(Elems, Data)]}})
      end;
  validate_rpc_request_missing(Stmt, Elems, Key, Info, Data, Acc, Meta) ->
      case lists:keyfind(mandatory,1,Info) of
          {mandatory,true} ->
              %% Mandatory leafs MUST NOT have a default statement
              %% (RFC6020, 7.6.4)
              AlsoMissing = also_missing(Elems, Data),
              throw({invalid_params, {required, [Key|AlsoMissing]}});
          _ when Stmt==leaf ->
              case lists:keyfind(default, 1, Info) of
                  {default, _, Def, _} ->
                      Value = convert_elem(Stmt, Key, Def, Info),
                       validate_rpc_request(
                         Elems, Data,
                         [{to_atom(Key), Value, Info}|Acc], Meta);
                  false ->
                      validate_rpc_request(Elems, Data, Acc, Meta)
              end;
          _ ->
              %% FIXME: check whether this is in fact the right action for all
              %% non-leaf statements
              validate_rpc_request(Elems, Data, Acc, Meta)
      end.
  
  also_missing(Elems, Env, Data) ->
      also_missing(Elems, Data ++ Env).
  
  also_missing([{Stmt,_,Key,I}|T], Data) ->
      case (req_mandatory(Stmt, I) andalso not_in_data(Key, Data)) of
          true ->
              [Key|also_missing(T, Data)];
          false ->
              also_missing(T, Data)
      end;
  also_missing([], _) ->
      [].
  
  req_mandatory(Stmt, I) when Stmt==leaf; Stmt==anyxml ->
      req_mandatory_elem(I);
  req_mandatory(Stmt, I) when Stmt==list; Stmt=='leaf-list' ->
      req_non_zero_list(I).
  
  req_mandatory_elem(I) ->
      case lists:keyfind(mandatory, 1, I) of
          {_, _, <<"true">>, _} ->
              true;
          _ ->
              false
      end.
  
  req_non_zero_list(I) ->
      case lists:keyfind('min-elements', 1, I) of
          {_, _, <<"0">>, _} ->
              false;
          false ->
              false;
          _ ->
              true
      end.
  
  not_in_data(Key, Data) ->
      not lists:any(fun({K,_}) ->
                            comp(Key, K)
                    end, Data).
  
  convert_elem(Stmt, Key, Value, Info) ->
      convert_elem(Stmt, Key, Value, lists:keyfind(type,1,Info), Info).
  
  convert_elem(anyxml, _, Value, _, _) ->
      Value;
  convert_elem(leaf, Key, Value, Type, _Info) ->
      case yang:check_type(Value, Type) of
          {true, NewVal} ->
              NewVal;
          false ->
              throw({invalid_params, {wrong_type, [Key, Value, Type]}})
      end.
  
  keytake(K, Data) ->
      case keyfind(K, Data) of
          false ->
              false;
          Found ->
              {Found, Data -- [Found]}
      end.
  
  keyfind(A, [H|T]) when is_tuple(H) ->
      K = element(1, H),
      case comp(A,K) of
          true ->
              H;
          false ->
              keyfind(A, T)
      end;
  keyfind(_, []) ->
      false.
  
  comp(A, A) -> true;
  comp(A, B) when is_binary(A), is_list(B) ->
      binary_to_list(A) == B;
  comp(A, B) when is_binary(A), is_atom(B) ->
      A == atom_to_binary(B, latin1);
  comp(_, _) ->
      false.
  
  %% ===================================================================
  %% data_to_json(Yang, Attrs, Reply) -> JSONTerm
  %% ===================================================================
  data_to_json([{Stmt, _, Key, Info} = Elem|T], Env, Data)
    when Stmt==leaf; Stmt==anyxml; Stmt==list; Stmt=='leaf-list' ->
      case keyfind(Key, Data) of
          false ->
              case keyfind(Key, Env) of
                  Found when is_tuple(Found) ->
                      [data_to_json_(Elem, Env, element(2, Found))
                       | data_to_json(T, Env, Data)];
                  false ->
                      case req_mandatory(Stmt, Info) of
                          true ->
                              error({invalid_params,
                                     {required, [Key|
                                                 also_missing(T, Env, Data)]}});
                          false ->
                              data_to_json_default(Elem)
                                  ++ data_to_json(T, Env, Data)
                      end
              end;
          Found when is_tuple(Found) ->
              [data_to_json_(Elem, Env, element(2, Found))
               | data_to_json(T, Env, Data)]
      end;
  data_to_json({Stmt, _, Key, _} = Elem, Env, {Key1, Data})
    when Stmt==leaf; Stmt==anyxml; Stmt==list; Stmt=='leaf-list' ->
      case comp(Key, Key1) of
          true ->
              data_to_json_(Elem, Env, Data);
          false ->
              error({invalid_params, {required, [Key]}})
      end;
  data_to_json([_|T], Env, Data) ->
      data_to_json(T, Env, Data);
  data_to_json([], _, _) ->
      [].
  
  data_to_json_default({Stmt,_,Key,Info}) when Stmt==leaf; Stmt==anyxml ->
      case lists:keyfind(default, 1, Info) of
          {_, _, Default, _} ->
              [{Key, to_json_type(Default, get_type(Info))}];
          false ->
              []
      end;
  data_to_json_default({Stmt,_,Key,_}) when Stmt==list; Stmt=='leaf-list' ->
      [{Key, {array, []}}].
  
  data_to_json_({Stmt, _, Key, Info}, _Env, Data)
    when Stmt==leaf; Stmt==anyxml ->
      {Key, to_json_type(Data, get_type(Info))};
  data_to_json_({Stmt, _, Key, Info}, Env, Data)
    when Stmt==list; Stmt=='leaf-list' ->
      Data1 = case Data of
                  {array, D} when is_list(D) -> D;
                  D when is_list(D) -> D;
                  _ ->
                      error({invalid_params, {type_error, [Key, Stmt, Data]}})
              end,
      if Stmt=='leaf-list' ->
              Type = get_type(Info),
              {Key, {array, [to_json_type(X, Type) || X <- Data1]}};
         Stmt==list ->
              {Key, {array,
                     lists:map(
                       fun({struct,Elems}) ->
                               {struct, data_to_json(Info, Env, Elems)};
                          (L) when is_list(L) ->
                               {struct, data_to_json(Info, Env, L)};
                          (Other) ->
                               error({invalid_params,
                                      [{type_error,[Key,Info,Other]}]})
                       end, Data1)}}
      end.
  
  get_type(Info) ->
      {type,_,_,_} = lists:keyfind(type, 1, Info).
  
  %% ===================================================================
  
  to_json_rpc(Data, Arg, M) ->
      Imports = imports(Data, Arg),
  