{"version":3,"sources":["../../src/actors/opinionsArray.ts","../../src/utils.ts"],"sourcesContent":["import { range, sum } from \"@gouvernathor/python\";\nimport * as math from \"@gouvernathor/python/math\";\nimport { createRandomObj, type RandomObjParam, type ReadonlyTuple, type Tuple } from \"../utils\";\n\n/**\n * Converts a normal distribution to an uniform distribution.\n *\n * (This function is on its own so that the code can be checked by stats people.)\n *\n * @param x generated from a normal distribution\n * @param mu mean value of the normal distribution\n * @param sigma standard deviation of the normal distribution\n * @returns a value following an uniform distribution between 0 and 1,\n * such that the higher the value of x, the higher the return value.\n */\nfunction normalToUniform(x: number, mu: number, sigma: number): number {\n    return 0.5 * (1 + math.erf((x - mu) / (sigma * Math.SQRT2)));\n}\n\n/**\n * A set of opinions represented as a multi-dimensional vector of integers,\n * with each dimension (each vector member) representing one subject on which\n * an opinion is held.\n * The value in each dimension can be symmetrically positive or negative.\n * Values close to 0 represent neutrality or indecision.\n * The type parameter should be a number giving the number of opinions,\n * of elements in the array.\n * The range of values in the array is from -opinMax to +opinMax,\n * inclusive, as provided to the OpinionsArrayManager object.\n */\nexport type OpinionsArray<N extends number> = Tuple<number, N>;\n\n/**\n * Returns a one-dimensional alignment of the opinions.\n *\n * @param opinions array of opinions\n * @returns a value such that if each opinion, in each member of a pool\n * of Opinionated instances, is generated randomly following an integer uniform distribution,\n * the return value of this function follows a uniform distribution.\n * With the default factors, the return value is between 0 and 1,\n * and is increasing with each separate opinion,\n * with opinions having a decreasing importance as the array goes.\n */\nexport interface Aligner<N extends number> {\n    (opinions: Readonly<OpinionsArray<N>>): number;\n}\n\n/**\n * @param opinMax maximum possible opinion value\n * @param factors ponderation for each opinion's importance in the alignment\n * @returns an aligner function to turns the opinions array\n * into a one-dimensional alignment value.\n */\nfunction getAligner<N extends number>(\n    opinMax: number,\n    factors: readonly number[], // not Tuple because annoying to cast and internal function\n): Aligner<N> {\n    const rang = range(-opinMax, opinMax+1);\n    // standard deviation of one opinion taken on its own\n    const oneSigma = Math.sqrt(sum(rang.map(i => i ** 2)) / rang.length);\n    // using Lyapunov's central limit theorem\n    const sigma = math.hypot(...factors.map(f => f * oneSigma));\n\n    return (opinions) => {\n        const scapro = sum((<number[]><any>opinions).map((opinion, i) => opinion * factors[i]));\n        return normalToUniform(scapro, 0, sigma);\n    };\n}\n\n/**\n * @returns factors such that the return value of the aligner function\n * is between 0 and 1, and is increasing with each separate opinion,\n * with opinions having a decreasing importance.\n */\nfunction getDefaultAlignmentFactors<N extends number>(nOpinions: N) {\n    return Array.from({length: nOpinions}, (_, i) => 1 - i / nOpinions) as Tuple<N, number>;\n}\n\n/**\n * This holds the parameters for the opinions arrays : their length,\n * their range of values, and how to align them to one dimension.\n *\n * It can be seen as a metaclass for the opinions arrays, except that\n * they are real arrays instead of a custom type, so this is a normal class.\n *\n * There should generally be only one instance of this class per system,\n * and all opinions arrays should share the same parameters -\n * at least the same length and value range - even for opinions of different kinds,\n * for instance parties, citizens, and bills.\n * In any case, disagreement should not pe supported between subclasses\n * of different nOpinions or opinMax values.\n *\n * Other than being a repository for the meta parameters,\n * this class provides :\n * - a balanced single-dimensional alignment for all opinions arrays\n * - a random generator for the opinions arrays\n */\nexport class OpinionsArrayManager<N extends number> {\n    /**\n     * The number of opinions in each array, equal to the type parameter,\n     * provided for runtime access.\n     */\n    readonly nOpinions: N;\n    /**\n     * The maximum possible value of each opinion in the array.\n     */\n    readonly opinMax: number;\n    readonly opinionAlignmentFactors: ReadonlyTuple<N, number>;\n\n    /**\n     * This function takes an opinions array and returns a single value\n     * representing the alignment of this set of opinions in a single axis.\n     * If using the default opinion alignment factors, the alignment is between 0 and 1.\n     */\n    readonly aligner: Aligner<N>;\n    /**\n     * A random generator for the opinions arrays.\n     * You can pass a random object generator, or a seed that will be used\n     * to initialize a random object.\n     *\n     * Each opinion value in the array will be generated following a uniform law.\n     */\n    readonly generator: (p?: RandomObjParam) => OpinionsArray<N>;\n\n    constructor({\n        nOpinions, opinMax,\n        opinionAlignmentFactors = getDefaultAlignmentFactors(nOpinions)\n    }: {\n        nOpinions: N, opinMax: number,\n        opinionAlignmentFactors?: ReadonlyTuple<N, number>,\n    }) {\n        this.nOpinions = nOpinions;\n        this.opinMax = opinMax;\n        this.opinionAlignmentFactors = opinionAlignmentFactors;\n\n        this.aligner = getAligner<N>(opinMax, opinionAlignmentFactors);\n        this.generator = (randomParam?: RandomObjParam) =>\n            createRandomObj(randomParam)\n                .choices(range(-opinMax, opinMax+1), { k: nOpinions }) as OpinionsArray<N>;\n    }\n}\n","import RNG from \"@gouvernathor/rng\";\n\nexport type RandomObjParam = {randomObj: RNG} | {randomSeed?: number|string};\n\nexport function createRandomObj(param?: RandomObjParam): RNG;\nexport function createRandomObj({ randomObj, randomSeed }:\n    { randomObj?: RNG, randomSeed?: number | string } = {},\n): RNG {\n    if (randomObj === undefined) {\n        randomObj = new RNG(randomSeed);\n    }\n    return randomObj;\n}\n\n\n// https://stackoverflow.com/a/71700658\n\n/**\n * Mutable tuple of a single element type and a given length.\n *\n * Warning: due to limitations in TypeScript, when N is unknown/generic,\n * the typing system does not recognize that the type extends array.\n * Using methods such as map will require a (double) cast first to any then to T[].\n */\nexport type Tuple<\n  T,\n  N extends number,\n  R extends T[] = [],\n> = R['length'] extends N ? R : Tuple<T, N, [T, ...R]>;\n\n/**\n * Immutable version of Tuple.\n *\n * Warning: due to limitations in TypeScript, when N is unknown/generic,\n * the typing system does not recognize that the type extends readonly array.\n * Using methods such as map will require a (double) cast first to any then to readonly T[].\n */\nexport type ReadonlyTuple<T, N extends number> = Readonly<Tuple<T, N>>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA2B;AAC3B,WAAsB;;;ACDtB,iBAAgB;AAKT,SAAS,gBAAgB,EAAE,WAAW,WAAW,IACA,CAAC,GAClD;AACH,MAAI,cAAc,QAAW;AACzB,gBAAY,IAAI,WAAAA,QAAI,UAAU;AAAA,EAClC;AACA,SAAO;AACX;;;ADGA,SAAS,gBAAgB,GAAW,IAAY,OAAuB;AACnE,SAAO,OAAO,IAAS,UAAK,IAAI,OAAO,QAAQ,KAAK,MAAM;AAC9D;AAoCA,SAAS,WACL,SACA,SACU;AACV,QAAM,WAAO,qBAAM,CAAC,SAAS,UAAQ,CAAC;AAEtC,QAAM,WAAW,KAAK,SAAK,mBAAI,KAAK,IAAI,OAAK,KAAK,CAAC,CAAC,IAAI,KAAK,MAAM;AAEnE,QAAM,QAAa,WAAM,GAAG,QAAQ,IAAI,OAAK,IAAI,QAAQ,CAAC;AAE1D,SAAO,CAAC,aAAa;AACjB,UAAM,aAAS,mBAAoB,SAAU,IAAI,CAAC,SAAS,MAAM,UAAU,QAAQ,CAAC,CAAC,CAAC;AACtF,WAAO,gBAAgB,QAAQ,GAAG,KAAK;AAAA,EAC3C;AACJ;AAOA,SAAS,2BAA6C,WAAc;AAChE,SAAO,MAAM,KAAK,EAAC,QAAQ,UAAS,GAAG,CAAC,GAAG,MAAM,IAAI,IAAI,SAAS;AACtE;AAqBO,IAAM,uBAAN,MAA6C;AAAA,EA2BhD,YAAY;AAAA,IACR;AAAA,IAAW;AAAA,IACX,0BAA0B,2BAA2B,SAAS;AAAA,EAClE,GAGG;AACC,SAAK,YAAY;AACjB,SAAK,UAAU;AACf,SAAK,0BAA0B;AAE/B,SAAK,UAAU,WAAc,SAAS,uBAAuB;AAC7D,SAAK,YAAY,CAAC,gBACd,gBAAgB,WAAW,EACtB,YAAQ,qBAAM,CAAC,SAAS,UAAQ,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC;AAAA,EACjE;AACJ;","names":["RNG"]}