164 lines
4.2 KiB
JavaScript
164 lines
4.2 KiB
JavaScript
(() => {
|
|
'use strict';
|
|
|
|
// rk4 :: (Double -> Double -> Double) ->
|
|
// Double -> Double -> Double -> Double
|
|
const rk4 = f => (y, x, dx) => {
|
|
const
|
|
k1 = dx * f(x, y),
|
|
k2 = dx * f(x + dx / 2.0, y + k1 / 2.0),
|
|
k3 = dx * f(x + dx / 2.0, y + k2 / 2.0),
|
|
k4 = dx * f(x + dx, y + k3);
|
|
return y + (k1 + 2.0 * k2 + 2.0 * k3 + k4) / 6.0;
|
|
};
|
|
|
|
// rk :: Double -> Double -> Double -> Double
|
|
const rk = rk4((x, y) => x * Math.sqrt(y));
|
|
|
|
// actual :: Double -> Double
|
|
const actual = x => (1 / 16) * ((x * x) + 4) * ((x * x) + 4);
|
|
|
|
|
|
// TEST -------------------------------------------------
|
|
|
|
// main :: IO ()
|
|
const main = () => {
|
|
const
|
|
step = 0.1,
|
|
ixs = enumFromTo(0, 100),
|
|
xys = scanl(
|
|
xy => Tuple(
|
|
((xy[0] * 10) + (step * 10)) / 10, rk(xy[1], xy[0], step)
|
|
),
|
|
Tuple(0.0, 1.0),
|
|
ixs
|
|
);
|
|
|
|
// samples :: [(Double, Double, Double)]
|
|
const samples = concatMap(
|
|
tpl => 0 === tpl[0] % 10 ? (() => {
|
|
const [x, y] = Array.from(tpl[1]);
|
|
return [TupleN(x, y, actual(x) - y)];
|
|
})() : [],
|
|
zip(ixs, xys)
|
|
);
|
|
|
|
console.log(
|
|
unlines(map(
|
|
tpl => {
|
|
const [x, y, v] = Array.from(tpl),
|
|
[sn, sm] = splitOn('.', y.toString());
|
|
return unwords([
|
|
'y' + justifyRight(3, ' ', '(' + Math.round(x).toString()) +
|
|
') =',
|
|
justifyRight(3, ' ', sn) + '.' + justifyLeft(15, ' ', sm || '0'),
|
|
'± ' + v.toExponential()
|
|
]);
|
|
},
|
|
samples
|
|
))
|
|
);
|
|
};
|
|
|
|
|
|
// GENERIC FUNCTIONS ----------------------------
|
|
|
|
// Tuple (,) :: a -> b -> (a, b)
|
|
const Tuple = (a, b) => ({
|
|
type: 'Tuple',
|
|
'0': a,
|
|
'1': b,
|
|
length: 2
|
|
});
|
|
|
|
// TupleN :: a -> b ... -> (a, b ... )
|
|
function TupleN() {
|
|
const
|
|
args = Array.from(arguments),
|
|
lng = args.length;
|
|
return lng > 1 ? Object.assign(
|
|
args.reduce((a, x, i) => Object.assign(a, {
|
|
[i]: x
|
|
}), {
|
|
type: 'Tuple' + (2 < lng ? lng.toString() : ''),
|
|
length: lng
|
|
})
|
|
) : args[0];
|
|
};
|
|
|
|
// concatMap :: (a -> [b]) -> [a] -> [b]
|
|
const concatMap = (f, xs) =>
|
|
xs.reduce((a, x) => a.concat(f(x)), []);
|
|
|
|
// enumFromTo :: Int -> Int -> [Int]
|
|
const enumFromTo = (m, n) =>
|
|
Array.from({
|
|
length: 1 + n - m
|
|
}, (_, i) => m + i)
|
|
|
|
// justifyLeft :: Int -> Char -> String -> String
|
|
const justifyLeft = (n, cFiller, s) =>
|
|
n > s.length ? (
|
|
s.padEnd(n, cFiller)
|
|
) : s;
|
|
|
|
// justifyRight :: Int -> Char -> String -> String
|
|
const justifyRight = (n, cFiller, s) =>
|
|
n > s.length ? (
|
|
s.padStart(n, cFiller)
|
|
) : s;
|
|
|
|
// Returns Infinity over objects without finite length
|
|
// this enables zip and zipWith to choose the shorter
|
|
// argument when one is non-finite, like cycle, repeat etc
|
|
|
|
// length :: [a] -> Int
|
|
const length = xs => xs.length || Infinity;
|
|
|
|
// map :: (a -> b) -> [a] -> [b]
|
|
const map = (f, xs) => xs.map(f);
|
|
|
|
// scanl :: (b -> a -> b) -> b -> [a] -> [b]
|
|
const scanl = (f, startValue, xs) =>
|
|
xs.reduce((a, x) => {
|
|
const v = f(a[0], x);
|
|
return Tuple(v, a[1].concat(v));
|
|
}, Tuple(startValue, [startValue]))[1];
|
|
|
|
// splitOn :: String -> String -> [String]
|
|
const splitOn = (pat, src) => src.split(pat);
|
|
|
|
// take :: Int -> [a] -> [a]
|
|
// take :: Int -> String -> String
|
|
const take = (n, xs) =>
|
|
xs.constructor.constructor.name !== 'GeneratorFunction' ? (
|
|
xs.slice(0, n)
|
|
) : [].concat.apply([], Array.from({
|
|
length: n
|
|
}, () => {
|
|
const x = xs.next();
|
|
return x.done ? [] : [x.value];
|
|
}));
|
|
|
|
// unlines :: [String] -> String
|
|
const unlines = xs => xs.join('\n');
|
|
|
|
// unwords :: [String] -> String
|
|
const unwords = xs => xs.join(' ');
|
|
|
|
// Use of `take` and `length` here allows for zipping with non-finite
|
|
// lists - i.e. generators like cycle, repeat, iterate.
|
|
|
|
// zip :: [a] -> [b] -> [(a, b)]
|
|
const zip = (xs, ys) => {
|
|
const lng = Math.min(length(xs), length(ys));
|
|
return Infinity !== lng ? (() => {
|
|
const bs = take(lng, ys);
|
|
return take(lng, xs).map((x, i) => Tuple(x, bs[i]));
|
|
})() : zipGen(xs, ys);
|
|
};
|
|
|
|
// MAIN ---
|
|
return main();
|
|
})();
|