import EtudeError from "../error/EtudeError"; import Exceptional from "../util/Exceptional"; import MathUtil from "../util/MathUtil"; export default class Degree { public static size = 0; private static _values: Degree[] = []; public static TONIC = new Degree(); public static SUPERTONIC = new Degree(); public static MEDIANT = new Degree(); public static SUBDOMINANT = new Degree(); public static DOMINANT = new Degree(); public static SUBMEDIANT = new Degree(); public static LEADING_TONE = new Degree(); private constructor() { ++Degree.size; Degree._values.push(this); } public static values(startingDegree: Degree = Degree.TONIC): Degree[] { let degrees = Degree._values.slice(); MathUtil.rotate(degrees, startingDegree.getValue() - 1); return degrees; } public ordinal(): number { return Degree._values.indexOf(this); } public static valueOf(degreeString: string): Degree { let degree = Degree[degreeString]; if (degree instanceof Degree) { return degree; } throw EtudeError.forInvalid(Degree, degreeString); } public static isValid(value: number): boolean { return 1 <= value && value <= Degree.size; } public static fromValue(value: number): Exceptional { return Exceptional .of(value) .filter(Degree.isValid, EtudeError.forInvalid(Degree, value, "out of range")) .map(i => Degree.values()[i - 1]); } public getValue(): number { return this.ordinal() + 1; } public toString(): string { return Object.keys(Degree).find(d => Degree[d] === this); } }