2024-03-27 10:51:48 +03:00
|
|
|
package inns
|
|
|
|
|
|
|
|
// The package implements the basic
|
|
|
|
// Russian Federation INN format
|
|
|
|
// storing it in int64 to keep
|
|
|
|
// searching and storing as
|
|
|
|
// quick as possible.
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"unicode"
|
|
|
|
"strconv"
|
|
|
|
"fmt"
|
|
|
|
"slices"
|
|
|
|
)
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
type Type uint8
|
2024-03-27 10:51:48 +03:00
|
|
|
const (
|
2024-05-31 16:58:28 +03:00
|
|
|
TypeIncorrect Type = iota
|
|
|
|
TypeLegal
|
|
|
|
TypeIndividual
|
2024-03-27 10:51:48 +03:00
|
|
|
)
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
|
2024-03-27 10:51:48 +03:00
|
|
|
func removeSpaces(s string) string {
|
|
|
|
return strings.Map(func(r rune) rune{
|
|
|
|
if unicode.IsSpace(r) {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}, s)
|
|
|
|
}
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
type INN int64
|
2024-03-27 10:51:48 +03:00
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
func NewFromInt64(innRaw int64) (INN, error) {
|
|
|
|
inn := INN(innRaw)
|
2024-03-27 10:51:48 +03:00
|
|
|
if !inn.isCorrectLen() {
|
2024-05-31 16:58:28 +03:00
|
|
|
return -1, ErrIncorrectLen
|
2024-03-27 10:51:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if !inn.isSumCorrect() {
|
2024-05-31 16:58:28 +03:00
|
|
|
return -1, ErrSumsNotMatch
|
2024-03-27 10:51:48 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return inn, nil
|
|
|
|
|
|
|
|
}
|
2024-05-31 16:58:28 +03:00
|
|
|
|
|
|
|
func (inn INN) IsCorrect() bool {
|
|
|
|
_, err := NewFromInt64(int64(inn))
|
|
|
|
return err == nil
|
|
|
|
}
|
2024-03-27 10:51:48 +03:00
|
|
|
// Convert string with spaces into the INN
|
|
|
|
// and return error if anything is wrong with it.
|
2024-05-31 16:58:28 +03:00
|
|
|
func NewFromStr(str string) (INN, error) {
|
2024-03-27 10:51:48 +03:00
|
|
|
str = removeSpaces(str)
|
|
|
|
innRaw, err := strconv.ParseInt(str, 10, 64)
|
|
|
|
if err != nil {
|
2024-05-31 16:58:28 +03:00
|
|
|
return -1, ErrIncorrectFormat
|
2024-03-27 10:51:48 +03:00
|
|
|
}
|
|
|
|
return NewFromInt64(innRaw)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) Type() Type {
|
2024-03-27 10:51:48 +03:00
|
|
|
ln := inn.length()
|
|
|
|
switch ln {
|
|
|
|
case 9, 10:
|
2024-05-31 16:58:28 +03:00
|
|
|
return TypeLegal
|
2024-03-27 10:51:48 +03:00
|
|
|
case 11, 12:
|
2024-05-31 16:58:28 +03:00
|
|
|
return TypeIndividual
|
2024-03-27 10:51:48 +03:00
|
|
|
}
|
2024-05-31 16:58:28 +03:00
|
|
|
return TypeIncorrect
|
2024-03-27 10:51:48 +03:00
|
|
|
}
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) length() int {
|
2024-03-27 10:51:48 +03:00
|
|
|
ln := 0
|
|
|
|
for {
|
|
|
|
if inn == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
inn /= 10
|
|
|
|
ln++
|
|
|
|
}
|
|
|
|
if ln == 9 || ln == 11 {
|
|
|
|
ln++
|
|
|
|
}
|
|
|
|
return ln
|
|
|
|
}
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) isCorrectLen() bool {
|
2024-03-27 10:51:48 +03:00
|
|
|
ln := inn.length()
|
|
|
|
if ln == 9 || ln == 11 {
|
|
|
|
ln++
|
|
|
|
}
|
|
|
|
return ln == 10 || ln == 12
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
// Koefs for the control sum.
|
|
|
|
innSum10 = [9]int64{
|
|
|
|
2, 4, 10, 3, 5, 9, 4, 6, 8,
|
|
|
|
}
|
|
|
|
innSum11 = [10]int64{
|
|
|
|
7, 2, 4, 10, 3, 5, 9, 4, 6, 8,
|
|
|
|
}
|
|
|
|
innSum12 = [11]int64{
|
|
|
|
3, 7, 2, 4, 10, 3, 5, 9, 4, 6, 8,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
// Get the integer representation of digits in string INN.
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) digits() []int64 {
|
2024-03-27 10:51:48 +03:00
|
|
|
ln := inn.length()
|
|
|
|
v := int64(inn)
|
|
|
|
ret := make([]int64, ln)
|
|
|
|
for i := 0 ; i<ln ; i++{
|
|
|
|
ret[i] = v % 10
|
|
|
|
v /= 10
|
|
|
|
}
|
|
|
|
slices.Reverse(ret)
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) specifiedSum() int64 {
|
2024-03-27 10:51:48 +03:00
|
|
|
ln := inn.length()
|
|
|
|
v := int64(inn)
|
|
|
|
switch ln {
|
|
|
|
case 10:
|
|
|
|
return v%10
|
|
|
|
case 12:
|
|
|
|
return v%100
|
|
|
|
default:
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the supposed control sum rune. (the last number)
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) sum() int64 {
|
2024-03-27 10:51:48 +03:00
|
|
|
d := inn.digits()
|
|
|
|
ln := inn.length()
|
|
|
|
switch ln {
|
|
|
|
case 10 :
|
|
|
|
var n10 int64
|
|
|
|
for i, ni := range d[:9] {
|
|
|
|
n10 += innSum10[i] * ni
|
|
|
|
}
|
|
|
|
n10 = (n10 % 11) % 10
|
|
|
|
return n10
|
|
|
|
case 12 :
|
|
|
|
var n11 int64
|
|
|
|
for i, ni := range d[:10] {
|
|
|
|
n11 += innSum11[i] * ni
|
|
|
|
}
|
|
|
|
n11 = (n11 % 11) % 10
|
|
|
|
|
|
|
|
var n12 int64
|
|
|
|
for i, ni := range d[:10] {
|
|
|
|
n12 += innSum12[i] * ni
|
|
|
|
}
|
|
|
|
n12 += innSum12[10] * n11
|
|
|
|
n12 = (n12 % 11) % 10
|
|
|
|
|
|
|
|
return n11*10 + n12
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) isSumCorrect() bool {
|
2024-03-27 10:51:48 +03:00
|
|
|
return inn.specifiedSum() == inn.sum()
|
|
|
|
}
|
|
|
|
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) String() string {
|
2024-03-27 10:51:48 +03:00
|
|
|
return fmt.Sprintf("%d", inn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fancy way to print INN with space delimiters.
|
2024-05-31 16:58:28 +03:00
|
|
|
func (inn INN) Fancy() string {
|
2024-03-27 10:51:48 +03:00
|
|
|
ln := inn.length()
|
|
|
|
str := fmt.Sprintf("%d", inn)
|
|
|
|
|
|
|
|
// Adding zero depending on length.
|
|
|
|
if len(str) == 9 || len(str) == 11 {
|
|
|
|
str = "0"+str
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ln {
|
|
|
|
case 10 :
|
|
|
|
return fmt.Sprintf("%s %s %s", str[:4], str[4:9], str[9:])
|
|
|
|
case 12 :
|
|
|
|
return fmt.Sprintf("%s %s %s", str[:4], str[4:10], str[10:])
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reaching the point if the length is incorrect.
|
2024-05-31 16:58:28 +03:00
|
|
|
return "<"+ErrIncorrectLen.Error()+">"
|
2024-03-27 10:51:48 +03:00
|
|
|
}
|
|
|
|
|