<commit_before>import {ListRow} from './list-row';
import {FirebaseDataModel} from './firebase-data-model';
import {CraftAddition} from './craft-addition';
import {GarlandToolsService} from '../../core/api/garland-tools.service';
import {I18nToolsService} from '../../core/i18n-tools.service';
import {MathTools} from 'app/tools/math-tools';

export class List extends FirebaseDataModel {
    name: string;
    recipes: ListRow[] = [];
    preCrafts: ListRow[] = [];
    gathers: ListRow[] = [];
    others: ListRow[] = [];
    crystals: ListRow[] = [];
    createdAt: string = new Date().toISOString();
    version: string;

    favorites: string[] = [];

    constructor() {
        super();
    }

    public clone(): List {
        const clone = new List();
        for (const prop of Object.keys(this)) {
            if (['recipes', 'preCrafts', 'gathers', 'others', 'crystals'].indexOf(prop) > -1) {
                clone[prop] = this[prop];
            }
        }
        clone.name = this.name;
        clone.version = this.version;
        delete clone.$key;
        return clone;
    }

    public forEachItem(method: (arg: ListRow) => void): void {
        (this.others || []).forEach(method);
        (this.gathers || []).forEach(method);
        (this.preCrafts || []).forEach(method);
    }

    public addToRecipes(data: ListRow): number {
        return this.add(this.recipes, data);
    }

    public addToPreCrafts(data: ListRow): number {
        return this.add(this.preCrafts, data);
    }

    public addToGathers(data: ListRow): number {
        return this.add(this.gathers, data);
    }

    public addToOthers(data: ListRow): number {
        return this.add(this.others, data);
    }

    public addToCrystals(data: ListRow): number {
        return this.add(this.crystals, data);
    }

    private add(array: ListRow[], data: ListRow): number {
        let previousAmount = 0;
        let row = array.find(r => {
            return r.id === data.id;
        });
        if (row === undefined) {
            array.push(data);
            row = array[array.length - 1];
        } else {
            row.amount = MathTools.round(row.amount + data.amount);
            if (row.amount < 0) {
                row.amount = 0;
                row.amount_needed = row.amount;
            }
            previousAmount = row.amount_needed;
        }
        row.amount_needed = MathTools.absoluteCeil(row.amount / row.yield);
        return row.amount_needed - previousAmount;
    }

    public clean(): List {
        for (const prop of Object.keys(this)) {
            if (['recipes', 'preCrafts', 'gathers', 'others', 'crystals'].indexOf(prop) > -1) {
                this[prop] = this[prop].filter(row => {
                    return row.amount > 0;
                });
            }
        }
        return this;
    }

    public isEmpty(): boolean {
        return this.recipes.length === 0 &&
            this.preCrafts.length === 0 &&
            this.gathers.length === 0 &&
            this.others.length === 0 &&
            this.crystals.length === 0;
    }

    public getItemById(id: number, addedAt?: number): ListRow {
        for (const prop of Object.keys(this)) {
            if (prop !== 'name') {
                for (const row of this[prop]) {
                    if (row.id === id) {
                        if (addedAt !== undefined) {
                            if (addedAt === row.addedAt) {
                                return row;
                            }
                        } else {
                            return row;
                        }
                    }
                }
            }
        }
        return undefined;
    }

    public setDone(pitem: ListRow, amount: number, recipe: boolean = false): void {
        const item = this.getItemById(pitem.id, pitem.addedAt);
        item.done += amount;
        if (item.done > item.amount) {
            item.done = item.amount;
        }
        if (item.done < 0) {
            item.done = 0;
        }
        if (recipe) {
            amount = MathTools.absoluteCeil(amount / pitem.yield);
        }
        if (item.requires !== undefined) {
            for (const requirement of item.requires) {
                const requirementItem = this.getItemById(requirement.id);
                this.setDone(requirementItem, MathTools.absoluteCeil(requirement.amount * amount / requirementItem.yield));
            }
        }
    }

    /**
     * Checks if the list is outdated, the implementation is meant to change.
     * @returns {boolean}
     */
    public isOutDated(): boolean {
        if (this.isEmpty()) {
            return false;
        }
        let res = false;
        this.forEachItem(i => {
            res = res || (i.amount_needed === undefined);
            if (i.gatheredBy !== undefined) {
                res = res || (i.gatheredBy.type === undefined);
            }
        });
        res = res || (this.version === undefined);
        return res;
    }
<commit_msg>fix: bad maths with multiple yielding precrafts<commit_after>import {ListRow} from './list-row';
import {FirebaseDataModel} from './firebase-data-model';
import {CraftAddition} from './craft-addition';
import {GarlandToolsService} from '../../core/api/garland-tools.service';
import {I18nToolsService} from '../../core/i18n-tools.service';
import {MathTools} from 'app/tools/math-tools';

export class List extends FirebaseDataModel {
    name: string;
    recipes: ListRow[] = [];
    preCrafts: ListRow[] = [];
    gathers: ListRow[] = [];
    others: ListRow[] = [];
    crystals: ListRow[] = [];
    createdAt: string = new Date().toISOString();
    version: string;

