function executar(count = 1, formatted = true, certificateType = '1', year = new Date().getFullYear()) {
const ELECTORAL_STATES = [
['01', 'SP'], ['02', 'MG'], ['03', 'RJ'], ['04', 'RS'], ['05', 'BA'],
['06', 'PR'], ['07', 'CE'], ['08', 'PE'], ['09', 'SC'], ['10', 'GO'],
['11', 'MA'], ['12', 'PB'], ['13', 'PA'], ['14', 'ES'], ['15', 'PI'],
['16', 'RN'], ['17', 'AL'], ['18', 'MT'], ['19', 'MS'], ['20', 'DF'],
['21', 'SE'], ['22', 'AM'], ['23', 'RO'], ['24', 'AC'], ['25', 'AP'],
['26', 'RR'], ['27', 'TO'], ['28', 'Exterior'],
];
function weightedSum(base, weights, alphanumeric = false) {
return [...base].reduce((sum, character, index) => sum + (alphanumeric ? character.charCodeAt(0) - 48 : Number(character)) * weights[index], 0);
}
function mod11(base, weights, alphanumeric = false) {
const remainder = weightedSum(base, weights, alphanumeric) % 11;
return remainder < 2 ? 0 : 11 - remainder;
}
function assertBase(base, length, alphanumeric = false) {
if (base.length !== length || !(alphanumeric ? /^[0-9A-Z]+$/ : /^\d+$/).test(base)) {
throw new Error(`A base deve ter ${length} ${alphanumeric ? 'caracteres de 0 a 9 ou A a Z' : 'dígitos'}.`);
}
}
function cpfChecksum(base) {
assertBase(base, 9);
const first = mod11(base, [10, 9, 8, 7, 6, 5, 4, 3, 2]);
return `${first}${mod11(base + first, [11, 10, 9, 8, 7, 6, 5, 4, 3, 2])}`;
}
function cnpjChecksum(base) {
assertBase(base, 12, true);
const first = mod11(base, [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2], true);
return `${first}${mod11(base + first, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2], true)}`;
}
function rgChecksum(base) {
assertBase(base, 8);
const digit = (11 - weightedSum(base, [2, 3, 4, 5, 6, 7, 8, 9]) % 11) % 11;
return digit === 10 ? 'X' : String(digit);
}
function cnhChecksum(base) {
assertBase(base, 9);
const remainder = weightedSum(base, [9, 8, 7, 6, 5, 4, 3, 2, 1]) % 11;
const first = remainder === 10 ? 0 : remainder;
const secondRemainder = weightedSum(base, [1, 2, 3, 4, 5, 6, 7, 8, 9]) % 11;
const second = secondRemainder === 10 ? 0 : secondRemainder - (remainder === 10 ? 2 : 0);
// Evita bases que levam a um resultado negativo nas regras legadas de CNH.
return second < 0 ? null : `${first}${second}`;
}
function electoralChecksum(base) {
assertBase(base, 10);
const state = base.slice(8);
if (!ELECTORAL_STATES.some(([code]) => code === state))
throw new Error('Selecione uma UF eleitoral válida.');
const digit = (remainder) => remainder === 10 ? 0 : remainder === 0 && (state === '01' || state === '02') ? 1 : remainder;
const first = digit(weightedSum(base.slice(0, 8), [2, 3, 4, 5, 6, 7, 8, 9]) % 11);
return `${first}${digit(weightedSum(state + first, [7, 8, 9]) % 11)}`;
}
function pisChecksum(base) {
assertBase(base, 10);
return String(mod11(base, [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]));
}
function renavamChecksum(base) {
assertBase(base, 10);
return String(mod11(base, [3, 2, 9, 8, 7, 6, 5, 4, 3, 2]));
}
function completeStateRegistration(base, state) {
if (state === 'SP') {
assertBase(base, 10);
const first = weightedSum(base.slice(0, 8), [1, 3, 4, 5, 6, 7, 8, 10]) % 11 % 10;
const partial = base.slice(0, 8) + first + base.slice(8);
return partial + (weightedSum(partial, [3, 2, 10, 9, 8, 7, 6, 5, 4, 3, 2]) % 11 % 10);
}
if (state === 'MG') {
assertBase(base, 11);
const expanded = base.slice(0, 3) + '0' + base.slice(3);
const sum = [...expanded].reduce((total, character, index) => {
const product = Number(character) * (index % 2 + 1);
return total + Math.floor(product / 10) + product % 10;
}, 0);
const first = (10 - sum % 10) % 10;
return base + first + mod11(base + first, [3, 2, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2]);
}
if (state === 'RJ') {
assertBase(base, 7);
return base + mod11(base, [2, 7, 6, 5, 4, 3, 2]);
}
throw new Error('UF não suportada. Escolha SP, MG ou RJ.');
}
function certificateChecksum(base) {
assertBase(base, 30);
const firstRemainder = weightedSum(base, Array.from({ length: 30 }, (_, index) => (index + 2) % 11)) % 11;
const first = firstRemainder === 10 ? 1 : firstRemainder;
const second = weightedSum(base + first, Array.from({ length: 31 }, (_, index) => (index + 1) % 11)) % 11;
return `${first}${second === 10 ? 1 : second}`;
}
function randomInt(maximum) {
const values = new Uint32Array(1);
const limit = Math.floor(0x100000000 / maximum) * maximum;
do {
crypto.getRandomValues(values);
} while (values[0] >= limit);
return values[0] % maximum;
}
function randomCharacters(length, alphabet = '0123456789') {
return Array.from({ length }, () => alphabet[randomInt(alphabet.length)]).join('');
}
function formatDocument(kind, value, state = 'SP') {
switch (kind) {
case 'cpf': return value.replace(/^(\d{3})(\d{3})(\d{3})(\d{2})$/, '$1.$2.$3-$4');
case 'cnpj': return value.replace(/^(.{2})(.{3})(.{3})(.{4})(\d{2})$/, '$1.$2.$3/$4-$5');
case 'rg': return value.replace(/^(\d{2})(\d{3})(\d{3})([\dX])$/, '$1.$2.$3-$4');
case 'titulo-eleitor': return value.replace(/^(\d{4})(\d{4})(\d{4})$/, '$1 $2 $3');
case 'pis-pasep': return value.replace(/^(\d{3})(\d{5})(\d{2})(\d)$/, '$1.$2.$3-$4');
case 'inscricao-estadual':
if (state === 'SP')
return value.replace(/^(\d{3})(\d{3})(\d{3})(\d{3})$/, '$1.$2.$3.$4');
if (state === 'MG')
return value.replace(/^(\d{3})(\d{3})(\d{3})(\d{4})$/, '$1.$2.$3/$4');
return value.replace(/^(\d{2})(\d{3})(\d{2})(\d)$/, '$1.$2.$3-$4');
case 'certidao': return value.replace(/^(\d{6})(\d{2})(\d{2})(\d{4})(\d)(\d{5})(\d{3})(\d{7})(\d{2})$/, '$1 $2 $3 $4 $5 $6 $7 $8-$9');
default: return value;
}
}
function generateDocument(kind, options = {}) {
const state = options.state ?? 'SP';
let value;
for (;;) {
switch (kind) {
case 'cpf': {
const base = randomCharacters(9);
value = base + cpfChecksum(base);
break;
}
case 'cnpj': {
const alphabet = options.cnpjFormat === 'alphanumeric' ? '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' : '0123456789';
let root = randomCharacters(8, alphabet);
if (options.cnpjFormat === 'alphanumeric' && !/[A-Z]/.test(root))
root = randomCharacters(1, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + root.slice(1);
const base = root + '0001';
value = base + cnpjChecksum(base);
break;
}
case 'rg': {
const base = randomCharacters(8);
value = base + rgChecksum(base);
break;
}
case 'cnh': {
const base = randomCharacters(9);
const checksum = cnhChecksum(base);
if (checksum === null)
continue;
value = base + checksum;
break;
}
case 'titulo-eleitor': {
const base = randomCharacters(8) + (options.electoralState ?? '01');
value = base + electoralChecksum(base);
break;
}
case 'pis-pasep': {
const base = '1' + randomCharacters(9);
value = base + pisChecksum(base);
break;
}
case 'inscricao-estadual': {
// MG: código municipal 062 e ordem de estabelecimento 00.
const base = state === 'MG' ? '062' + randomCharacters(6) + '00' : state === 'RJ' ? randomCharacters(7) : randomCharacters(10);
value = completeStateRegistration(base, state);
break;
}
case 'renavam': {
const base = '00' + randomCharacters(8);
value = base + renavamChecksum(base);
break;
}
case 'certidao': {
const year = options.year ?? new Date().getFullYear();
if (!Number.isInteger(year) || year < 1900 || year > new Date().getFullYear())
throw new Error('Informe um ano entre 1900 e o ano atual.');
const type = options.certificateType ?? '1';
if (!['1', '2', '4'].includes(type))
throw new Error('Tipo de certidão inválido.');
// CNS aleatório: não há consulta à existência de uma serventia.
const base = String(randomInt(999999) + 1).padStart(6, '0') + '0155' + year + type
+ String(randomInt(99999) + 1).padStart(5, '0')
+ String(randomInt(300) + 1).padStart(3, '0')
+ String(randomInt(9999999) + 1).padStart(7, '0');
value = base + certificateChecksum(base);
break;
}
default: throw new Error('Documento não suportado.');
}
if (/^(.)\1+$/.test(value))
continue;
return options.formatted ? formatDocument(kind, value, state) : value;
}
}
if (!Number.isInteger(count) || count < 1 || count > 100)
throw new Error('Quantidade: de 1 a 100');
if (typeof formatted !== 'boolean')
throw new Error('Máscara deve ser booleana');
const values = new Set();
for (let attempts = 0; values.size < count && attempts < count * 20; attempts++) {
values.add(generateDocument('certidao', { formatted, certificateType, year }));
}
if (values.size !== count)
throw new Error('Não foi possível completar o lote sem repetições');
return [...values].join('\n');
}
<?php
function executar($count = 1, $type = '1', $year = null, $formatted = true) {
if (!is_int($count) || $count < 1 || $count > 100) throw new InvalidArgumentException('Quantidade: de 1 a 100');
if (!is_bool($formatted)) throw new InvalidArgumentException('Máscara deve ser booleana');
$generate = function () use ($type, $year) {
$results = [];
$random_chars = function($n, $alphabet = "0123456789") use (&$random_chars, &$weighted_sum, &$dv) {
$value = "";
for ($i = 0; $i < $n; $i++) {
$value .= $alphabet[random_int(0, strlen($alphabet) - 1)];
}
return $value;
};
$weighted_sum = function($base, $weights, $alpha = false) use (&$random_chars, &$weighted_sum, &$dv) {
$sum = 0;
foreach (str_split($base) as $i => $c) {
$sum += ($alpha ? ord($c) - 48 : (int) $c) * $weights[$i];
}
return $sum;
};
$dv = function($base, $weights, $alpha = false) use (&$random_chars, &$weighted_sum, &$dv) {
$r = $weighted_sum($base, $weights, $alpha) % 11;
return $r < 2 ? 0 : 11 - $r;
};
// Matrícula sintética: CNS aleatório, acervo 01, serviço 55.
// Tipo: 1=nascimento, 2=casamento, 4=óbito.
$year ??= (int)date('Y');
if (!in_array($type, ['1','2','4'], true) || !is_int($year) || $year < 1900 || $year > (int)date('Y')) throw new InvalidArgumentException('Tipo ou ano inválido');
$pad = fn($max, $size) => str_pad((string) random_int(1,$max), $size, "0", STR_PAD_LEFT);
$base = $pad(999999,6) . "0155" . $year . $type
. $pad(99999,5) . $pad(300,3) . $pad(9999999,7);
$w1 = array_map(fn($i) => ($i+2)%11, range(0,29));
$w2 = array_map(fn($i) => ($i+1)%11, range(0,30));
$r1 = $weighted_sum($base,$w1) % 11;
$d1 = $r1 === 10 ? 1 : $r1;
$r2 = $weighted_sum($base . $d1,$w2) % 11;
$results[] = $base . $d1 . ($r2 === 10 ? 1 : $r2);
return count($results) === 1 ? $results[0] : $results;
};
$values = [];
for ($attempts = 0; count($values) < $count && $attempts < $count * 30; $attempts++) {
$value = $generate();
if ($formatted) {
$sizes = [6,2,2,4,1,5,3,7,2]; $separators = [" "," "," "," "," "," "," ","-"];
$cursor = 0; $parts = [];
foreach ($sizes as $index => $size) {
$parts[] = substr($value, $cursor, $size); $cursor += $size;
if (isset($separators[$index])) $parts[] = $separators[$index];
}
$value = implode('', $parts);
}
if (!in_array($value, $values, true)) $values[] = $value;
}
if (count($values) !== $count) throw new RuntimeException('Não foi possível completar o lote sem repetições');
return implode("\n", $values);
}