-
Notifications
You must be signed in to change notification settings - Fork 0
Modulus 11 ‐ Español
Modulus 11 comenzó como un experimento con una problemática sencilla, orientada al cálculo del dígito verificador del rut o run, que es un código usado para identificar a las personas y entidades en Chile.
El módulo 11, o dígito verificador, es ´generado a partir del número entero. Por ello, es una función del número, y cada número tiene un sólo dígito verificador.
Si al ingresar un par número-dígito verificador da un resultado distinto al calculado, o el número está mal, o el dígito verificador está mal. En ambos casos, confirmamos que es inválido y no puede usarse.
Generalicé pensando en otras posibles aplicaciones que también usan este algoritmo, dividiendo el software en dos paquetes:
- Cálculo genérico del módulo 11 para un entero
- Validación y generación de ruts, basado en reglas generales
El cálculo está documentado en la página del proyecto Modulus 11, en inglés. Este es un resumen.
Dado un número entero positivo, número, incluyendo el cero y ordenado como un vector de sus dígitos,
Y el búfer circular de seis dígitos (2,3,4,5,6,7), truncado o extendido a la cantidad de dígitos del número,
- Se multiplican en orden de menor a mayor, como vectores de largo del número de dígitos
- Se suman los resultados
- Luego se obtienen el módulo 11 (resto)
- El cálculo genérico del módulo 11 implica la siguiente conversión
a) Se sustrae a 11 el resto (11-resto)
b) Si el resultado de la resta es 10, el módulo es 1
c) Si el resultado de la resta es 11, el módulo es 0
d) Si no, el resultado de la resta es el mismo dígito (entre 0-9)
Notar que:
- El cero puede aparecer en dos circunstancias: El resultado de la resta es 11 ó 0
- El uno puede aparecer en dos circunstancias: El resultado de la resta es 10 ó 1
En el caso del Rut chileno, cuando el resultado de la resta es 10, se modifica el algoritmo y se retorna una letra K
| Circular buffer | 4 | 3 | 2 | 7 | 6 | 5 | 4 | 3 | 2 |
|---|---|---|---|---|---|---|---|---|---|
| Number | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
| Multiplication | 4x9 | 3x8 | 2x7 | 7x6 | 6x5 | 5x4 | 4x3 | 3x2 | 2x1 |
| Result | 36 | 24 | 14 | 42 | 30 | 20 | 12 | 6 | 2 |
La suma es 186
186 % 11 = 10
DIF = 11-10 = 1
Code = 1, 987654321 / 1
| Circular buffer | 4 | 3 | 2 | 7 | 6 | 5 | 4 | 3 | 2 |
|---|---|---|---|---|---|---|---|---|---|
| Number | 4 | 4 | 2 | 6 | 1 | 5 | 3 | 9 | |
| Multiplication | 4x | 3x4 | 2x4 | 7x2 | 6x6 | 5x1 | 4x5 | 3x3 | 2x9 |
| Result | 0 | 12 | 8 | 14 | 36 | 5 | 20 | 9 | 18 |
La suma es 122
122 % 11 = 1
DIF = 11-1 = 10 // Rule: if 11 => 1
Code = 1, 44261539 / 1
Como es un rut chileno, entonces 10=>'k', 44261539 / k or 44261539-k (con puntos, 44.261.539-k)
El diseño se compone de una clase que encapsula la lógica de cálculo, llamada Modulus11, y un registro de sólo lectura que encapsula la respuesta con su formato, llamada ModulusRecord.
Para prevenir problemas de concurrencia, esta clase no es abstracta. El cálculo del módulo bruto puede hacerse mediante el método GetModulus(long input).
La siguiente prueba genérica muestra cómo usarlo para calcular el módulo:
[Test]
public void TestBorderCondition_ModulusIs_11()
{
var m11 = new Modulo11.Modulus11(charFor10Value: "10", charFor11Value: "11");
var ver = m11.GetModulus(11111151);
Assert.That<string>(ver, Is.EqualTo("11"));
}Los parámetros charFor10Value y charFor11Value son usados para definir la respuesta cuando retorna 10 u 11 y por omisión son 1 y 0, según la definición estándar (en el caso del rut chileno, cambiamos el valor de charFor10Value por 'k').\
Esta otra prueba muestra cómo usarlo para retornar un registro con los datos por separado, lo que permite parar el valor completo ya calculado y aplicar formato para un despliegue sencillo (además de mostrar cómo hacer pruebas genéricas basadas en casos de uso en nunit):
[TestCase("1.000.000.000", "6", Category = "Borde", TestName = "Condiciones de Borde - 1M")]
[TestCase("1.000.000.001", "4", Category = "Borde", TestName = "Condiciones de Borde - 1M+1")]
public void TestDecimalOrders(string input, string expectedResult)
{
var m11 = new Modulo11.Modulus11("k");
var aNumberInt = long.Parse(input.Replace(".", ""));
ModulusRecord rec = m11.GetModulusRecord(aNumberInt);
Assert.That<string>(rec.Digit, Is.EqualTo(expectedResult));
Assert.That<string>(rec.ToString(), Is.EqualTo($"{aNumberInt}-{expectedResult}"));
}El registro ModulusRecord almacena el número, su módulo y opciones de formato usadas para ToString(). Permite obtener una copia autocontenida, liviana y versátil usable directamente por las aplicaciones.
Tiene la siguiente inicialización abreviada de c#12:
public record ModulusRecord(long Number, string Digit, string Hyphen = "-", string NumberGroupSeparator = "")Internamente hace un override de la función ToString() para mostrar una salida número-módulo con varias opciones y algunas optimizaciones para acelerar el despliegue en formato numérico, con o sin puntos.