Created
August 14, 2017 06:44
-
-
Save cherepanov/4e785264fb8d9c13230620b22f56631c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
calc.js | |
DETAILS | |
ACTIVITY | |
Last year | |
You uploaded an item | |
Jan 11, 2016 | |
Javascript | |
calc.js | |
No recorded activity before January 11, 2016 | |
var Calc = (function() { | |
var DEBUG = false; | |
Calc = function(data) { | |
this.data = data; | |
return this; | |
}; | |
Calc.prototype = { | |
constructor: Calc, | |
getDepositsOptions: function() { | |
var res = {}; | |
this.data.every(function(d) { | |
res[d.currency] = res[d.currency] || {}; | |
res[d.currency] = { | |
hasCapitalize: res[d.currency].hasCapitalize || d.capitalize, | |
hasRefill: res[d.currency].hasRefill || d.refill, | |
hasLoyalty: res[d.currency].hasLoyalty || !!d.loyalty | |
}; | |
return true; | |
}); | |
return res; | |
}, | |
_filterByCurrencyCode: function(data, code) { | |
return data.filter(function(d) { | |
return d.currency === code; | |
}); | |
}, | |
_filterByTerms: function(data, term) { | |
return data.filter(function(d) { | |
return ~~d.terms === term; | |
}); | |
}, | |
_filterByAmount: function(data, amount) { | |
return data.filter(function(d) { | |
var min = d.min, | |
max = d.max ? d.max : Number.POSITIVE_INFINITY; | |
return amount >=min && amount <= max; | |
}); | |
}, | |
_filterByOptions: function(data, options) { | |
return data.filter(function(d) { | |
var loyaltyPass = Boolean(!!options.loyalty ? !!d.loyalty : true), | |
refillPass = Boolean(!!options.refill ? d.refill : true), | |
capitalizePass = Boolean(!!options.capitalize ? d.capitalize : true); | |
return loyaltyPass && refillPass && capitalizePass; | |
}); | |
}, | |
_isLeapYear: function(year) { | |
return ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0); | |
}, | |
_getDaysInYear: function(year) { | |
return this._isLeapYear(year) ? 366 : 365; | |
}, | |
_getDaysInMonth: function(year, month) { | |
switch(month) { | |
case 2: return (this._isLeapYear(year) ? 29 : 28); | |
case 4:case 6:case 9:case 11: return 30; | |
default: return 31; | |
} | |
}, | |
/* | |
* http://alcor.concordia.ca/~gpkatch/gdate-algorithm.html | |
* */ | |
_fromGregorian: function(dg) { | |
var y = ~~((10000 * dg + 14780)/3652425), | |
ddd = dg - (365*y + ~~(y/4) - ~~(y/100) + ~~(y/400)); | |
if (ddd < 0) { | |
y = y - 1; | |
ddd = dg - (365*y + ~~(y/4) - ~~(y/100) + ~~(y/400)); | |
} | |
var mi = ~~((100*ddd + 52)/3060), | |
mm = (mi + 2) % 12 + 1; | |
y = y + ~~((mi + 2)/12); | |
var dd = ddd - ~~((mi*306 + 5)/10) + 1; | |
return [y, mm - 1, dd]; | |
}, | |
_gregorianDate: function (date) { | |
var y = date.getFullYear(), | |
m = date.getMonth() + 1, | |
d = date.getDate(); | |
m = (m + 9) % 12; | |
y = y - ~~(m/10); | |
return 365*y + ~~(y/4) - ~~(y/100) + ~~(y/400) + ~~((m*306 + 5)/10) + ( d - 1 ); | |
}, | |
_incDays: function(date, n) { | |
var dateParts = this._fromGregorian(this._gregorianDate(date) + n); | |
return new Date(dateParts[0], dateParts[1], dateParts[2]); | |
}, | |
_incMonths: function(date, n, startDay) { | |
var y = date.getFullYear(), | |
m = date.getMonth() + 1, | |
d = startDay || date.getDate(), | |
nextMonth = (m + n) % 12, | |
yearsToAdd = ~~((m + n) / 12), | |
nextYear = y + yearsToAdd, | |
nextMonthDays = this._getDaysInMonth(y, nextMonth), | |
nextMonthDay = Math.min(d, nextMonthDays), | |
nextDate = new Date(nextYear, nextMonth - 1, nextMonthDay), | |
daysToAdd = this._daysDiff(nextDate, date); | |
return this._incDays(date, daysToAdd); | |
}, | |
_daysDiff: function(dateTwo, dateOne) { | |
return this._gregorianDate(dateTwo) - this._gregorianDate(dateOne); | |
}, | |
_isValidDate: function(y, m, d) { | |
var reverse = this._fromGregorian(this._gregorianDate(new Date(y, m, d))); | |
return (y === reverse[0] && m === reverse[1] && d === reverse[2]); | |
}, | |
_formatDate: function(date) { | |
return date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear(); | |
}, | |
_composePeriods: function(startDate, term) { | |
var periodStart = new Date(startDate), | |
startDay = startDate.getDate(), | |
endDate = this._incDays(startDate, term), | |
daysLeft = this._daysDiff(endDate, periodStart), | |
year = startDate.getFullYear(), | |
month = startDate.getMonth() + 1, | |
daysInMonth = this._getDaysInMonth(year, month), | |
periods = [], | |
addition = false; // summarize percents and capitalization at the end of period | |
DEBUG && console.log('----------------'); | |
/* | |
console.log(this._fromGregorian(this._gregorianDate(new Date(2014, 0, 1)))); | |
console.log(this._fromGregorian(this._gregorianDate(new Date(2014, 11, 31)))); | |
console.log(this._fromGregorian(this._gregorianDate(new Date(2014, 11, 12)))); | |
console.log(this._fromGregorian(this._gregorianDate(new Date(2014, 11, 30)))); | |
console.log(this._incDays(startDate, 1)); | |
console.log(this._incDays(startDate, 10)); | |
console.log(this._incDays(startDate, 20)); | |
console.log(this._incDays(startDate, 30)); | |
console.log(this._incDays(startDate, 31)); | |
console.log(this._incDays(startDate, 61)); | |
console.log(this._incDays(startDate, 91)); | |
console.log('---'); | |
console.log(this._isValidDate(2014, 11, 31)); | |
console.log(this._isValidDate(2014, 10, 30)); | |
console.log(this._isValidDate(2014, 1, 1)); | |
console.log('---'); | |
console.log(this._isValidDate(2014, 10, 31)); | |
console.log(this._isValidDate(2014, -1, 0)); | |
console.log(this._isValidDate(2014, 1, 0)); | |
console.log(this._isValidDate(2014, 0, -1)); | |
console.log('---'); | |
$.each(new Array(20), function(m) { | |
var newDate = this._incMonths(startDate, m), | |
daysDiff = this._daysDiff(newDate, startDate); | |
console.log(newDate, daysDiff); | |
}.bind(this)); | |
*/ | |
while(daysLeft > 0) { | |
var periodEnd = this._incMonths(periodStart, 1, startDay), | |
periodDays = this._daysDiff(periodEnd, periodStart); | |
//FIXME: evaluate once | |
if(periodDays > daysLeft) { | |
periodDays = daysLeft; | |
periodEnd = this._incDays(periodStart, daysLeft); | |
} | |
year = periodStart.getFullYear(); | |
month = periodStart.getMonth() + 1; | |
daysInMonth = this._getDaysInMonth(year, month); | |
if(periodEnd.getFullYear() > year) { | |
var subDays = daysInMonth - startDay, | |
subEnd= this._incDays(periodStart, subDays); | |
periods.push({ | |
start: this._formatDate(periodStart), | |
end: this._formatDate(subEnd), | |
days: subDays, | |
base: this._getDaysInYear(year), | |
addition: true | |
}); | |
daysLeft -= subDays; | |
addition = false; | |
year++; | |
periodStart = new Date(subEnd); | |
periodDays = this._daysDiff(periodEnd, periodStart); | |
} | |
periods.push({ | |
start: this._formatDate(periodStart), | |
end: this._formatDate(periodEnd), | |
days: periodDays, | |
base: this._getDaysInYear(year), | |
addition: addition | |
}); | |
periodStart = new Date(periodEnd); | |
daysLeft -= periodDays; | |
addition = true; | |
} | |
return periods; | |
}, | |
_calc: function(deposit, amount, term, options) { | |
var total = amount, | |
loyalty = 0, | |
percents = 0, | |
rate = deposit.percents / 100, | |
refill = options.refill, | |
capitalize = options.capitalize, | |
investments = 0, | |
currentDate = new Date(), | |
startDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate()), | |
periods = this._composePeriods(startDate, term), | |
periodPercents = 0, | |
periodStart = amount, | |
daysCounted = 0; | |
if(options.loyalty) { | |
loyalty = amount * (deposit.loyalty / 100); | |
total += loyalty; | |
} | |
periods.forEach(function(p) { | |
if(p.addition) { | |
if(capitalize) { | |
periodStart += periodPercents; | |
} | |
if(refill) { | |
periodStart += refill; | |
investments += refill; | |
} | |
} | |
periodPercents = periodStart * p.days * rate / p.base; | |
daysCounted += p.days; | |
DEBUG && console.log('start|' + p.start, ' end|' + p.end, ' days|' + p.days, ' base|', p.base ,' sum|', periodStart, ' refill|', investments, ' |percents', periodPercents); | |
percents += periodPercents; | |
}); | |
DEBUG && console.log(daysCounted); | |
DEBUG && console.log(percents); | |
DEBUG && console.log(investments); | |
DEBUG && console.log(total); | |
total += percents + investments; | |
return { | |
start: amount, | |
total: total, | |
loyalty: loyalty, | |
investments:investments, | |
percents: percents, | |
term: term | |
}; | |
}, | |
getDepositsByParams: function(currency, term, amount, options) { | |
var deposits = this.data; | |
deposits = this._filterByCurrencyCode(deposits, currency); | |
deposits = this._filterByTerms(deposits, term); | |
deposits = this._filterByAmount(deposits, amount); | |
deposits = this._filterByOptions(deposits, options); | |
return deposits; | |
}, | |
calculate: function(currency, term, amount, options) { | |
var deposits = this.getDepositsByParams(currency, term, amount, options) || [], | |
max = 0, | |
maxPriority = Number.NEGATIVE_INFINITY, | |
mostValuable = null; | |
deposits.forEach(function(d) { | |
var res = this._calc(d, amount, term, options); | |
/*DEBUG && */console.log(res.total, d.priority, d); | |
if(res.total + 1 >= max && d.priority > maxPriority) { | |
max = res.total; | |
maxPriority = d.priority; | |
mostValuable = { | |
deposit: d, | |
request: { | |
currency: currency, | |
term: term, | |
amount: amount, | |
options: options | |
}, | |
result: res | |
}; | |
} | |
}.bind(this)); | |
DEBUG && console.log(mostValuable); | |
return mostValuable; | |
} | |
}; | |
return Calc | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment