using PetsMall.Common;
using PetsMall.Model.Entites;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CommonUtil
{
/// <summary>
/// 生成编号
/// 编号模拟数字进制规则,可以用一个数字序号表示,字符在字符数组中下标表示数值,如编号“0”等于序号0,如字符集Sort中编号“A”等于序号9。
/// 编号转为常见进制的序号,表示的数值可能非常大,无法计算。固用int数组内每个数值表示每个字符在字符数组下标,来转换数字和字符。
/// 可用构造参数设置初始编号,序号将从初始编号开始,即初始编号等于序号0;
///
/// 例如ReportNumberSort为自增
/// testItem.ReportNumber = "";
/// db.Set<Order_TestItem>().Add(testItem);
/// db.SaveChanges();
/// testItem.ReportNumber = NumberCreateHelper.NotSort.No_SortToNumberString(testItem.ReportNumberSort);
/// db.SaveChanges();
/// </summary>
public class NumberCreateHelper
{
/// <summary>
/// 生成编号显示字符,字符顺序
/// </summary>
public static readonly NumberCreateHelper Sort = new NumberCreateHelper(new char[] { '0', '1', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'X', 'Y', 'Z' }
, ConfigurationHelper.AppSetting("NumberCreateHelper_InitNumber_Sort"));
/// <summary>
/// 生成编号显示字符,乱序
/// </summary>
public static readonly NumberCreateHelper NotSort = new NumberCreateHelper(new char[] { 'D', 'E', '0', '1', 'Q', 'C', 'F', 'G', '5', 'N', 'P', 'M', '6', 'R', 'X', '7', 'K', '8', 'S', 'T', '9', 'A', '3', '4', 'B', 'H', 'Y', 'Z' }
, ConfigurationHelper.AppSetting("NumberCreateHelper_InitNumber_NotSort"));
readonly char[] NO_pattern;
readonly List<char> NO_patternList;
readonly string initNumber;
readonly int[] initInts;
private NumberCreateHelper() { }
/// <summary>
///
/// </summary>
/// <param name="_NO_pattern">使用字符集,字符集下标对应int数组的数值,与int数组转换</param>
/// <param name="_initNumber">初始编号,使用序号取编号,将从初始编号开始,初始编号等于序号0</param>
private NumberCreateHelper(char[] _NO_pattern, string _initNumber = "")
{
if (_NO_pattern != null)
{
NO_pattern = _NO_pattern;
}
NO_patternList = NO_pattern.ToList();
if (string.IsNullOrWhiteSpace(_initNumber))
{
_initNumber = NO_pattern[0].ToString();
}
initNumber = _initNumber;
initInts = NO_NumberStringToInts(initNumber);
}
#region 生成编号
/// <summary>
/// 生成编号
/// </summary>
/// <param name="len">编号位数</param>
/// <param name="v">编号数值</param>
/// <returns></returns>
public string NO_CreateNumberString(int len, out int[] v)
{
//生成len个小于_pattern.Length的整数
Random random = new Random(~unchecked((int)DateTime.Now.Ticks));
v = new int[len];
string nu = "";
for (int i = 0; i < len; i++)
{
v[i] = random.Next(0, NO_pattern.Length);
nu += NO_pattern[v[i]];
}
return nu;
}
/// <summary>
/// 下一个编号
/// </summary>
/// <returns></returns>
public string NO_NumberStringNext(string nu)
{
var v = NO_NumberStringToInts(nu);
v = NO_IntsIncreaseOne(v);
return NO_IntsToNumberString(v);
}
/// <summary>
/// int数组转编号字符
/// </summary>
/// <param name="nu"></param>
/// <returns></returns>
public string NO_IntsToNumberString(int[] v)
{
string nu = "";
for (int i = 0; i < v.Length; i++)
{
nu += NO_pattern[v[i]];
}
return nu;
}
/// <summary>
/// 编号字符转int数组
/// </summary>
/// <param name="nu"></param>
/// <returns></returns>
public int[] NO_NumberStringToInts(string nu)
{
int[] v = new int[nu.Length];
for (int i = 0; i < nu.Length; i++)
{
v[i] = NO_patternList.IndexOf(nu[i]);
}
return v;
}
/// <summary>
/// int数组增加1
/// </summary>
/// <param name="v"></param>
/// <param name="index">当前执行下标,初始请默认为null</param>
/// <returns></returns>
private int[] NO_IntsIncreaseOne(int[] v, int? index = null)
{
int i = index ?? v.Length - 1;
if (i < 0)
{
//无法增加
throw new Exception("CreateReportNumberLast 无法增加,超出模块支持数字界限");
return null;
}
if (v[i] < NO_pattern.Length - 1)
{
v[i]++;
return v;
}
else
{
v[i] = 0;
return NO_IntsIncreaseOne(v, i - 1);
}
}
/// <summary>
/// int数组减少1
/// </summary>
/// <param name="v"></param>
/// <param name="index">当前执行下标,初始请默认为null</param>
/// <returns></returns>
private int[] NO_IntsReduceOne(int[] v, int? index = null)
{
int i = index ?? v.Length - 1;
if (i < 0)
{
//无法减少
throw new Exception("CreateReportNumberLast 无法减少,模块不支持负数");
return null;
}
if (v[i] > 0)
{
v[i]--;
return v;
}
else
{
v[i] = 0;
return NO_IntsReduceOne(v, i - 1);
}
}
/// <summary>
/// 数组相减得到序号
/// </summary>
/// <param name="left">数组减法左边</param>
/// <param name="right">数组减法右边,被减去</param>
/// <param name="index">当前执行以右侧开锁计算的下标,初始请默认为null</param>
/// <param name="max">字符集长度表示数字数组长度,相当于进制上限</param>
/// <returns></returns>
private int No_IntsReduceInts(int[] left, int[] right, int? index = null, int max = 0)
{
//max类型进制位数,满max进1
if (max <= 0)
{
max = NO_pattern.Length;
}
//首次计算,将数组位数对齐
if (index == null)
{
var lList = left.ToList();
int lIndex = lList.FindIndex(x => x > 0);
if (lIndex > 0)
{
//移除减数左边的0
lList.RemoveRange(0, lIndex);
left = lList.ToArray();
}
var rList = right.ToList();
int rIndex = rList.FindIndex(x => x > 0);
if (rIndex < 0)
{
throw new Exception("CreateReportNumberLast 无法计算序号,被减编号数组为负");
////减负数和0都不变
//return NO_IntsToInt(left);
}
else if (rIndex > 0)
{
//移除被减数左边的0
rList.RemoveRange(0, rIndex);
right = rList.ToArray();
}
if (right.Length > left.Length)
{
throw new Exception("CreateReportNumberLast 无法计算序号,被减编号数组长度大于原编号数组");
////被减位数大于减数位数,不变
//return NO_IntsToInt(left);
}
index = right.Length - 1;
}
int i = index.Value;
//计算数组相减
if (left[right.Length - left.Length + i] >= right[i])
{
left[right.Length - left.Length + i] -= right[i];
i--;
if (i < 0)
{
return NO_IntsToInt(left);
}
else
{
return No_IntsReduceInts(left, right, i);
}
}
else
{
//无高位可取
if (right.Length - left.Length + i - 1 < 0)
{
throw new Exception("CreateReportNumberLast 无法计算序号,被减编号数组数值大于原编号数组");
}
//取高位减,被取高位为0,则会为负1,下一次递归还会到这里继续取高位
left[right.Length - left.Length + i - 1]--;
//如果当前位的left被上一次取高位到负1,则会到这里变为max-1。
left[right.Length - left.Length + i] += max;
return No_IntsReduceInts(left, right, i);
}
}
/// <summary>
/// 顺序序号转数组
/// 编号表示数字很大,int类型不够作为参数用此方法转换,需配合初始编号使用
/// </summary>
/// <param name="sort"></param>
/// <returns></returns>
public int[] NO_SortNoToInts(int sort, int minLen = 0, int max = 0)
{
//max类型进制位数,满max进1
if (max <= 0)
{
max = NO_pattern.Length;
}
List<int> res = new List<int>();
int now = sort;
while (now > 0)
{
res.Add(now % max);//取余
now = now / max;//偏移
}
//补充0,
if (minLen > 0 && res.Count() < minLen)
{
int count = res.Count();
for (int i = 0; i < minLen - count; i++)
{
res.Add(0);
}
}
res.Reverse();
return res.ToArray();
}
/// <summary>
/// 数组转顺序序号
/// 编号表示数字很大,容易数字溢出,需配合初始编号使用
/// </summary>
/// <param name="ints"></param>
/// <param name="max"></param>
/// <returns></returns>
public int NO_IntsToInt(int[] ints, int max = 0)
{
if (max <= 0)
{
max = NO_pattern.Length;
}
int res = 0;//ints[ints.Length-1]
for (int i = ints.Length - 1; i > 0; i--)
{
res += (ints[i] * (int)Math.Pow(max, (ints.Length - 1) - i));
}
if (res < 0)
{
throw new Exception("CreateReportNumberLast 无法转为序号,数组表示数值溢出");
}
return res;
}
/// <summary>
/// 序号转编号
/// </summary>
/// <param name="sort"></param>
/// <returns></returns>
public string No_SortToNumberString(int sort)
{
int[] ints = new int[initInts.Length];
initInts.CopyTo(ints, 0);
if (sort > 0)
{
for (int i = 0; i < sort; i++)
{
ints = NO_IntsIncreaseOne(ints);
}
}
return NO_IntsToNumberString(ints);
}
/// <summary>
/// 编号转序号
/// </summary>
/// <param name="sort"></param>
/// <returns></returns>
public int No_NumberStringToSort(string nu)
{
int[] ints = new int[initInts.Length];
initInts.CopyTo(ints, 0);
return No_IntsReduceInts(NO_NumberStringToInts(nu), ints);
}
#endregion
}
}