LLTR Parte 2: Algoritmo para determinar la topología de la red a partir de estadísticas recopiladas

Logotipo de revelación de topología de capa de enlace


P: ¿Qué tenemos?
A: Estadísticas recopiladas de los hosts.


P: ¿Qué queremos obtener?
A: topología de red! Más precisamente, necesita construir la cadena correcta de pares (hosts) para RingSync .


Tenemos que encontrar un algoritmo que primero convierta las estadísticas en una topología de red y luego en una cadena de pares. Hasta ahora, el algoritmo se ve así:


 –-[**]-->   --[**]-->   


Si disfrutaste leyendo la "Parte 1" en las páginas de GitHub , aquí hay un enlace a esa parte en las páginas de GitHub.


Advertencia : a continuación se mostrarán los mismos artefactos de Habr-parser que advertí en la "Parte 1" .


Cualquier tecnología suficientemente avanzada es indistinguible de la magia.  - Arthur C. Clarke


Nota : además de “ –-[**]--> ” usaré “ –-[???]--> ”.


Las estadísticas recopiladas nos muestran en qué hosts ha disminuido la velocidad de recepción del tráfico de difusión. Por ejemplo, observe el resultado de la iteración cero en la red "N2_2" (" Network " del artículo anterior "LLTR Parte 1"):


 {300,164,164}, 


2 estados anfitriones son claramente visibles aquí:


  • velocidad normal (valor " 300 ") - sin reacción ;
  • la velocidad ha disminuido (valor " 164 "): hay una reacción .


¿A qué estoy llegando? ¡A la binarización! Si codificamos la ausencia de una reacción como 0 , y la presencia de una reacción como 1 , entonces podemos poner todas las reacciones de los hosts en una sola iteración en una variable ( 32 - 512 bit [ AVX - 512 ]). Además de ahorrar memoria (y espacio gastado en cachés), esto aumentará la velocidad de procesamiento: todas las respuestas del host de una iteración particular ( SIMD ) se procesarán de una vez en una instrucción.


Nota : porque El uso de LLTR Basic para una gran cantidad de hosts es muy costoso ( consulte el comienzo de la sección "LLTR Parte 0 :: LLTR Advanced" ), luego todo cabe en registros x86‑64 de 64 bits .


Nota : En el texto del enlace a la sección ubicada en otro artículo (otra parte), agregaré el número de parte en el formato: " LLTR Parte # :: ‹ nombre de sección › ". Y en el " title " del enlace escribiré el nombre de la parte, por ejemplo, para "LLTR Parte 0 ::", "Aparecerá automáticamente la topología de red y aparecerán los conmutadores no administrados". ¿Misión imposible?


Tomemos el mismo ejemplo de iteración cero y veamos cómo se verá después de la binarización:


 {300,164,164} --[]--> 011 


Muy compacto, pero me gustaría que " 1 " (la presencia de una reacción ) me llame la atención de inmediato al ver una lista de todas las iteraciones. Ahora " 1 " no se destaca sobre el fondo " 0 " (datos falsos, para un ejemplo visual ):


 0101011010110 1100010110010 0101101010111 0100010110100 


Para resaltar " 1 ", presento la notación:


  • " 1 " significa 1 - hay una reacción ;
  • " . "Significa 0 - sin reacción .


Veamos nuevamente los "datos falsos":


 .1.1.11.1.11. 11...1.11..1. .1.11.1.1.111 .1...1.11.1.. 


Mucho mejor (en mi humilde opinión ).


El algoritmo, en este momento, se ve así:


  –-[]-->   --[???]-->   --[???]-->   


Dejamos los detalles de la binarización para el final del artículo y nos concentramos en el resto del algoritmo.


Es más fácil crear un algoritmo basado en datos de entrada / entrada específicos (casos especiales, condiciones límite, pruebas en términos de TDD ). En nuestro caso, los datos iniciales dependen de la topología de la red, por lo que debe crear una red que sea pequeña y al mismo tiempo contenga diferentes esquemas de conexión de conmutador ( estrella , conexión en serie ). Quizás se pueda incluir algo especial en él ... En general, la imaginación dibujó dicha red (la notación para los elementos es similar a la notación utilizada al final de la sección " LLTR Parte 0 :: Topología:" conexión en serie de interruptores " "):


Gráfico: red híbrida LLTR


Nota : al mirar esta red, la pregunta "¿es posible realizar un análisis completo en esta topología si uno de los conmutadores ..." (cerca del final de la sección " LLTR Parte 0 :: Topología:" conexión en serie de conmutadores " "), y observa que ningún host está conectado directamente a uno de los conmutadores. Además, no hay problemas, porque Hay 3 interruptores más conectados a este interruptor (conté solo los interruptores conectados "desde abajo", sin tener en cuenta el hecho de que está conectado a otro interruptor "desde arriba"), cada uno de los cuales tiene hosts.


Sin embargo, en este diagrama hay algunos detalles adicionales (que distraen). Lo voy a limpiar quitando:


  • host de difusión (no está en la entrada / estadísticas);
  • puertos que conectan conmutadores entre sí.

Gráfico: Red híbrida LLTR (borrar)


Aquí el interruptor "sin hosts" es inmediatamente visible. Además, arreglé todos los interruptores de tal manera que los hosts en ellos no se superpongan entre sí verticalmente. Esto será útil si en el futuro quiero mostrar "reacciones de host" no en forma de una entrada de texto " .....1...... ", sino en forma de diagrama (solo hay un host en una vertical):


Gráfico: red híbrida LLTR (clara), explicación de la designación de "respuestas de host"


Ahora imagine las estadísticas que obtenemos al final de todas las iteraciones de escaneo de esta red. Hay 12 hosts en la red (excluyendo el host de difusión), por lo tanto, tendremos datos en 132 iteraciones. Sin embargo, no todos los resultados de iteración nos serán útiles, por ejemplo, serán inútiles:



Después de la limpieza, de los 132 resultados de iteración, solo quedarán 5 (respuestas del host):


 1111111111.. 11111111.... ..111....... .....111.... 11.......... 


Nota : para mayor claridad, arreglé las iteraciones en orden de un número mayor de " 1 " a uno más pequeño.


El algoritmo comenzó a verse así:


  –-[]-->   --[   ]--[  ]--[???]-->   --[???]-->   

punto de reinicio