    favorites: string[] = [];

    constructor() {
        super();
    }

    public clone(): List {
        const clone = new List();
        for (const prop of Object.keys(this)) {
            if (['recipes', 'preCrafts', 'gathers', 'others', 'crystals'].indexOf(prop) > -1) {
                clone[prop] = this[prop];
            }
        }
        clone.name = this.name;
        clone.version = this.version;
        delete clone.$key;
        return clone;
    }

    public forEachItem(method: (arg: ListRow) => void): void {
        (this.others || []).forEach(method);
        (this.gathers || []).forEach(method);
        (this.preCrafts || []).forEach(method);
    }

    public addToRecipes(data: ListRow): number {
        return this.add(this.recipes, data);
    }

    public addToPreCrafts(data: ListRow): number {
        return this.add(this.preCrafts, data);
    }

    public addToGathers(data: ListRow): number {
        return this.add(this.gathers, data);
    }

    public addToOthers(data: ListRow): number {
        return this.add(this.others, data);
    }

    public addToCrystals(data: ListRow): number {
        return this.add(this.crystals, data);
    }

    private add(array: ListRow[], data: ListRow): number {
        let previousAmount = 0;
        let row = array.find(r => {
            return r.id === data.id;
        });
        if (row === undefined) {
            array.push(data);
            row = array[array.length - 1];
        } else {
            row.amount = MathTools.round(row.amount + data.amount);
            if (row.amount < 0) {
                row.amount = 0;
                row.amount_needed = row.amount;
            }
            previousAmount = row.amount_needed;
        }
        row.amount_needed = MathTools.absoluteCeil(row.amount / row.yield);
        return row.amount_needed - previousAmount;
    }

    public clean(): List {
        for (const prop of Object.keys(this)) {
            if (['recipes', 'preCrafts', 'gathers', 'others', 'crystals'].indexOf(prop) > -1) {
                this[prop] = this[prop].filter(row => {
                    return row.amount > 0;
                });
            }
        }
        return this;
    }

    public isEmpty(): boolean {
        return this.recipes.length === 0 &&
            this.preCrafts.length === 0 &&
            this.gathers.length === 0 &&
            this.others.length === 0 &&
            this.crystals.length === 0;
    }

    public getItemById(id: number, addedAt?: number): ListRow {
        for (const prop of Object.keys(this)) {
            if (prop !== 'name') {
                for (const row of this[prop]) {
                    if (row.id === id) {
                        if (addedAt !== undefined) {
                            if (addedAt === row.addedAt) {
                                return row;
                            }
                        } else {
                            return row;
                        }
                    }
                }
            }
        }
        return undefined;
    }

    public setDone(pitem: ListRow, amount: number, recipe: boolean = false): void {
        const item = this.getItemById(pitem.id, pitem.addedAt);
        item.done += amount;
        if (item.done > item.amount) {
            item.done = item.amount;
        }
        if (item.done < 0) {
            item.done = 0;
        }
        amount = MathTools.absoluteCeil(amount / pitem.yield);
        if (item.requires !== undefined) {
            for (const requirement of item.requires) {
                const requirementItem = this.getItemById(requirement.id);
                this.setDone(requirementItem, MathTools.absoluteCeil(requirement.amount * amount / requirementItem.yield));
            }
        }
    }

    /**
     * Checks if the list is outdated, the implementation is meant to change.
     * @returns {boolean}
     */
    public isOutDated(): boolean {
        if (this.isEmpty()) {
            return false;
        }
        let res = false;
        this.forEachItem(i => {
            res = res || (i.amount_needed === undefined);
            if (i.gatheredBy !== undefined) {
                res = res || (i.gatheredBy.type === undefined);
            }
        });
        res = res || (this.version === undefined);
        return res;
    }

{'fraction_non_alphanumeric': 0.13601990049751245, 'fraction_numerical': 0.003582089552238806, 'mean_word_length': 1.6526788070731064, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4409984', 'n_tokens_mistral': 2955, 'n_tokens_neox': 2831, 'n_words': 656}