/** * A vector type constrained to a particular size enforced by the type system, and * associated typeclasses. * * @since 1.0.0 */ import * as Fun from 'fp-ts/Functor' import * as FunI from 'fp-ts/FunctorWithIndex' import * as Ap from 'fp-ts/Apply' import * as Apl from 'fp-ts/Applicative' import * as Bnd from 'fp-ts/Bounded' import * as Chn from 'fp-ts/Chain' import * as Fl from 'fp-ts/Foldable' import * as FlI from 'fp-ts/FoldableWithIndex' import * as Fld from 'fp-ts/Field' import * as IO from 'fp-ts/IO' import { HKT } from 'fp-ts/HKT' import * as Mon from 'fp-ts/Monad' import * as Mn from 'fp-ts/Monoid' import * as O from 'fp-ts/Option' import * as Tr from 'fp-ts/Traversable' import * as TrI from 'fp-ts/TraversableWithIndex' import * as Pt from 'fp-ts/Pointed' import * as RA from 'fp-ts/ReadonlyArray' import * as Rng from 'fp-ts/Ring' import { flow, identity, pipe, tuple, unsafeCoerce } from 'fp-ts/function' import * as TC from './typeclasses' import { Complex } from './complex' // ############# // ### Model ### // ############# /** * @since 1.0.0 * @category Model */ export interface Vec extends ReadonlyArray { _length: N } // #################### // ### Constructors ### // #################### /** * @since 1.0.0x * @category Internal */ const wrap: (ks: ReadonlyArray) => Vec = unsafeCoerce /** * @since 1.0.0 * @category Internal */ const unwrap: (ks: Vec) => ReadonlyArray = identity /** * @since 1.0.0 * @category Constructors */ export const fromTuple: { (t: []): Vec<0, A> (t: [A]): Vec<1, A> (t: [A, A]): Vec<2, A> (t: [A, A, A]): Vec<3, A> (t: [A, A, A, A]): Vec<4, A> (t: [A, A, A, A, A]): Vec<5, A> (t: [A, A, A, A, A, A]): Vec<6, A> (t: [A, A, A, A, A, A, A]): Vec<7, A> (t: [A, A, A, A, A, A, A, A]): Vec<8, A> (t: [A, A, A, A, A, A, A, A, A]): Vec<9, A> (t: [A, A, A, A, A, A, A, A, A, A]): Vec<10, A> } = wrap /** * @since 1.0.0 * @category Constructors */ export const fromReadonlyArray: ( n: N ) => (as: ReadonlyArray) => O.Option> = n => flow( O.fromPredicate(xs => xs.length === n), O.map(a => wrap(a)) ) /** * @since 1.0.0 * @category Constructors */ export const makeBy: (n: N, make: (i: number) => A) => Vec = ( n, f ) => pipe( Array.from({ length: n }, (_, i) => f(i)), a => wrap(a) ) /** * @since 1.0.0 * @category Constructors */ export const repeat: (n: N, a: A) => Vec = (n, a) => makeBy(n, () => a) /** * @since 1.0.0 * @category Constructors */ export const randVec: (n: N, make: IO.IO) => IO.IO> = (n, make) => () => makeBy(n, make) // ##################### // ### Non-Pipeables ### // ##################### const _map: Fun.Functor2['map'] = (fa, f) => pipe(fa, map(f)) const _mapWithIndex: FunI.FunctorWithIndex2['mapWithIndex'] = (fa, f) => pipe(fa, mapWithIndex(f)) const _ap: Apl.Applicative2['ap'] = (fab, fa) => pipe(fab, ap(fa)) const _chain: Mon.Monad2C['chain'] = (fa, f) => pipe(fa, chain(f)) const _reduce: Fl.Foldable2['reduce'] = (fa, b, f) => pipe(fa, reduce(b, f)) const _foldMap: Fl.Foldable2['foldMap'] = M => (fa, f) => pipe(fa, foldMap(M)(f)) const _reduceRight: Fl.Foldable2['reduceRight'] = (fa, b, f) => pipe(fa, reduceRight(b, f)) const _reduceWithIndex: FlI.FoldableWithIndex2['reduceWithIndex'] = ( fa, b, f ) => pipe(fa, reduceWithIndex(b, f)) const _foldMapWithIndex: FlI.FoldableWithIndex2['foldMapWithIndex'] = M => (fa, f) => pipe(fa, foldMapWithIndex(M)(f)) const _reduceRightWithIndex: FlI.FoldableWithIndex2< URI, number >['reduceRightWithIndex'] = (fa, b, f) => pipe(fa, reduceRightWithIndex(b, f)) const _traverse: Tr.Traversable2['traverse'] = ( F: Apl.Applicative ): ((ta: Vec, f: (a: A) => HKT) => HKT>) => { const traverseF = traverse(F) return (ta, f) => pipe(ta, traverseF(f)) } const _traverseWithIndex: TrI.TraversableWithIndex2['traverseWithIndex'] = < F >( F: Apl.Applicative ): ((ta: Vec, f: (i: number, a: A) => HKT) => HKT>) => { const traverseWithIndexF = traverseWithIndex(F) return (ta, f) => pipe(ta, traverseWithIndexF(f)) } // ################# // ### Instances ### // ################# /** * @since 1.0.0 * @category Instances */ export const URI = 'Vec' /** * @since 1.0.0 * @category Instances */ export type URI = typeof URI declare module 'fp-ts/HKT' { interface URItoKind2 { readonly [URI]: Vec } } /** * @since 1.0.0 * @category Instances */ export const getAdditiveAbelianGroup: ( R: Rng.Ring ) => (n: N) => TC.AbelianGroup> = R => n => ({ concat: lift2(R.add), inverse: map(x => R.sub(R.zero, x)), empty: repeat(n, R.zero), }) /** * @since 1.0.0 * @category Instances */ export const getBimodule: ( R: Rng.Ring ) => (n: N) => TC.Bimodule, R> = R => n => ({ ...getAdditiveAbelianGroup(R)(n), leftScalarMul: (r, v) => pipe( v, map(x => R.mul(r, x)) ), rightScalarMul: (v, r) => pipe( v, map(x => R.mul(x, r)) ), }) /** * @since 1.0.0 * @category Instance Operations */ export const map: (f: (a: A) => B) => (v: Vec) => Vec = f => v => pipe(v, RA.map(f), a => wrap(a)) /** * @since 1.0.0 * @category Instances */ export const Functor: Fun.Functor2 = { URI, map: _map, } /** * @since 1.0.0 * @category Instance Operations */ export const mapWithIndex: ( f: (i: number, a: A) => B ) => (v: Vec) => Vec = f => v => pipe(v, RA.mapWithIndex(f), a => wrap(a)) /** * @since 1.0.0 * @category Instances */ export const FunctorWithIndex: FunI.FunctorWithIndex2 = { ...Functor, mapWithIndex: _mapWithIndex, } /** * @since 1.0.0 * @category Instance Operations */ export const of: (a: A) => Vec<1, A> = a => fromTuple([a]) /** * @since 1.0.0 * @category Instances */ export const Pointed: Pt.Pointed2C = { _E: 1, URI, of, } /** * @since 1.0.0 * @category Instance Operations */ export const ap: (fa: Vec) => (fab: Vec B>) => Vec = fa => fab => pipe(fab, RA.ap(fa), a => wrap(a)) /** * @since 1.0.0 * @category Instances */ export const Apply: Ap.Apply2 = { ...Functor, ap: _ap, } /** * @since 1.0.0 * @category Combinators */ export const apFirst = Ap.apFirst(Apply) /** * @since 1.0.0 * @category Combinators */ export const apSecond = Ap.apSecond(Apply) /** * @since 1.0.0 * @category Instances */ export const Applicative: Apl.Applicative2C = { ...Apply, ...Pointed, } /** * @since 1.0.0 * @category Instance Operations */ export const chain: (f: (a: A) => Vec<1, B>) => (ma: Vec) => Vec = f => ma => pipe(ma, RA.chain(f), a => wrap(a)) /** * @since 1.0.0 * @category Instances */ export const Chain: Chn.Chain2C = { ...Apply, _E: 1, chain: _chain, } /** * @since 1.0.0 * @category Combinators */ export const chainFirst = Chn.chainFirst(Chain) /** * @since 1.0.0 * @category Instances */ export const Monad: Mon.Monad2C = { ...Applicative, ...Chain, } /** * @since 1.0.0 * @category Instance Operations */ export const reduce: (b: B, f: (b: B, a: A) => B) => (fa: Vec) => B = (b, f) => fa => pipe(fa, RA.reduce(b, f)) /** * @since 1.0.0 * @category Instance Operations */ export const foldMap: ( M: Mn.Monoid ) => (f: (a: A) => M) => (fa: Vec) => M = M => f => fa => pipe(fa, RA.foldMap(M)(f)) /** * @since 1.0.0 * @category Instance Operations */ export const reduceRight: (b: A, f: (b: B, a: A) => A) => (fa: Vec) => A = (a, f) => fa => pipe(fa, RA.reduceRight(a, f)) /** * @since 1.0.0 * @category Instances */ export const Foldable: Fl.Foldable2 = { URI, reduce: _reduce, foldMap: _foldMap, reduceRight: _reduceRight, } /** * @since 1.0.0 * @category Instance Operations */ export const reduceWithIndex: ( b: B, f: (i: number, b: B, a: A) => B ) => (fa: Vec) => B = (b, f) => fa => pipe(fa, RA.reduceWithIndex(b, f)) /** * @since 1.0.0 * @category Instance Operations */ export const foldMapWithIndex: ( M: Mn.Monoid ) => (f: (i: number, a: A) => M) => (fa: Vec) => M = M => f => fa => pipe(fa, RA.foldMapWithIndex(M)(f)) /** * @since 1.0.0 * @category Instance Operations */ export const reduceRightWithIndex: ( b: A, f: (i: number, b: B, a: A) => A ) => (fa: Vec) => A = (a, f) => fa => pipe(fa, RA.reduceRightWithIndex(a, f)) /** * @since 1.0.0 * @category Instances */ export const FoldableWithIndex: FlI.FoldableWithIndex2 = { ...Foldable, reduceWithIndex: _reduceWithIndex, foldMapWithIndex: _foldMapWithIndex, reduceRightWithIndex: _reduceRightWithIndex, } /** * @since 1.0.0 * @category Instance Operations */ export const traverse: Tr.PipeableTraverse2 = (F: Apl.Applicative) => (f: (a: A) => HKT) => (fa: Vec): HKT> => { const traverseWithIndexF = traverseWithIndex(F) return pipe( fa, traverseWithIndexF((_, a) => f(a)) ) } /** * @since 1.0.0 * @category Instance Operations */ export const sequence = (F: Apl.Applicative) => (fa: Vec>): HKT> => pipe(fa, RA.sequence(F), as => F.map(as, a => wrap(a))) /** * @since 1.0.0 * @category Instances */ export const Traversable: Tr.Traversable2 = { URI, map: _map, reduce: _reduce, foldMap: _foldMap, reduceRight: _reduceRight, traverse: _traverse, sequence, } /** * @since 1.0.0 * @category Instance Operations */ export const traverseWithIndex: TrI.PipeableTraverseWithIndex2 = (F: Apl.Applicative) => (f: (i: number, a: A) => HKT) => (ta: Vec): HKT> => pipe(ta, RA.traverseWithIndex(F)(f), fbs => F.map(fbs, bs => wrap(bs))) /** * @since 1.0.0 * @category Instances */ export const TraversableWithIndex: TrI.TraversableWithIndex2 = { URI, map: _map, mapWithIndex: _mapWithIndex, reduce: _reduce, foldMap: _foldMap, reduceRight: _reduceRight, reduceWithIndex: _reduceWithIndex, foldMapWithIndex: _foldMapWithIndex, reduceRightWithIndex: _reduceRightWithIndex, traverse: _traverse, sequence, traverseWithIndex: _traverseWithIndex, } // ################### // ### Destructors ### // ################### /** * @since 1.0.0 * @category Destructors */ export const toTuple: { (t: Vec<0, A>): [] (t: Vec<1, A>): [A] (t: Vec<2, A>): [A, A] (t: Vec<3, A>): [A, A, A] (t: Vec<4, A>): [A, A, A, A] (t: Vec<5, A>): [A, A, A, A, A] (t: Vec<6, A>): [A, A, A, A, A, A] (t: Vec<7, A>): [A, A, A, A, A, A, A] (t: Vec<8, A>): [A, A, A, A, A, A, A, A] (t: Vec<9, A>): [A, A, A, A, A, A, A, A, A] (t: Vec<10, A>): [A, A, A, A, A, A, A, A, A, A] } = unsafeCoerce(unwrap) /** * @since 1.0.0 * @category Destructors */ export const size: (v: Vec) => N = v => v.length as typeof v['_length'] /** * @since 1.0.0 * @category Destructors */ export const lpNorm: ( p: number ) => ( R: Rng.Ring, abs: (x: A) => A, pow: (x: A, n: number) => A ) => (x: Vec) => A = p => (R, abs, pow) => x => { const _ = (rs: ReadonlyArray, i: number): A => unsafeCoerce(rs[i]) let out = R.zero for (let i = 0; i < x.length; i++) { out = R.add(out, pow(abs(_(x, i)), p)) } return pow(out, 1 / p) } /** * @since 1.0.0 * @category Destructors */ export const l1Norm: ( R: Rng.Ring ) => (x: Vec) => A = R => x => { const _ = (rs: ReadonlyArray, i: number): A => unsafeCoerce(rs[i]) let out = R.zero for (let i = 0; i < x.length; i++) { out = R.add(out, R.mul(_(x, i), _(x, i))) } return out } /** * @since 1.0.0 * @category Destructors */ export const l2Norm = lpNorm(2) /** * Get the maximum value of a vector * * @since 1.0.0 * @category Destructors */ export const lInfNorm: ( B: Bnd.Bounded, abs: (a: A) => A ) => (x: Vec) => A = (B, abs) => foldMap(Mn.max(B))(abs) // ######################### // ### Vector Operations ### // ######################### /** * @since 1.1.0 * @category Vector Operations */ export const switchIndices: ( i: number, j: number ) => (v: Vec) => O.Option> = (i, j) => v => pipe( O.Do, O.filter(() => i >= 0 && j >= 0 && i < size(v) && j < size(v)), O.bind('vi', () => pipe(v, get(i))), O.bind('vj', () => pipe(v, get(j))), O.chain(({ vi, vj }) => pipe(v, updateAt(i)(vj), O.chain(updateAt(j)(vi)))) ) /** * @since 1.0.0 * @category Vector Operations */ export const lift2: ( f: (x: A, y: A) => B ) => (x: Vec, y: Vec) => Vec = f => (x, y) => pipe(RA.zipWith(x, y, f), a => wrap(a)) /** * @since 1.0.0 * @category Vector Operations */ export const mapIndex: ( n: number ) => (f: (a: A) => A) => (fa: Vec) => O.Option> = n => f => fa => pipe( fa, RA.lookup(n), O.chain(a => pipe(fa, RA.updateAt(n, f(a)))), O.map(a => wrap(a)) ) /** * @since 1.0.0 * @category Vector Operations */ export const updateAt: ( n: number ) => (a: A) => (fa: Vec) => O.Option> = n => a => flow( RA.updateAt(n, a), O.map(a => wrap(a)) ) /** * @since 1.0.0 * @category Vector Operations */ export const zipVectors: ( v1: Vec, v2: Vec ) => Vec = (v1, v2) => pipe(RA.zip(v1, v2), a => wrap(a)) /** * @since 1.0.0 * @category Vector Operations */ export const reverse: (v1: Vec) => Vec = flow(RA.reverse, a => wrap(a)) /** * @since 1.0.0 * @category Vector Operations */ export const get: (i: number) => (fa: Vec) => O.Option = RA.lookup /** * @since 1.0.0 * @category Vector Operations */ export const crossProduct: ( R: Rng.Ring ) => (x: Vec<3, A>, y: Vec<3, A>) => Vec<3, A> = R => (x, y) => pipe(tuple(toTuple(x), toTuple(y)), ([[a1, a2, a3], [b1, b2, b3]]) => fromTuple([ R.sub(R.mul(a2, b3), R.mul(a3, b2)), R.sub(R.mul(a3, b1), R.mul(a1, b3)), R.sub(R.mul(a1, b2), R.mul(a2, b1)), ]) ) /** * @since 1.0.0 * @category Vector Operations */ export const innerProduct: ( R: Rng.Ring, conj: (r: A) => A ) => (x: Vec, y: Vec) => A = (R, conj) => (x, y) => { const _ = (rs: ReadonlyArray, i: number): A => unsafeCoerce(rs[i]) let out = R.zero for (let i = 0; i < x.length; i++) { out = R.add(out, R.mul(_(x, i), conj(_(y, i)))) } return out } /** * @since 1.0.0 * @category Vector Operations */ export const projection: ( F: Fld.Field, conj: (r: R) => R ) => (u: Vec, v: Vec) => Vec = (F, conj) => (u, v) => { const n = size(u) const BM = getBimodule(F)(n) const uv = innerProduct(F, conj)(u, v) const uu = innerProduct(F, conj)(u, u) return BM.leftScalarMul(F.div(uv, uu), u) } /** * Add an element at the beginning of a vector. Due to the limitations of the typesystem, * the length parameter must be passed explicitly, and will be the new length of the * returned matrix. * * @since 1.1.0 * @category Vector Operations */ export const prepend: ( head: A ) =>

(v: Vec) => Vec = head => flow(RA.prepend(head), a => wrap(a)) /** * Add an element at the end of a vector. Due to the limitations of the typesystem, the * length parameter must be passed explicitly, and will be the new length of the returned matrix. * * @since 1.1.0 * @category Vector Operations */ export const append: ( head: A ) =>

(v: Vec) => Vec = head => flow(RA.append(head), a => wrap(a)) // ################### // ### Do Notation ### // ################### /** * @since 1.0.0 * @category Do notation */ export const bindTo = Fun.bindTo(Functor) /** * @since 1.0.0 * @category Do notation */ export const bind = Chn.bind(Chain) /** * @since 1.0.0 * @category Do notation */ export const apS = Ap.apS(Apply)