using System;
using System.Collections.Generic;
using System.Linq;

[Serializable]
public abstract class ChooseData {
    public float ratio;
}

public static class ListExtensions{

    public static T Head<T>(this List<T> list){
        return list[0];
    }
    public static List<T> Tail<T>(this List<T> list){
        return new List<T>(list.Skip(1));
    }
    public static T Front<T>(this List<T> list){
        return list[0];
    }
    public static T Back<T>(this List<T> list){
        return list[list.Count - 1];
    }

    public static bool Empty<T>(this List<T> list){
        return list.Count == 0;
    }

    public static List<T> Shuffle<T>(this List<T> list){
        return Shuffle(list, new System.Random());
    }
    public static List<T> Shuffle<T>(this List<T> list, System.Random rand){
        int n = list.Count;
        while (n > 1) {
            --n;
            int k = rand.Next(n + 1);
            T value = list[k];
            list[k] = list[n];
            list[n] = value;
        }
        return list;
    }

    public static T RandomChoose<T>(this IList<T> list){
        return list.RandomChoose(new System.Random());
    }
    public static T RandomChoose<T>(this IList<T> list, System.Random rand){
        return list[rand.Next(list.Count)];
    }
    public static List<T> RandomChoose<T>(this IList<T> list, int count){
        return list.RandomChoose(count, new System.Random());
    }
    public static List<T> RandomChoose<T>(this IList<T> list, int count, System.Random rand){
        if(count <= 0){
            return new List<T>();
        }
        var tempList = new List<T>(list);
        if(list.Count < count){
            return tempList;
        }

        var resultList = new List<T>();
        for(int i = 0; i < count; ++i){
            var result = tempList.RandomChoose(rand);
            tempList.Remove(result);
            resultList.Add(result);
        }
        return resultList;
    }

    public static T RandomChooseRatio<T>(this IEnumerable<T> list) where T : ChooseData{
        return list.RandomChooseRatio(new System.Random());
    }
    public static T RandomChooseRatio<T>(this IEnumerable<T> list, System.Random rand) where T : ChooseData{
        float totalRatio = list.Sum(i => i.ratio);
        float targetRatio = (float)rand.NextDouble() * totalRatio;
        float r = 0.0f;
        T result = null;
        foreach(var item in list){
            r += item.ratio;
            if(r > targetRatio){
                result = item;
                break;
            }
        }
        return result;
    }
    public static List<T> RandomChooseRatio<T>(this List<T> list, int count) where T : ChooseData {
        return list.RandomChooseRatio(count, new System.Random());
    }
    public static List<T> RandomChooseRatio<T>(this List<T> list, int count, System.Random rand) where T : ChooseData {
        if(list.Count < count) return list;
        var chooseList = new List<T>(list);
        var resultList = new List<T>();
        for(int i = 0; i < count; ++i){
            var result = chooseList.RandomChooseRatio(rand);
            chooseList.Remove(result);
            resultList.Add(result);
        }
        return resultList;
    }
}