Pensé en incluir todo esto en el spoiler, pero al final me di cuenta de que esta es una parte importante de la historia, que es mejor no perderse al leer.


¬ ( No salte al cerebro cuando lea : Cong



[punto de reinicio] En los 5 resultados de iteración restantes, los dos primeros llaman la atención: el primero incluye el segundo y el segundo incluye los 3 inferiores restantes. Aquí recuerdo la "sombra" de la sección " LLTR Parte 0 :: Topología:" conexión en serie de conmutadores " ". En la misma sección, al final de cada iteración, formamos (o no formamos) nuevos grupos basados ​​en los datos que acabamos de obtener. Ahora tenemos que hacer lo mismo.


¿Pero cómo formamos nuevos grupos? De hecho, todas las reacciones (no únicas) de los hosts " 1 " de la iteración actual fueron el "nuevo grupo", solo tuvimos que encontrar las intersecciones ("∩"; no está vacío "∅") con los grupos existentes para eliminar ("∖") de los más grandes hosts de clúster incluidos en un clúster más pequeño.


Sin embargo, en nuestras acciones hubo una condición / ramificación (if): debe determinar cuál de los grupos es más grande y luego realizar una operación simple (A ∖ B): reste el más pequeño (B) del grupo más grande (A). Representando el tormento de una CPU con una larga tubería causada por la necesidad de restablecer la tubería si la predicción de rama es incorrecta (si hay un "bloque de predicción de rama"), casi decidí usar el “?: " , Pero en ese momento ...

Me paré en el inodoro y colgué el reloj. De repente resbaló, se golpeó la cabeza con el fregadero, y cuando desperté tuve una visión, una imagen en mi cerebro, una visión de esto: un separador de flujo de transmisión ( Regreso al futuro ) :

Regreso al futuro: Flux Divider
 // Flux Divider c=a^b; aa=a&c; bb=b&c; cc=a&b; 


E inmediatamente vea su trabajo sobre el ejemplo de clústeres superpuestos (más precisamente, un conjunto (clúster) está estrictamente incluido " " en otro conjunto):


 .....11..... - a ..11111111.. - b ..111..111.. - c=a^b ............ - aa=a&c ..111..111.. - bb=b&c .....11..... - cc=a&b 


Grupos disjuntos:


 ..111....... - a .......111.. - b ..111..111.. - c=a^b ..111....... - aa=a&c .......111.. - bb=b&c ............ - cc=a&b 


Resulta que:


  • " aa " contiene elementos exclusivos de " a ";
  • en " bb " - exclusivo de " b ";
  • en " cc " - común a " a " y " b ".


Otro ejemplo con grupos de intersección ("imposible", pero un buen ejemplo):


 ...1111..... - a .....1111... - b ...11..11... - c=a^b ...11....... - aa=a&c .......11... - bb=b&c .....11..... - cc=a&b 


Nota : este tipo de respuesta (reacción del host) no está en los datos de origen.


Del mismo modo, puede deshacerse de las tomas :


 .....11..... - a .....11..... - b ............ - c=a^b ............ - aa=a&c ............ - bb=b&c .....11..... - cc=a&b 


Pero un poco más tarde ...

La cabeza deja de doler después de golpear el fregadero, la mente se aclara y surgen problemas obvios ...

En la entrada, tenemos 2 variables (resultados de iteración / reacciones del host / clusters / sets / ...), pero ya hay 3 en la salida, y al menos una de ellas estará vacía ("∅"). Si no se deshace inmediatamente de "∅", tendrá que incluirlos en el procesamiento en el futuro. Por lo tanto, es mejor deshacerse de "∅" de inmediato. ¿Pero cómo hacerlo? Condición de uso / ramificación! ... En general, regresé a donde comencé. Además, si todo se hace como se describió anteriormente, además de eliminar ",", al final obtenemos de:


 1111111111.. 11111111.... ..111....... .....111.... 11.......... 


Esto es:


 ........11..             -     "............",     :( ..111....... .....111.... 11.......... 


Es hora de hacer la pregunta: "¿Cómo obtener la topología de red de esto?" Ahora, estos datos pueden "decir" sobre qué clúster pertenece un host en particular (es decir, a qué conmutador está conectado el host), pero estos datos ahora carecen por completo de información sobre la topología de los conmutadores (es decir, cómo están conectados cambia entre sí): perdimos esta información durante la conversión de datos. Además, ¿a qué clúster (conmutador) pertenecen los 2 hosts más a la derecha? Si consideramos cada línea como un clúster separado (o como una indicación de qué hosts están conectados a un conmutador en particular), ¡resulta que estos 2 hosts extremos no están conectados en ninguna parte! Además, tenemos 6 conmutadores en la red y quedan 4 líneas, ¿dónde quedan 2 líneas más? Borramos uno (como dice el comentario anterior), y en el otro, debería haber "2 hosts en el extremo derecho".


[Ir al punto de reinicio ] Para desarrollar aún más esta idea es inútil. Callejón sin salida (git branch). Tendrá que volver a la etiqueta de "punto de reinicio", olvidando todo lo que estaba después, pero dejando esta rama para la historia.


Ahora, para no caer en otra "rama muerta", debe decidir sobre la estructura final (representación) de la topología de red en la memoria. Es decir, con lo que queremos obtener en el momento de la "topología de red":


  –-[]-->   --[   ]--[  ]--[???]--> <strong> </strong> --[???]-->   


Primero , todos los hosts deben estar presentes:


 <strong>..........11</strong> <-- 1111111111.. 11111111.... ..111....... .....111.... 11.......... 


En segundo lugar , se deben indicar los padres (el grupo de padres para cada grupo; en este momento: padre hijo ; en el diagrama de red, coloqué a los padres sobre los hijos) (los números de grupo se agregan a la izquierda):


 0) ..........11 parent: ? 1) 1111111111.. parent: ? 2) 11111111.... parent: 1 3) ..111....... parent: 2 4) .....111.... parent: 2 5) 11.......... parent: 2 


Nota : si nota algo extraño aquí, al comparar el diagrama de esta red con estos datos, le gusto .


Spoiler, es mejor no abrir hasta leer la lista completa

De hecho (de acuerdo con el diagrama), el padre para el clúster 1 es el clúster 0, pero la condición " padre hijo " no se cumple. Quizás en " Primero " cometimos un error, y en lugar de " ..........11 " valió la pena agregar " 111111111111 "?



