using Island.StandardLib.Storage;
using System;
namespace Island.StandardLib.Math
{
///
/// 表示一个总和为1的平衡容器
///
public class Percentage : IStorable
{
StorableFixedArray percentages;
public int KeyLength => percentages.Length;
public float Maxium
{
get
{
float maxium = percentages[0].Value;
for (int i = 1; i < percentages.Count; i++)
if (percentages[i].Value > maxium) maxium = percentages[i].Value;
return maxium;
}
}
public int MaxiumId
{
get
{
float maxium = percentages[0].Value;
int id = 0;
for (int i = 1; i < percentages.Count; i++)
if (percentages[i].Value > maxium)
{
maxium = percentages[i].Value;
id = i;
}
return id;
}
}
public float Minium
{
get
{
float minium = percentages[0].Value;
for (int i = 1; i < percentages.Count; i++)
if (percentages[i].Value < minium) minium = percentages[i].Value;
return minium;
}
}
public int MiniumId
{
get
{
float minium = percentages[0].Value;
int id = 0;
for (int i = 1; i < percentages.Count; i++)
if (percentages[i].Value < minium)
{
minium = percentages[i].Value;
id = i;
}
return id;
}
}
public Percentage() { }
///
/// 初始化容器
///
/// 初始数据
public Percentage(params float[] sourceValue)
{
percentages = new StorableFixedArray();
if (sourceValue.Length == 0) throw new Exception();
float val = 0;
for (int i = 0; i < sourceValue.Length; i++)
{
val += sourceValue[i];
percentages.Add(new Key(sourceValue[i]));
}
if (val != 1f)
AdjustBalance();
}
///
/// 获取和设置数据。其中,设置数据后会自动按比例平衡容器,保证容器总和为1
///
/// 数据序号
///
public float this[int index]
{
get => percentages[index].Value;
set => AdjustKeep(index, value);
}
public float[] SelectBalance(params int[] selectedIndex)
{
float[] values = new float[selectedIndex.Length];
for (int i = 0; i < selectedIndex.Length; i++)
values[i] = percentages[selectedIndex[i]].Value;
float total = 0f;
for (int i = 0; i < values.Length; i++)
total += values[i];
float px = 1 / total;
for (int i = 0; i < values.Length; i++)
values[i] *= px;
return values;
}
public int Random(int seed = 0)
{
Random rd = seed == 0 ? new Random() : new Random(seed);
double select = rd.NextDouble();
float thisMin = 0f;
for (int i = 0; i < KeyLength; i++)
{
float thisMax = thisMin + this[i];
if (select >= thisMin && select < thisMax)
return i;
thisMin = thisMax;
}
return -1;
}
public long Total(int index, long totalValue)
{
return (long)(this[index] * totalValue);
}
void AdjustBalance()
{
float total = 0f;
for (int i = 0; i < KeyLength; i++)
total += this[i];
float px = 1 / total;
for (int i = 0; i < KeyLength; i++)
percentages[i].Value *= px;
}
void AdjustKeep(int keepIndex, float newValue)
{
AdjustBalance();
float distance = this[keepIndex] - newValue;
percentages[keepIndex].Value = newValue;
float[] gains = new float[KeyLength - 1];
int g = 0;
for (int i = 0; i < KeyLength; i++)
{
if (keepIndex == i)
continue;
gains[g] = this[i]; g++;
}
Percentage p = new Percentage(gains);
g = 0;
for (int i = 0; i < KeyLength; i++)
{
if (keepIndex == i)
continue;
percentages[i].Value += p[g] * distance; g++;
}
}
public override string ToString()
{
string s = "Percentage [";
float total = 0f;
for (int i = 0; i < KeyLength; i++)
{
total += this[i];
if (i == KeyLength - 1)
s += this[i].ToString("P");
else s += this[i].ToString("P") + ", ";
}
return s + "]";
}
public void ReadFromData(DataStorage data)
{
data.Read(out percentages);
}
public void WriteToData(DataStorage data)
{
data.Write(percentages);
}
class Key : IStorable
{
public float Value;
public Key() => Value = 0f;
public Key(float sourceValue) => Value = sourceValue;
public void ReadFromData(DataStorage data) => data.Read(out Value);
public void WriteToData(DataStorage data) => data.Write(Value);
}
}
}