HighTimeframeTiming
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © SimpleCryptoLife

//@version=5

// @description Functions for sampling high timeframe (HTF) historical data at an arbitrary number of HTF bars back, using a single security() call.
// The data is fixed and does not alter over the course of the HTF bar. It also behaves consistently on historical and elapsed realtime bars.

library("HighTimeframeTiming", overlay = true)

// @function f_offset_synch(float _HTF_X, int _HTF_H, int _openChartBarsIn, bool _updateEarly)
// Returns the number of chart bars that you need to go back in order to get a stable HTF value from a given number of HTF bars ago.
// @param float _HTF_X is the timeframe multiplier, i.e. how much bigger the selected timeframe is than the chart timeframe. The script shows a way to calculate this using another of my libraries without using up a security() call.
// @param int _HTF_H is the historical operator on the HTF, i.e. how many bars back you want to go on the higher timeframe. If omitted, defaults to zero.
// @param int _openChartBarsIn is how many chart bars have been opened within the current HTF bar. An example of calculating this is given below. 
// @param bool _updateEarly defines whether you want to update the value at the closing calculation of the last chart bar in the HTF bar or at the open of the first one.
// @returns an integer that you can use as a historical operator to retrieve the value for the appropriate HTF bar.

// ‼ LIMITATIONS: This function depends on there being a consistent number of chart timeframe bars within the HTF bar. This is almost always true in 24/7 markets like crypto.
//      This might not be true if the chart doesn't produce an update when expected, for example because the asset is thinly traded and there is no volume or price update from the feed at that time.
//      To mitigate this risk, use this function on liquid assets and at chart timeframes high enough to reliably produce updates at least once per bar period.
//      The consistent ratio of bars might also break down in markets with irregular sessions and hours. I'm not sure if or how one could mitigate this.
//      Another limitation is that because we're accessing a multiplied number of chart bars, you will run out of chart bars faster than you would HTF bars. This is only a problem if you use a large historical operator.
//      If you call a function from this library, you should probably reproduce these limitations in your script description.
//      However, all of this doesn't mean that this function might not still be the best available solution, depending what your needs are. 
//      If a single chart bar is skipped, for example, the calculation will be off by 1 until the next HTF bar opens. This is certainly inconsistent, but potentially still usable.


// @function f_HTF_Array(string _HTF=, float _source, int _arrayLength, int _HTF_Offset, bool _useLiveDataOnChartTF=false)
//      Returns a floating-point number from a higher timeframe, with a historical operator within an abitrary (but limited) number of bars.
// @param string _HTF is the string that represents the higher timeframe. It must be in a format that the request.security() function recognises.
// @param float _source is the source value that you want to sample, e.g. close will use the chart timeframe close, or you can use any floating-point number.
// @param int _arrayLength is the number of HTF bars you want to store. You can't go back further in history than this number of bars (minus one, because the current/most recent available bar is also stored).
// @param int _HTF_Offset is the historical operator for the value you want to return. E.g., if you want the most recent fixed close, _source=close and _HTF_Offset = 0.
//      If you want the one before that, _HTF_Offset=1, etc.
// @param bool _useLiveDataOnChartTF uses live data on the chart timeframe.
//      If the higher timeframe is the same as the chart timeframe, store the live value (i.e., from this very bar). For all truly higher timeframes, store the fixed value (i.e., from the previous bar).
//      The default is to use live data for the chart timeframe, so that this function works intuitively, that is, it does not fix data unless it has to (i.e., because the data is from a higher timeframe).
//      This means that on default settings, on the chart timeframe, it matches option B, raw source values from security(), and differs from every other option.
//      You can override this behaviour by passing _useLiveDataOnChartTF as false. Then it will fix all data for all timeframes.
// @returns a floating-point value that you requested from the higher timeframe.

// ‼ LIMITATIONS: This function does NOT depend on there being a consistent number of chart timeframe bars within the HTF bar. It should be, and seems to be, more robust than f_offset_synch() for this reason.
//      You can, however, see some strange discrepancies between f_HTF_Array(), f_offset_synch, method E (the PineCoders' modified function), and raw security() data on thinly traded assets that do not update every chart bar.
//      Therefore, use this function on liquid assets and at chart timeframes high enough to reliably produce updates at least once per bar period.
//      A more conventional and universal limitation is that the function does not offer an unlimited view of historical bars. You need to define upfront how many HTF bars you want to store. Very large numbers might conceivably run into data or performance issues.