En tercer lugar , debería haber un padre "raíz" que vincule árboles individuales (es decir, bosque ) en un árbol:


 -1) 111111111111 0) ..........11 parent:-1 1) 1111111111.. parent:-1 2) 11111111.... parent: 1 3) ..111....... parent: 2 4) .....111.... parent: 2 5) 11.......... parent: 2 


Cuarto , sería bueno tener listas de niños con cada padre:


 -1) 111111111111            children: 0,1 0) ..........11 parent:-1 1) 1111111111.. parent:-1, children: 2 2) 11111111.... parent: 1, children: 3,4,5 3) ..111....... parent: 2 4) .....111.... parent: 2 5) 11.......... parent: 2 


Y finalmente , ahora es posible excluir a los niños de sus padres:


 -1) ............            children: 0,1 0) ..........11 parent:-1 1) ........11.. parent:-1, children: 2 2) ............ parent: 1, children: 3,4,5 3) ..111....... parent: 2 4) .....111.... parent: 2 5) 11.......... parent: 2 


Ahora cada fila describe un grupo, es decir apunta a hosts conectados al mismo conmutador. Sin embargo, espere, hay 6 conmutadores en nuestra red, ¡y hay 7 grupos! Finalmente, es hora de leer el texto del spoiler sobre " Spoiler, es mejor no abrir hasta leer la lista completa " y corregir la situación:


 0) ..........11            children: 1 1) ........11.. parent: 0, children: 2 2) ............ parent: 1, children: 3,4,5 3) ..111....... parent: 2 4) .....111.... parent: 2 5) 11.......... parent: 2 


Estos datos son precisamente la "topología de red": describe el árbol de conmutadores y, a partir de él, puede determinar todos los hosts conectados a un conmutador en particular.


  –-[]-->   --[   ]--[  ]--[???]--> <strong> </strong> --[???]-->   


Queda por comprender cómo llevar los datos a este formulario. De hecho, todo lo que hemos hecho (primero, segundo, ...) se puede convertir en un algoritmo:


  1. "En primer lugar" (después de hacer correcciones desde el spoiler, se vuelve similar a la acción "en tercer lugar"): agregue un clúster "raíz" " 111111111111 " ( universal ), que incluye (hosts de todos los árboles en el bosque ∪ hosts ubicados en el mismo conmutador que el host de transmisión ), es decir Incluye todos los hosts en la red;
  2. "Segundo": busca un padre para cada grupo ;
  3. "Cuarto": crear una lista de hijos para cada padre ;
  4. “Y finalmente”: la exclusión de los niños de sus padres .


Ahora puede agregar estas acciones al algoritmo general (cambió ligeramente el aspecto):


                                               ●  ●                                [] ►                 [   ]                          [  ] ► /                [ "" ] ► /        [    ] [     ]              [   ] ►   ●                                        [???] ►   ● 

Vista alternativa

 ●      ► [] ▬   ► [   ]                   [  ] ▬ /   ► [ "" ] ▬ / ► [    ]                   [     ]                   [   ] ●   ► [???] ●    ● 


Veamos qué sucede si aplica este algoritmo a otra red. Me gustaría tomar la red Network_ serial y sus resultados de simulación (estadísticas) de la sección " LLTR Parte 1 :: Más redes con diferentes topologías, agregando nuevas redes ".


Nota : ¿Por qué elegí esta red en particular? Es bastante grande y existen fallas en los datos recopilados (ver el final del spoiler “Resultados de simulación” para esta red).


Vamos!


Binarización

Reacciones del anfitrión:


 .111111.. .111111.. .111111.. .111111.. .111111.. .111111.. .......11 .......11 ..1...... ...1111.. ...1111.. ...1111.. ...1111.. .......11 .......11 1........ ...1111.. ...1111.. ...1111.. ...1111.. .......11 .......11 1........ .1....... ....1.... .....11.. .....11.. .......11 .......11 1........ .1....... ..1...... .....11.. .....11.. .......11 .......11 1........ .1....... ..1...... ...1..... ......1.. ......... ......... ......... .1....... ..1...... ...1..... ....1.... ......... ......... ......... .1....... ..1...... ...1..... ....1.... .....1... ........1 1........ .111111.. .111111.. .111111.. .111111.. .111111.. .111111.. 1........ .111111.. .111111.. .111111.. .111111.. .111111.. .111111.. .......1. 


Purificación de reacciones individuales.

 .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .......11 --> .......11 .......11 --> .......11 ..1...... --> ...1111.. --> ...1111.. ...1111.. --> ...1111.. ...1111.. --> ...1111.. ...1111.. --> ...1111.. .......11 --> .......11 .......11 --> .......11 1........ --> ...1111.. --> ...1111.. ...1111.. --> ...1111.. ...1111.. --> ...1111.. ...1111.. --> ...1111.. .......11 --> .......11 .......11 --> .......11 1........ --> .1....... --> ....1.... --> .....11.. --> .....11.. .....11.. --> .....11.. .......11 --> .......11 .......11 --> .......11 1........ --> .1....... --> ..1...... --> .....11.. --> .....11.. .....11.. --> .....11.. .......11 --> .......11 .......11 --> .......11 1........ --> .1....... --> ..1...... --> ...1..... --> ......1.. --> ......... --> ......... ......... --> ......... ......... --> ......... .1....... --> ..1...... --> ...1..... --> ....1.... --> ......... --> ......... ......... --> ......... ......... --> ......... .1....... --> ..1...... --> ...1..... --> ....1.... --> .....1... --> ........1 --> 1........ --> .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. 1........ --> .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .111111.. --> .111111.. .......1. --> 


Limpieza de duplicados (obtenemos "grupos / bosque"):


 .111111.. .......11 ...1111.. .....11.. ......... 


Además, por conveniencia , ordenaré en orden descendente la cantidad " 1 ":


 .111111.. ...1111.. .....11.. .......11 ......... 


Nota : Puede valer la pena incluir la clasificación en el algoritmo. Que piensas


Agregar un clúster "raíz" (obtenemos "clústeres / árbol"):


 111111111 .111111.. ...1111.. .....11.. .......11 ......... 


Incluye hosts de 2 árboles (izquierda " .111111.. " y derecha " .......11 " parte de la red) y 1 host (" 1........ " ubicado en uno cambiar con el host de difusión).


