let train_in_place alpha ?(decay=0.0) () net (input,example) =
   (* unrolled loop for the training *)
   let loop rep activation =
      (* this loop handles layers 2 to n *)
      let rec loop2 rep activation =
         match rep with
            neurons :: rest_rep ->
               begin
                  if Array.length (get_synapses neurons.(0)) != Array.length activation +1 then
                     raise (Invalid_argument "Net size does not match input")
                  else ();
                  (* add the bias *)
                  let activation = Array.append [|1.0|] activation in
                  (* calculate the outputs*)
                  let output = neurons |*| activation in
                  (* descend to next layer and get weighted sums of deltas from there*)
                  let delta_sums = loop2 rest_rep output in
                  (* calculate deltas from those sums *)
                  let deltas = Array.mapi (fun i x -> (derive (get_transfer neurons.(i)) output.(i))*.x) delta_sums in
                  (* create array to hold new deltas*)
                  let new_deltas = Array.make (Array.length activation) 0.0 in
                  
                  (* calculate the differences*)
                  let n = Array.length neurons in
                  let m = Array.length (get_synapses neurons.(0)) in
                  for i = 0 to n - 1 do
                     let weightsi = get_synapses neurons.(i) in
                     let deltai = deltas.(i) in
                     for j = 0 to m - 1 do
                        let weightij = Array.unsafe_get weightsi j in
                        let change = alpha *. deltai *. Array.unsafe_get activation j -. decay*.weightij in
                        (* get this part of the corresponding new_delta *)
                        Array.unsafe_set new_deltas j (Array.unsafe_get new_deltas j +. deltai *. weightij);
                        Array.unsafe_set weightsi j  (weightij +. change);
                     done
                  done;
                  
                  (* pass the deltas to caller, but throw away the one for the bias *)
                  Array.sub new_deltas 1 (Array.length activation - 1) 
               end
         |   [] -> Array.mapi (fun i x -> example.(i) -. x) activation
      in
      (* Special faster case for the first (and most often largest) layer *)
      match rep with 
         neurons :: rest_rep ->
            begin
               if Array.length (get_synapses neurons.(0)) != Array.length activation +1 then
                  raise (Invalid_argument "Net size does not match input")
               else ();
               (* add the bias *)
               let activation = Array.append [|1.0|] activation in
               (* calculate the weighted sums *)
               let output = neurons |*| activation in
               (* descend to next layer and get weighted sums of deltas from there*)
               let delta_sums = loop2 rest_rep output in
               (* calculate deltas from those sums *)
               let deltas = Array.mapi (fun i x -> (derive (get_transfer neurons.(i)) output.(i))*.x) delta_sums in
                
               (* calculate the differences*)
               let n = Array.length neurons in
               let m = Array.length (get_synapses neurons.(0)) in
               for i = 0 to n - 1 do
                  let weightsi = get_synapses neurons.(i) in
                  let deltai = Array.unsafe_get deltas i in
                  for j = 0 to m - 1 do
                     let weightij = Array.unsafe_get weightsi j in
                     let change = alpha *. deltai *. Array.unsafe_get activation j -. decay*.weightij in
                     (* get this part of the corresponding new_delta *)
                     Array.unsafe_set weightsi j  (weightij +. change);
                  done
               done
               (* from the first layer nothing has to be returned *)
            end
      | [] ->
         ()
   in loop net input