// 🙏Credits: This library is an attempt at a solution of the problems in using HTF data that were laid out by Pinecoders in "security() revisited" - https://www.tradingview.com/script/00jFIl5w-security-revisited-PineCoders/
//      Thanks are due to the authors of that work for an understanding of HTF issues. In addition, the current script also includes some of its code.
//      Specifically, this script reuses the main function recommended in "security() revisited", for the purposes of comparison. And it extends that function to access historical data, again just for comparison.
//      All the rest of the code, and in particular all of the code in the exported function, is my own.
//      Special thanks to LucF for pointing out the limitations of my approach.


// ~~~~~~~~~~~~~~~~~
//    EXPLANATION  
// ~~~~~~~~~~~~~~~~~
// Problems with live HTF data: Many problems with using live HTF data from security() have been clearly explained by Pinecoders in "security() revisited"
//      In short, its behaviour is inconsistent between historical and elapsed realtime bars, and it changes in realtime, which can cause calculations and alerts to misbehave.
//      Various unsatisfactory solutions are discussed in "security() revisited", and understanding that script is a prerequisite to understanding this library.
//      PineCoders give their own solution, which is to fix the data by essentially using the previous HTF bar's data. Importantly, that solution is consistent between historical and realtime bars.
//      This library is an attempt to provide an alternative to that solution.

// Problems with historical HTF data: In addition to the problems with live HTF data, there are different issues when trying to access historical HTF data.
//      Why use historical HTF data? Maybe you want to do custom calculations that involve previous HTF bars. Or to use HTF data in a function that has mutable variables and so can't be done in a security() call.
//      Most obviously, using the historical operator (in this description, represented using { } because the square brackets don't render) on variables already retrieved from a security() call, e.g. HTF_Close{1}, is not very useful: 
//      it retrieves the value from the previous *chart* bar, not the previous HTF bar.
//      Using {1} directly in the security() call instead does get data from the previous HTF bar, but it behaves inconsistently, as we shall see.
//      This library addresses these concerns as well.

// Housekeeping: To follow what's going on with the example and comparisons, turn line labels on: Settings > Scales > Indicator Name Label. 
//      The following explanation assumes "close" as the source, but you can change it if you want.
//      To quickly see the difference between historical and realtime bars, set the HTF to something like 3 minutes on a 15s chart.
//      The bars at the top of the screen show the status. Historical bars are grey, elapsed realtime bars are red, and the realtime bar is green. A white vertical line shows the open of a HTF bar.

// A1: This library function f_offset_synch(): When supplied with an input offset of 0, it plots a stable value of the close of the *previous* HTF bar. This value is thus safe to use for calculations and alerts.
//      For a historical operator of {1}, it gives the close of the *last-but-one* bar. Sounds simple enough. Let's look at the other options to see its advantages.

// B: Live HTF data: Represented on the line label as "security(){0}". Note: this is the source that f_offset_synch() samples.
//      The raw HTF data is very different on historical and realtime bars: 
//      + On historical bars, it uses a flat value from the end of the previous HTF bar. It updates at the close of the HTF bar.
//      + On realtime bars, it varies between and within each chart bar.
//      There might be occasions where you want to use live data, in full knowledge of its drawbacks described above. For example, to show simple live conditions that are reversible after a chart bar close.
//      This library transforms live data to get the fixed data, thus giving you access to both live and fixed data with only one security() call.

// C: Historical data using security(){H}: To see how this behaves, set the {H} value in the settings to 1 and show options A, B, and C.
//      + On historical bars, this option matches option A, this library function, exactly. It behaves just like security(){0} but one HTF bar behind, as you would expect.
//      + On realtime bars, this option takes the value of security(){0} at the end of a HTF bar, but it takes it from the previous *chart* bar, and then persists that.
//      The easiest way to see this inconsistency is on the first realtime bar (marked red at the top of the screen). This option suddenly jumps, even if it's in the middle of a HTF bar.
//      Contrast this with option A, which is always constant, until it updates, once per HTF bar.

// D: PineCoders' original function: To see how this behaves, show options A, B, and D. Set the {H} value in the settings to 0, then 1.
//      The PineCoders' original function (D) and extended function (E) do not have the same limitations as this library, described in the Limitations section.
//      This option has all of the same advantages that this library function, option A, does, with the following differences:
//      + It cannot access historical data. The {H} setting makes no difference.
//      + It always updates at the open of the first chart bar in a new HTF bar. 
//      By contrast, this library function, option A, is configured by default to update at the close of the last chart bar in a HTF bar.
//      This little frontrunning is only a few seconds but could be significant in trading. E.g. on a 1D HTF with a 4H chart, an alert that involves a HTF change set to trigger ON CLOSE would trigger 4 hours later using this method - 
//      but use exactly the same value. It depends on the market and timeframe as to whether you could actually trade this. E.g. at the very end of a tradfi day your order won't get executed.
//      This behaviour mimics how security() itself updates, as is easy to see on the chart. If you don't want it, just set in_updateEarly to false. Then it matches option D exactly.