Búsqueda principal para cada grupo:


 0) 111111111 1) .111111.. parent: 0 2) ...1111.. parent: 1 3) .....11.. parent: 2 4) .......11 parent: 0 5) ......... parent: 4 


Nota : Aquí es donde se produjo el impacto negativo de las brechas de datos: el cuarto grupo se convirtió en el padre del quinto. En general, cualquier grupo puede convertirse en el padre del quinto grupo, porque Está vacío (∅).


Crear una lista de hijos para cada padre:


 0) 111111111            children: 1,4 1) .111111.. parent: 0, children: 2 2) ...1111.. parent: 1, children: 3 3) .....11.. parent: 2 4) .......11 parent: 0, children: 5 5) ......... parent: 4 


Exclusión de hijos de los padres:


 0) 1........            children: 1,4 1) .11...... parent: 0, children: 2 2) ...11.... parent: 1, children: 3 3) .....11.. parent: 2 4) .......11 parent: 0, children: 5 5) ......... parent: 4 


En este paso, se suponía que debíamos obtener una "topología de red". Y lo conseguimos. Si solo estamos interesados ​​en la ubicación de los hosts, entonces esta "topología de red" es bastante satisfactoria para nosotros. Sin embargo, apareció otro interruptor en nuestra red, ¡en el cual 0 hosts!


Para arreglar todo, será suficiente después de uno de los primeros pasos para eliminar estos "defectos de datos". Esto se puede hacer inmediatamente después de la "binarización":


                                               ●  ●                                [] ►   [<strong>   (∅),    (⦱)</strong>]               [   ]                          [  ] ► /                [ "" ] ► /        [    ] [     ]              [   ] ►   ●                                        [???] ►   ● 


Eliminamos conjuntos vacíos (∅; “ ......... ”), pero ¿por qué eliminar universos (⦱; “ 111111111 ”)? La respuesta se hará evidente cuando comencemos a implementar la fase de "binarización". Las diferentes variantes de la implementación de la "binarización" pueden producir tanto " ......... " como " 111111111 " en los mismos datos (datos con el defecto descrito). Y porque es tan imposible obtener " 111111111 " en los datos de entrada correctos como obtener " ......... ", entonces podemos eliminar todos los " 111111111 " (además, no llevan ninguna información excepto que hay "fallas" en los datos).


Si aplica este algoritmo (aumentado, corregido) a la misma red (" Network_ serial "), la "topología de red" se verá así:


 0) 1........            children: 1,4 1) .11...... parent: 0, children: 2 2) ...11.... parent: 1, children: 3 3) .....11.. parent: 2 4) .......11 parent: 0 


Note : , . , . , 2 ( “switch0”), 1 ( 2 ):


“ ”

 0) 11........            children: 1,4 1) ..11...... parent: 0, children: 2 2) ....11.... parent: 1, children: 3 3) ......11.. parent: 2 4) ........11 parent: 0 

 0) 1......            children: 1,4 1) .1..... parent: 0, children: 2 2) ..1.... parent: 1, children: 3 3) ...11.. parent: 2 4) .....11 parent: 0 


“ ”. “ ” “ ”. RingSync , ( : Pre‑order ). “ ” :


 1 1........ hostS/seed -> host0 -> . .11...... host1 -> host2 -> . ...11.... host3 -> host4 -> . .....11.. host5 -> host6 -> . .......11 host7 -> host8/leech 


Note : (, ) , broadcast .


, “ ” ( ), (“ Network_ serial ”). ( ), . :


Diagrama: conexión en serie de interruptores;  ruta de flujo de tráfico para una cadena construida sin prioridades


, “ ” (“ ”):


 ..........11 1 hS/seed -> h10 -> h11 -> ........11.. . h8 -> h9 -> ..111....... . h2 -> h3 -> h4 -> .....111.... . h5 -> h6 -> h7 -> 11.......... . h0 -> h1/leech 


( “ ”) . , – 2, .. (∅). , “ ” , “ ” ( , ), (∅) ? , : ‑, “” , ( , ;); ‑, ( ).


, “ ” , “ ”

: LLTR   (clear),  ,   “depth first traversal”


( ) , , “ ”,



, “ ”, …


Note : , , . .



, ( ), .


: LLTR   (clear);


:


 ..........11 1 hS/seed -> <strong>h11</strong> -> <strong>h10</strong> -> ........11.. . <strong>h9</strong> -> <strong>h8</strong> -> ..111....... . h2 -> h3 -> h4 -> .....111.... . h5 -> h6 -> h7 -> 11.......... . h0 -> h1/leech 


Network_ serial ”…


, :


           switch0 -> switch1 -> switch2 -> switch3 -┐ switch4 <- switch0 <- switch1 <- switch2 <-----------┘ 


… “” “ switch0 <- switch1 <- switch2 ”. :


                                 switch0 -> switch4 -┐ switch3 <- switch2 <- switch1 <- switch0 <-----------┘ 


:


Diagrama: conexión en serie de interruptores;  ruta de flujo de tráfico para una cadena prioritaria


, , , !


Note : , .. “ ”.


Note : “ ”, “ ” ( ; – L0 ) – .


, “ ” .


Note : , – .


() : “ ” ( LLTR 0:: : “ ” ) :


  1. – ;
  2. – ;
  3. – ( );
  4. – ( ) – , .


Note : ” “ , ”, , , .


Note : – ( ). – ( ) . , ( ): ( ); ( ).


:


                                                    ●  ●                                     [] ►      [   (∅),    (⦱)]                    [   ]                               [  ] ► /                     [ "" ] ► /             [    ]    [     ]                   [   ] ►   ● [      /] ►   ● 


“ ” “ Network_ serial ” :


 1 1........ hostS/seed -> host0 -> . .......11 host7 -> host8 -> . .11...... host1 -> host2 -> . ...11.... host3 -> host4 -> . .....11.. host5 -> host6/leech 


“ ”, .


“ ” . “ ” :


 s0) ..........11 1 hS/seed -> h10 -> h11 -> s1) ........11.. . h8 -> h9 -> s3) ..111....... . h2 -> h3 -> h4 -> s4) .....111.... . h5 -> h6 -> h7 -> s5) 11.......... . h0 -> h1/leech 


? , , , ( ):


 s0 -> s1 -> s2 -> s3 -┐  ┌- s4 <- s2 <------┘  └------> s2 -> s5 


Note :s# ” “ ” (. ).


# TL;DR


:


  1. (~~ k‑medoids ~~) + (∅), (⦱) + :
    1. a min a max
    2. 2
      1. + (∅), (⦱)
    3. :
      1. ( : )
      2. ( O(nlogn) O(1) )
      3. ( nth_element implementations complexities )
    4. a medL (medLow) a medR (medHi)
    5. 2 ,
    6. +
  2. + “” :
    1. + “”
    2. + bitCount ( max min)
  3. :
    1. min (min) (max) ( ) , ;
      bitCount(a i )==bitCount(a i |a min ) , : a i ==a i |a min
    2. , ( ) –
    3. min ( )
  4. () :
    1. ( “” “”)
  5. :
    1. “”, max , or|=a i , a max &=~or
      ( “ a max ^=or ” – )

    2. ( a max a min , .. , )
  6. /:
    1. (RingSync)


Note : git , .



. ( ), .

,


, , (“ {…} ”) () . ():


 //    "  " int ...;{   //    "" } 


“”, ():


 //==[Name]==// int ...;{   ... } 


, :


Tensors Flowing

? TensorsFlowing


es decir – , “, ” – .


?
:


  • – ( ) , . “” , .. “” “” . , “ ”, .
  • – “” / , , . . , (Interprocedural optimization, Whole program optimization; Link‑time optimization) “” – .


Note : : .. (2D/3D , , *, …). (), , , ( , , 24 , ; , ACPI ), ( ) , :(. (, , …) , ‑ . , , ‑. ( “” “”), “ *”. , – , , , . () – , . – ( ), . – , //‑/ /. (debug) ‑ .


Note : Debug , (, – { 9 , ; – ×16 ( 1.2 1.5); }), warning' .


Note : , , , ‑. , , ( “ ” ) .



# Tooo Long; Didn't Read; Visualize, plz.


Note : , ( GIF “TensorsFlowing” “ ”). GIF “TensorsFlowing” GIF “ Loop over python list animation ”. , GIF , “ ” / . , ‑ 1:1, “ ”.


#


Note : GIF ( “Loop over python list animation”), . , , . ( ;)


Note : ( ) ( ). , .


Note : GIF ( “Scroll Down”) – (Ctrl+R), GIF . ( , ; , ‑ <oembed> ? )


Animación: binarización

#1

 int average;{      int max,min;      max=min=countFill[i][0];      for(int j=1;j<numHosts;j++){            max=countFill[i][j]>max?countFill[i][j]:max;            min=countFill[i][j]<min?countFill[i][j]:min;      }      average=(max+min)/2; } 

:  –  1


Note : GIF …



#2

 int lo=0; struct CnN{      int Count; }iFill[numHosts]; for(int j=0,hi=numHosts-1;j<numHosts;j++){      if(countFill[i][j]<average) iFill[lo++].Count=countFill[i][j];      else                       iFill[hi--].Count=countFill[i][j]; } bitCluster[i]=0; if(lo==0||lo==numHosts) continue; //-      


Note : ( ) .


:  –  2


#3

 int averageMed;{      CnN *iFillLo=&iFill[0];      CnN *iFillHi=&iFill[lo];      const int hi=numHosts-lo;      if(lo>1) std::nth_element(iFillLo,&iFillLo[lo/2],&iFillLo[lo],[](const CnN a,const CnN b){return a.Count<b.Count;});      if(hi>1) std::nth_element(iFillHi,&iFillHi[hi/2],&iFillHi[hi],[](const CnN a,const CnN b){return a.Count<b.Count;});      averageMed=(iFillLo[lo/2].Count+iFillHi[hi/2].Count)/2; } 

:  –  3


Note : std::nth_element() , , ( + = ).



#4

 for(unsigned int j=0;j<numHosts;j++) bitCluster[i]|=( (countFill[i][j]<averageMed)?1:0 )<<j; 

:  –  4


#5

 bitCluster[i] = bitCluster[i]^(1<<((i/(numHosts-1))+(i%(numHosts-1)+1))%numHosts) ? bitCluster[i]:0; 

:  –  5


Note : GIF git . ReadMe ( ; ‑ , ).



OMNeT++ “ ”, “ DAT_EX.h ”.



...


#


3‑ 1.92 , , 1.6 - 2 . , 3‑ ( ) ( Go – 2 , – 2 - 4 ). (4 ), 2.5 LLTR.


+ TODO' + .


, ‑ , , , 2 .


Note : , / , …


Spoiler

2 ?



?


, , . , , 2 . .



Si


# Tooo Long; Didn't Read; Visualize, plz.


TODO[old]: (1 – gif_1, , 2 – gif_2, , …)


TODO: ,


:   & –   ‑

? ( )


TODO: ( GIF “TensorsFlowing”, ‑ – ),


( Note, GIF , , , YouTube. : 4:2:0 TV‑ ( 16 - 235 ). , – (). : SVG – , “ ‑”; SWF – RIP)


# ?


( ), std (, ) ( );


( “ 1 ” == “ 1 ” ). Un ejemplo:


 0) 111111111111 1) 1111111111.. 2) 11111111.... 3) ..111....... 4) .....111.... <-  ,     2‑,  3‑ 5) 11.......... 


(.. ), .. “ 1 ” ( ) (. “ ” “ ”). “ 1 ”, ..:


 0) 111111111111 1) 1111111111.. 0 2) 11111111.... 1 3) ..111....... 2 4) .....111.... 2 5) 11.......... 4 


( , – + (+), )


( “”). CPU, + . , , , , .


...



3: OMNeT++

LLTR 3: OMNeT++


Golang. ( , )


( , OMNeT++ c Qtenv)


( “background/fresh” “.ned” {“ grey99 ” → “ -,-,0;bgi=background/fresh ”}, “blueprint/print-26.png” Qtenv “LLTR 1:: ”)


( , “OMNetProject (v0.9) lltdapp”)


( , “hostS” – ( ) . , , – broadcast , unicast , .. – , . , – “ ”. “ – ”, : “ ” – “Serial” “ 1” ( – “ ”). – (, , broadcast unicast )[ rand , , – , – ])


( Precision Time Protocol (PTP) 2016-04-12)


( – , , “a3_v0.3_ft0.1.0”, “a3_v0.3.0” – , ; “ft” – fixed time)


.


TODO [x]: , , . “ TODO [x]” “ ” ( )


Referencias




4:

LLTR 4:


Wolfram Mathematica – Numbers (last episode 1 season) – .