// E: PineCoders' function, extended to get history: To see how this behaves, show options A and E. Set the {H} value in the settings to 0, then 1.
//      I modified the original function to be able to get historical values. In all other respects it is the same.
//      Apart from not having the option to update earlier, the only disadvantage of this method vs this library function is that it requires one security() call for each historical operator.
//      For example, if you wanted live data, and fixed data, and fixed data one bar back, you would need 3 security() calls. My library function requires just one.
//      This is the essential tradeoff: extra complexity and less robustness in certain circumstances (the PineCoders function is simple and universal by comparison) for more flexibility with fewer security() calls.

// A2: This library's alternative function using an array to store values as we go along rather than calculating an offset like A1 does.
//      This function appears to be more stable than A1 in practice, although in theory they should be the same.
//      A2 should be the same as E, the PineCoders' function, which we can use as a standard. However, for tradfi markets with sessions, E is one chart bar behind when the new HTF bar/trading session opens,
//      at least with historical data. If you have the patience to leave the indicator running for several days you can test elapsed realtime data for such sessions.
//      Therefore, this function seems to me to be, overall, tied for accuracy with the PineCoders' function out of all the options presented here (with the caveat, additional to the limitations described above, that I haven't tested it with extended sessions).
//      Like A1, it is inexpensive, using only a single security() call for the raw data and returning values from an abritrary number of bars back.
//      It is a little simpler than A1. And it has potential to be extended, for example, to retrieve the highest value in a range of HTF bars back.
//      A single version of this function is included in this library, in order that a comparison with other methods of fixing HTF data can be made. Multiple variations of this function are published in a separate library https://www.tradingview.com/script/tRv6GGg7-HighTimeframeSampling/, which, because it does not contain any other methods, is easier to refer to and use. In other words, this library, although functional, should be considered more like an extended example or proof of concept to be understood first, while the HighTimeframeSampling library contains the functions that you would probably want to call if you decided to use this method of sampling HTF data in your script.


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
//                                            INPUTS                                               |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

// Inputs needed for the library
in_HTF = input.timeframe(defval="", title="Higher Timeframe", group="General")
in_source = input.source(defval=close, title="Source")
in_HTF_Offset = input.int(defval=0, minval=0, maxval=4, title="No. of HTF bars to look back [H]", tooltip="This lookback value is used for all options that have [H] in the description.")
bool in_updateEarly = input.bool(defval=false, title="Update on the close of the last chart bar (the earliest possible time to update)", group="Option A1")

in_arrayLength = input.int(minval=1, maxval=100, defval=10, title="Number of HTF source values to store", group="Option A2")
in_useLiveDataOnChartTF = input.bool(defval=false, title="Use live data on chart timeframe (HTF data is always fixed)")  // 
in_showHTF_Synch = input.bool(defval=true, title="A1: Show this library function f_offset_synch()[H]]", group="Display", tooltip="Looking back X bars, but synchronised to the beginning of the HTF bar. This is my preferred method.")
in_showHTF_Array = input.bool(defval=true, title="A2: Show this library function f_HTF_Array()[H]", tooltip="Looking back X bars, using an array.")

// Inputs for examples and comparisons
in_showHTF_Raw = input.bool(defval=true, title="B: Show HTF Raw [0] source values using security()")
in_showHTF_Sec = input.bool(defval=false, title="C: Show security(source[H])", tooltip="The simplest way of accessing HTF historical values - but not the best.")
in_showHTF_PineCoders = input.bool(defval=false, title="D: Show Pinecoders original function")
in_showHTF_PineCodersHist = input.bool(defval=false, title="E: Show Pinecoders extended function [H]")


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
//                                            RAW HTF DATA                                         |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

// This raw HTF value is requested directly from the security() call on the current bar. It used as the source for the library functions f_offset_synch() and f_HTF_Array, and can also be plotted.
float HTF_Raw = request.security(syminfo.tickerid, in_HTF, in_source, barmerge.gaps_off, barmerge.lookahead_off)


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
//                                  CALCULATIONS FOR OFFSET FUNCTION                               |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