∀ habrauser ∈ {user ∈ Habrahabr | user “”},

, “” .



(, . )


Referencias



, (hostsCount) – . . ? (: )


(, “”, {“”,“”,“”})


( ( ) [ ; “ ”], – n‑ “ ”; , LLTR, )


Permutation of bitsets (“lexicographic” order) derivation (mathematical induction)

( , __ [ , , , ]):


 n=4; k=2 bitset  i 0011 <- 0 0101 <- 1 1001 <- 2 0110 <- 3 1010 <- 4 1100 <- 5 


Note: , .. bitset k i < bitset k i+1 , i – “ ”; k – ; n – .


“” ( ; /; , “”/), ?


  • ( “B9”) ( “ ” O_o; , )
  • _tmain() ” ( )
  • , , – “ med() ” “ demed()


, :



:
“ ” (“ ”; “Permutations of multisets”).
Cual es la diferencia ( [abcdef]), ( [000011]).
, ( ):


 a => 0 b => 0 c => 0 d => 0 e => 1 f => 1 


, , .. , , [abcdfe] ⇒ [000011], [000011] . (, )


{{000011}}.
{abcdef} 6! ( nuclphys.sinp.msu.ru/mathan/p1/m0204.html ).
.
, , ( [000011]) , ( (“1”) 2! × (“0”) 4! ) = 2! × 4! = 2! × (6−2)! .
= 6! ∕ (2! × (6−2)!).


( nuclphys.sinp.msu.ru/mathan/p1/m0204.html ), ( ru.wikipedia.org/wiki/?stable=1 ) – . . “ ” ( ru.wikipedia.org/wiki/?stable=1 ), “” “1” “0” – ( ru.wikipedia.org/wiki/?stable=1#___ ).


EN: → → combination: ( k‑combination with repetitions / k‑multicombination / multisubset ), ( en.wikipedia.org/wiki/Combination?stable=1#Example_of_counting_multisubsets ), “Stars and Bars” ( en.wikipedia.org/wiki/Stars_and_bars_(combinatorics)?stable=1#Proofs_via_the_method_of_stars_and_bars ). (/ ): “1” – Star, “0” – Bar.


, “Stars and Bars” “” ( “ ” – k‑combination with repetitions) “ ” (permutations of multisets): en.wikipedia.org/wiki/Permutation?stable=1#Permutations_of_multisets .
RU: ru.wikipedia.org/wiki/?stable=1#__


PS stackoverflow.com/a/24257996 , ( – : n!∕((n−k)!); n⩵k; (n−k)!⇒1; n! ).


PPS [ alisey Trif ] ‑ / ( “Permutations of multisets”), ?




5: OMNeT++ 2

LLTR 5: OMNeT++ 2



( LLTR-Process sequence, – { “LLTD-Process (vFinal)”}, – , i → dstId, )


Referencias




6+7: +

LLTR 6:


, Golang.


Referencias



LLTR 7: (: “ ” – )


( 4 { //Wi‑Fi}, 3 ? – 2 ! – MacBook, Wi‑Fi Ethernet Thunderbolt)


( , “ ”, , “ ”)


( Wi‑Fi UDP broadcast – WNIC //. : How to override wifi broadcast speed limit? , Why does my UDP Broadcast wireless communication is capped at 1MBs? . 3 Mbps, 5 Mbps { }. MacBook {Wi‑Fi } Super‑, broadcast‑, unicast, {Wi‑Fi- ‑} unicast‑ broadcast { – Wi‑Fi}. , Wi‑Fi- – CPU . ‑.)


( UDP‑, !? : Windows “” {Windows NIC ?..}, API, “ CPU” { Win8 API, … (. “LLTD/flood/main.go”)}. “ ”. – API , “” . *nix { API}, , “” {. “LLTD/flood/main.go”}. : “ iperf3 and microbursts ”)


( → . { ; SMB}: → → → MacBook . , .)


( “LLTD/Prepare test environment.txt”)


Referencias



( “LLTD/Client.go”, “‑” – “LLTD/flood/main.go”)


( {Client1} NIC , – , , “ ” : “ interface always dead”)


Note: – Wi‑Fi ( ADSL‑/, ADSL – )


Note: ‑ : “” 100 Mbps unicast ; 100 Mbps broadcast . ( , /, )



TODO : : ( – ; ; +1/−1 ). Google Wave, Google Docs, Discus. :


  • – ,
  • :
    • , (.. ) – “” “ ” – (.. “” )


UserJS/UserCSS, , , .. , .


– – , UI (, , ) ( , “”). “” UserCSS. , , , ( ), ( ) ( ).


( ) ( ). ( UserJS UserCSS; Opera Presto , Firefox )


– “ OMNeT++ 2”.


TODO [x]: () + , + , , OMNeT++ v5.0b1 INET v3.0.0 + , ( ), – /


:



( ) (), . “ ” – , .


Note : – , ( ) – .


“ ”, , “ ”.


“ ”, , :


:    TOP SECRET


– . , – “ ”.


Note : “” – ( −1 ) ( ) (: ; ; – ); “‑‑‑” – ( ) , , ( ), , { “” ( ) – , , “ ?”; + “ ' ', ”, : (cookie) view‑only}


Note : (‑)



LLTD v1 – TCP ( map?), ,
() ,


LLTD v0.9 – client , ( )


v0.5 Go
IP, github.com/hashicorp/mdns
github.com/davecheney/mdns
grokbase.com/t/gg/golang-nuts/132tcpawde/go-nuts-udp-multicast-who-is-on-my-network


PS ( ) “ ”.
r=rand();
r, .
:
1. ‑ . , – . + ± ( “” ).
2. “”. ( , ; ) + ( “” )


iperf3 and microbursts burntchrome.blogspot.ru/2016/09/iperf3-and-microbursts.html



# Check‑list (TODO's)


TODO, .


PNG{SVG} (SVG thumbnail PNG) :


  1. PNG:
    1. [ 778px, 756px] ‑ ( . )
    2. ‑ 7z (un[7z]me), ( – “ ”, ‑ , ‑ )
      • [ Photoshop] “Save for Web” → PNG 24+alpha
      • [ GIMP] “8bpc RGBA” ( ), “ Save for Web
    3. 256 + alpha‑
    4. “” , Image Catalyst ( “” 2 : 2.1 2.5 , ):
      1. “” Image Catalyst 2.1 ([5] Xtreme profile)
        Tools\config.ini

         [options] ;         ,    "true"  "false". fs = true ;    PNG.    0,       %NUMBER_OF_PROCESSORS%. threatpng = 0 ; .          ,    "true"  "false". up = false [JPEG] ; Metadata.       Metadata  JPEG,    "true"  "false" ,   . dc = true   ;Delete comment field (as left by progs like Photoshop & Compupic). de = true   ;Strip Exif section (smaller JPEG file, but lose digicam info). di = true   ;Delete IPTC section (from Photoshop, or Picasa). dx = true   ;Deletex XMP section. du = true   ;Delete non image sections except for Exif and comment sections. [PNG] ; ColorType  BitDepth.      ColorType  BitDepth  PNG,    "true"  "false". nc = true ; -.       "Dirty Transparency"  PNG c -,    "true"  "false". na = true ; Chunks. ;     Chunks   Chunks,   "remove"       Chunks   Chunks,   . ;     Chunks   Chunks,   "keep"       Chunks   Chunks,   . ; Chunks: ;text = iTXt,tEXt,zTXt ;color = cHRM,sRGB,iCCP,gAMA ;misc = bKGD,pHYs,sBIT,sPLT,hIST,tIME ;all  = all of noncritical chunks hunks = remove all 


        Note :Image Catalyst 2.1 . Enter. ”, , , ( “Image Catalyst 2.1” “Image-Catalyst-2.1”)


      2. “” Image Catalyst 2.5 ([1] Xtreme profile)
        Tools\config.ini

         [options] ;Number of streams. If value early 0, is used value of parameter %NUMBER_OF_PROCESSORS%. thread=0 ;Automatic replacement of original images by the optimized. outdir=true ;Check update update=false [PNG] ;Parameters of optimization of PNG: ;/a# - PNG dirty transparency 0=Clean, 1=Optimize; ;/g# - PNG gamma 0=Remove, 1=Apply & Remove, 2=Keep; ;/na - PNG don't change RGB values for fully transparent pixels; ;/nc - PNG don't change ColorType and BitDepth; ;/np - PNG don't change Palette. xtreme=/a1 /g0 advanced=/a0 /g0 ;Remove PNG Metadata (Chunks). chunks=true [JPEG] ;Remove JPEG Metadata. metadata=true [GIF] ;Remove GIF Metadata. giftags=true 


        Note :Attention: running 2 of Image Catalyst. ”, , , ( “iCatalyst-2.5”)



      3. merge_min.bat

         @echo off setlocal enabledelayedexpansion :: Copy file from source to destination directory only if :: source file is smaller in size than in destination directory echo Src dir: %~f1 echo Dst dir: %~f2 echo --- for /r "%~1" %%A in (*) do ( set FileA=%%~fA set FileB=!FileA:%~f1=%~f2! set FileASize=%%~zA for %%Z in ("!FileB!") do set FileBSize=%%~zZ if !FileASize! LSS !FileBSize! copy "!FileA!" "!FileB!" ) 

    5. “.svg” ( ) – (SVG) (un[7z]me)
  2. SVG:
    1. {SVG 1.1; UTF-8; ; : ; : “1:100”; } ( , 2 – 1‑ )
    2. transform SVG ( 90 ) ( SVG ):
      1. DevTools transform ( “ [transform] ”)
      2. Rotate90AndSwapWH() ” ( “ ”)
        Rotate90AndSwapWH()

         Sub Rotate90AndSwapWH()   Dim sr As ShapeRange, s As Shape, w#, h#   Set sr = ActiveSelectionRange   On Error Resume Next   boostStart2 "Rotate 90 and Swap WH"   For Each s In sr       s.GetSize w, h       s.Rotate -90       s.SetSizeEx s.CenterX, s.CenterY, w, h   Next s   boostFinish2 End Sub 


        + boostStart2/boostFinish2:



        :


         Private Sub boostStart2(ByVal unDo$)   On Error Resume Next   ActiveDocument.BeginCommandGroup unDo   Optimization = True   EventsEnabled = False End Sub Private Sub boostFinish2()   On Error Resume Next   EventsEnabled = True   Optimization = False   ActiveWindow.Refresh   ActiveDocument.EndCommandGroup   'Refresh End Sub 

    3. :
      • :
        • ( [, ] )
        • ( )
    4. ( )
    5. XML ( )
      1. ( ):
        • DOCTYPE ” “ Creator ” “ 96ppi ” ( ppi CorelDRAW SVG)
        • metadata ”, “ id ” ( )
        • svg:
          1. xmlns ” “ xml:space
          2. xmlns:xlink
          3. [, “ style ” “ fill-rule:evenodd; clip-rule:evenodd ”] “ version ” “ style ” ` style="margin:16px auto" shape-rendering="geometricPrecision" fill-rule="evenodd" clip-rule="evenodd" xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" `
        • ( ) ` " ` ` " `
      2. ( <rect> <g>), , “ viewBox ” ( <svg>)
        • , SVG , CorelDRAW – , , , ( , )
      3. SVG optimiser :
        • :
          • Whitespace: pretty
          • Style type: optimal
          • Truncate * numbers: unchanged
          • ( , “Remove clean group”, )
        • <svg>
        • <style> – SVG optimiser CDATA ( )
      4. XML
  3. PNG SVG:
    1. “PNG_SVG.bat” ( 7-Zip SVG: “ -txz -m0=LZMA2:lc1:pb0 -mx ”)
      PNG_SVG.bat

       @echo off setlocal enabledelayedexpansion :: PNG+7Zip{SVG} echo PNG dir: %~f1 echo SVG dir: %~f2 echo --- for /r "%~2" %%A in (*.svg) do ( set SVG=%%~fA set PNG=!SVG:%~f2=%~f1!.png "%ProgramFiles%\7-Zip\7z.exe" a dummy -txz -m0=LZMA2:d96m:fb74:lc1:pb0 -mx -so -- "!SVG!" >> "!PNG!" ) 


      LZMA2:d96m:fb74:lc1:pb0 ”?


      ‑ ( “RingSync_no_problem.svg”):


       - "LZMA2:d96m:fb64"        6804 byte - "LZMA2:d96m:fb74"        6800 byte - "LZMA2:d96m:fb74:lc2"    6812 byte - "LZMA2:d96m:fb57:lc2"    6780 byte - "LZMA2:d96m:fb57:lc1"    6768 byte - "LZMA2:d96m:fb56:lc1"    6760 byte - "LZMA2:d96m:fb49:lc1"    6760 byte - "LZMA2:d96m:fb56:lc1:pb0" 6696 byte - "LZMA2:d96m:fb46:lc1:pb0" 6688 byte (fb44-fb47) - "LZMA2:d96m:fb63:lc1:pb0" 6688 byte - "LZMA2:d96m:fb66:lc1:pb0" 6684 byte - "LZMA2:d96m:fb74:lc1:pb0" 6692 byte 


      svg “ LZMA2:d96m ” (fb64), “ LZMA2:d96m:fb74:lc1:pb0 ” .



Note : Image Catalyst: ping timeout, ( 2.5) ( 2.1 – )


Image Catalyst.bat

v2.1 diff:


 182c182 < if defined thrt >nul 2>&1 ping -n 1 -w 500 127.255.255.255 & goto:waithreat --- > if defined thrt >nul 2>&1 timeout /t 1 /nobreak & goto:waithreat 203c203 < 1>nul 2>&1 ping -n 1 -w 500 127.255.255.255 --- > 1>nul 2>&1 timeout /t 1 /nobreak 237c237 < if exist "%~1" (1>nul 2>&1 ping -n 1 -w 500 127.255.255.255 & goto:waitflag) --- > if exist "%~1" (1>nul 2>&1 timeout /t 1 /nobreak & goto:waitflag) 513c513 <     if exist "%tmppath%\typelog.lck" (1>nul 2>&1 ping -n 1 -w 500 127.255.255.255 & goto:savelog) --- >     if exist "%tmppath%\typelog.lck" (1>nul 2>&1 timeout /t 1 /nobreak & goto:savelog) 534c534 < if "%jpeg%" equ "0" if "%png%" equ "0" 1>nul ping -n 1 -w 500 127.255.255.255 2>nul & goto:finmessage --- > if "%jpeg%" equ "0" if "%png%" equ "0" 1>nul timeout /t 1 /nobreak 2>nul & goto:finmessage 572c572 <     1>nul ping -n 1 -w 500 127.255.255.255 2>nul --- >     1>nul timeout /t 1 /nobreak 2>nul 


V2.5 diff:


 319,320c319 <     call:division float 1024 100 <     call:echostd " In   - !float! " --- >     call:echostd " In   - !float! " 322d320 <     call:division change 1024 100 324,325c322 <     call:division float 1024 100 <     call:echostd " Out  - !float!  (!change! , %5%%%%%%)" --- >     call:echostd " Out  - !float!  (!change! , %5%%%%%%)" 362,363c359,360 < set /a "ww=%random%%%%1" < 1>nul 2>&1 ping -n 1 -w %ww% 127.255.255.255 --- > set /a "ww=%random%%%%1/1000" > 1>nul 2>&1 timeout /t %ww% /nobreak 707c704 < if %jpeg% equ 0 if %png% equ 0 if %gif% equ 0 1>nul 2>&1 ping -n 1 -w 500 127.255.255.255 & goto:finmessage --- > if %jpeg% equ 0 if %png% equ 0 if %gif% equ 0 1>nul 2>&1 timeout /t 1 /nobreak & goto:finmessage 741d737 < call:division changePNG 1024 100 747d742 < call:division changeJPG 1024 100 753d747 < call:division changeGIF 1024 100 800c794 <     call:echostd " Total %1:        %%change%1%% , %%perc%1%%%%%%" --- >     call:echostd " Total %1:        %%change%1%% , %%perc%1%%%%%%" 


Note : Image Catalyst ( ) CP866, diff, , .



:


  • 778px – (780px – − 2px )
    • 756px – (758px – − 2px )
    • 738px – (740px – − 2px )
  • Image Catalyst v2.1 v2.5, ( “ merge_min.bat ”).
  • – : habrastorage “dwbmwbyvlzes80cep1hvcdb5iy.png” () HTTP‑ “ Content-Disposition : inline ;... ”, , , (): “dwbmwbyvlzes80cep1hvcdb5iy.png#real-name.png”. , – ( ). SVG – (), , …
  • (id, name). . ( – , , – )
  • , ( ).
  • ‑ (un[7z]me), habrastorage – , CloudFlare Polish .


Note : habrastorage SVG ( ): ( ), PNG{SVG} ( SVG, , – ) ( , , / – ‑ / , )


git:


  • git tag git “git-tag-‹›” .
  • git , / , “article_#”. ( LLTR Simulation Model )
  • ( “http”), ( ) web.archive.org, sohabr.net:
     var res=str.match(/http[^#)\s]*/gm); var res_l=res.length; for(var i=0;i<res_l;i++) console.log(res[i]); var archive = res.filter(function(a){return a.search(/(omnetpp.org\/doc\/omnetpp\/manual\/#|wikipedia|\/github.com\/)/)==-1;}); 
    • , web.archive.org sohabr.net .
    • habrahabr.ru habr.com, .. web.archive.org ( , ).
  • , Wikipedia “?stable=1”.
  • () MediaWiki (“#.D0.AD.D0.B2.D1.80.D0.B8.D1.81…”; “wikipedia”, “#.D0”) (“#…”).
  • C ( ) + Git.
  • [ “ 2”] (“LLTR #::”), “title” ( ).
  • (id, name), (, “#”) ( title “ ”).
    • sohabr.net ` id ` ( ), ` <a name=""></a> `?
    • “Unicode Link Symbol” (U+1F517; “&#128279;”) , (Chrome , , ), .. .
  • (<hr />) – , UserCSS ( UserCSS ).
  • ` <p><br /></p> `, UserCSS ` <br /> `, ` margin ` ` <p> ` ( ).
    • `<p> ` , Markdown… (, ` <p> ` info , , UserCSS, ).
  • height ( ‑), , width.
  • Full width brackets ” ( ; , ).
  • “ ?”
    • ( , , ). , ( ) , . , – . , ( ). /, . //, – .
    • ”.
  • habrahabr.ru/info/help/posts/ ( , old )
    , how‑to – « » (tutorial), ;
  • .


Note : habrahabr <oembed> , GitHub , .


Note : TODO‑ , 43 KiB ( “ 0”), 69 KiB ( “ 1”), 45 KiB ( ).




DOI: 10.5281 / zenodo.1407060

Source: https://habr.com/ru/post/es421243/


All Articles