// ===  Get the timeframe multiplier  ===
var int chartTFInSeconds = timeframe.in_seconds(timeframe.period) 
var int HTF_FixedInSeconds = timeframe.in_seconds(in_HTF)  // Need separate for error-checking

var HTF_X = int(HTF_FixedInSeconds / chartTFInSeconds)  // How much bigger HTF is than the chart timeframe
// Error-checking
var string errorTextLibraryName = "SimpleCryptoLife/HighTimeframeTiming"
if HTF_FixedInSeconds < chartTFInSeconds
    runtime.error("Input timeframe cannot be lower than the chart timeframe [Error 001:" + " from section \"Get the timeframe multiplier\" in library " + errorTextLibraryName + "]")
if HTF_FixedInSeconds % chartTFInSeconds > 0
    runtime.error("Input timeframe must be exact multiple of chart timeframe [Error 002:" + " from section \"Get the timeframe multiplier\" in library " + errorTextLibraryName + "]")


export f_offset_synch(float _HTF_X, int _HTF_H = 0, int _openChartBarsIn, bool _updateEarly) =>
    // Throw an error if HTF_H is out of bounds
    var string _errorTextFunctionName = "f_offset_synch"
    if _HTF_H < 0
        runtime.error("Historical operator " + str.tostring(_HTF_H) + " is less than zero" + " [Error 003:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _HTF_H % 1 > 0
        runtime.error("Historical operator " + str.tostring(_HTF_H) + " is not a whole number" + " [Error 004:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if barstate.isrealtime and ((_HTF_H * _HTF_X) > bar_index)
        runtime.error("Historical operator " + str.tostring(_HTF_H) + " is greater than number of HTF bars: " + str.tostring(bar_index) + " [Error 005:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    float _chart_H_Normal = _openChartBarsIn + (_HTF_X * _HTF_H)  // The offset for most bars most of the time
    bool _isLastChartUpdate = barstate.isconfirmed  // This is true only for the closing update of a chart bar. It is true once for each chart bar.
    bool _isLastHTFBar = _openChartBarsIn == _HTF_X  // Is this the last chart bar in the current HTF bar?
    float _chart_H_Last = _isLastChartUpdate and _isLastHTFBar and _updateEarly ? (_HTF_X * _HTF_H) : na  // The offset for the last update of the HTF bar
    float _chart_H_float = na(_chart_H_Last) ? _chart_H_Normal : _chart_H_Last  // Choose which one to use
    int _chart_H = int(_chart_H_float)  // We had to use floats for ints before because reasons. But it's really an int. OCD satisfied.


// These bits are just required for the function and can be reused without much thought.
var int openChartBars = na, openChartBars := ta.change(time(in_HTF)) ? 1 : openChartBars + 1  //  Count the number of opened chart bars in this HTF bar.

// Here's where we actually call the function
chart_H = f_offset_synch(HTF_X, in_HTF_Offset, openChartBars, in_updateEarly)  //  Get the offset

// Using the offset to get a certain value
float HTF_HistSynch = HTF_Raw[chart_H]  // Keeping this outside the function for maximum flexibility


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
//                                 CALCULATIONS FOR ARRAY FUNCTION                                 |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

export f_HTF_Array(string _HTF="", float _source, int _arrayLength, int _HTF_Offset=0, bool _useLiveDataOnChartTF=false) =>

    var string _errorTextFunctionName = "f_HTF_Array"  // Error-checking.
    if _HTF_Offset + 1 > _arrayLength
        runtime.error("The number of stored values configured is " + str.tostring(_arrayLength) + ", and must be at least " + str.tostring(_HTF_Offset+1) + " in order to look " + str.tostring(_HTF_Offset) + " HTF bars back." + " [Error 006:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")
    if _arrayLength < 1
        runtime.error("The number of stored values configured must be more than zero." + " [Error 007:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")        
    if _HTF_Offset < 0
        runtime.error("The number of HTF bars to look back must be zero or more." + " [Error 008:" + " from function " + _errorTextFunctionName + " in library " + errorTextLibraryName + "]")   
    
    var _arraySource = array.new_float(_arrayLength,na)  // Create the array to hold the floats.
    bool isChartTimeframe = _HTF=="" or _HTF == timeframe.period
    var int _offset = isChartTimeframe and _useLiveDataOnChartTF==true ? 0 : 1  // Use live data on chart TF if that option is selected; otherwise use fixed (the previous bar's) data. 
    // By using ta.change(), we store the source value only when the HTF bar *opens*. We do updates throughout every bar if the timeframe =""; otherwise all throughout the chart bar that corresponds to the first HTF bar.
    // It might be more efficient to update only at the beginning of the chart bar, but a) it didn't seem to work, and b) even if I could get it working, it might be less robust.
    if _HTF=="" or ta.change(time(_HTF))
        array.unshift(_arraySource,_source[_offset])  // Add the source value to index 0 and shift other indexes along. It's important to keep the order so that it's simple to look up values.
        if array.size(_arraySource) > _arrayLength
            array.pop(_arraySource)  // Remove the oldest value, keeping the array the same length.

    float _myFloat = array.get(_arraySource,_HTF_Offset)  // This function just returns the one value requested. There's more you could do with the array.
    _myFloat

HTF_valueFromArray = f_HTF_Array(in_HTF, HTF_Raw, in_arrayLength, in_HTF_Offset, in_useLiveDataOnChartTF)  // Call the function and use the offset to get a certain value


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
//                                  CALCULATIONS FOR COMPARISONS                                   |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

// This value is also requested directly from a security() call, but with a variable historical operator. It is not used as input to anything else, just for comparison.
float HTF_HistSec = request.security(syminfo.tickerid, in_HTF, in_source[in_HTF_Offset], barmerge.gaps_off, barmerge.lookahead_off)  

// This is the preferred (non-tuple) function presented by PineCoders in "security() revisited" - https://www.tradingview.com/script/00jFIl5w-security-revisited-PineCoders/
// It presents a fixed (as in static) version of live data which is one bar behind.
f_security(_sym, _res, _src, _rep) => request.security(_sym, _res, _src[not _rep and barstate.isrealtime ? 1 : 0])[_rep or barstate.isrealtime ? 0 : 1]
HTF_PineCoders = f_security(syminfo.tickerid, in_HTF, in_source, false)  // Hardcoding repainting to false so as not to confuse matters.

// This is the same PineCoders' function but with a historical operator added.
f_securityHistorical(_sym, _res, _src, _rep) => request.security(_sym, _res, _src[not _rep and barstate.isrealtime ? 1 + in_HTF_Offset: 0 + in_HTF_Offset])[_rep or barstate.isrealtime ? 0 : 1]
HTF_PineCodersHist = f_securityHistorical(syminfo.tickerid, in_HTF, in_source, false)  // Hardcoding repainting to false so as not to confuse matters.


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
//                                            PLOTS                                                |
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|

plot(in_showHTF_Raw? HTF_Raw : na, color=color.new(color.orange,20), linewidth=14, title=" B: security() [0]")
plot(in_showHTF_Sec ? HTF_HistSec : na, color=color.blue, linewidth=6, title=" C: security() [H]")
plot(in_showHTF_Synch ? HTF_HistSynch : na, style=plot.style_circles, color=color.new(color.green,50), linewidth=18, title=" A: 😎 f_offset_synch()[H]")
plot(in_showHTF_PineCoders ? HTF_PineCoders : na, title=" D: PineCoders Original", color=color.yellow, linewidth=2)
plot(in_showHTF_PineCodersHist ? HTF_PineCodersHist : na, style=plot.style_cross, title=" E: PineCoders Historical", color=color.purple, linewidth=5)
plot(in_showHTF_Array ? HTF_valueFromArray : na, style=plot.style_circles, title=" A2: 😍 f_HTF_Array", color=color.new(color.orange,30), linewidth=12)

// ————— Colored bar at the top of the chart lifted from "security() revisited" by PineCoders
plotchar(na, "══════════ BAR STATES",  "", location.top)
plotchar(barstate.ishistory,  "ishistory",  "█", location.top, color=color.new(color.silver,50), size = size.normal)
plotchar(barstate.isrealtime, "isrealtime", "█", location.top, color=color.new(color.green,50),  size = size.normal)
plotchar(barstate.isrealtime and barstate.isconfirmed, "isrealtime and isconfirmed", "█", location.top, color=color.new(color.red,50), size = size.normal)

plotchar(ta.change(time(in_HTF)), "ta.change(time(in_HTF))", "|", location.top, color=color.white, size = size.normal)  // Show when a new HTF bar opens

// ======================================================= //
//                                                         //
//   (╯°□°）╯︵ ǝsn pǝpuǝʇuᴉ ǝɥʇ ʇou sᴉ ʇɐɥʇ ɹᴉS           //
//                                                         //
// ======================================================= //