Why are these equivalent functions producing a different result? - function

This is the original method, my intent was to abstract away many of the specifics of the code, to improve readability, by placing the specifics in functions which performed the same action, but with a more readable name.
With this first method I achieve the desired behavior and rows[x] is properly added to lineRows.
def getAllRowsForLine( rows, index ) {
def lineRows = [rows[index]]
def newOperatorNotFound
def x = index + 1
if ( x <= rows.size() - 1 ) {
newOperatorNotFound = true
while ( x <= ( rows.size() - 1 ) && newOperatorNotFound ) {
if ( rows[x].PGM_PROC_OPE.trim() == "" ) {
lineRows << rows[x]
} else if ( rows[x].PGM_PROC_TY == "AN" || rows[x].PGM_PROC_TY == "OR" ) {
lineRows << rows[x]
}
else {
newOperatorNotFound = false
}
x++
}
}
return lineRows
}
Here is the refactored code along with the relative methods for context.
This method is not resulting in the desired behavior, breaking the loop after the first rows[x] is added to lineRows.
def getAllRowsForLine2( rows, index ) {
def lineRows = [rows[index]]
def newOperatorNotFound
def i = index + 1
if ( moreRows( rows, i ) ) {
newOperatorNotFound = true
while ( moreRows( rows, i ) && newOperatorNotFound ) {
if ( operatorEmpty( rows, index ) ) {
lineRows << rows[i]
}
else if ( procTypeAnd( rows, i ) || procTypeOr( rows, i ) ) {
lineRows << rows[i]
} else {
newOperatorNotFound = false
}
i++
}
}
return lineRows
}
def operatorEmpty( rows, index ) {
return rows[index].PGM_PROC_OPE.trim() == ""
}
def procTypeAnd( rows, index ) {
return rows[index].PGM_PROC_TY == "AN"
}
def procTypeOr( rows, index ) {
return rows[index].PGM_PROC_TY == "OR"
}
def moreRows( rows, index ) {
return index <= ( rows.size() - 1 )
}
As far as I can tell these things are equivalent. I ran the below code to attempt testing the equivalency of the functions and it returns true.
println lineProcessor.getAllRowsForLine( rows, 0 ) == lineProcessor.getAllRowsForLine2( rows, 0 )
=> true

Oops, I realized that I had used index in the operatorEmpty function instead of i. If I change index to i the function performs as expected.

Related

How to make a MetaTrader4 Terminal to export to CSV in realtime?

I just finished building my algorithm but now I need to export data from the MetaTrader terminal every minute to a CSV file that my algorithm can read and run predictions on.
There are multiple ways online to export MetaTrader data to a CSV file in real-time but I can't find anything that will let me export even just the open price of the new candle as soon as it forms.
I'd like to export the last 10 OHLC candles on a minute timeframe and the open price of the current 11th candle. The open price of the current candle that's STILL forming and hasn't closed yet. I just need the open price for this as soon as the candle starts.
Any ideas? I'm stuck here
UPDATE
I added the code.
This current code is a MetaTrader script, that fetches the past 10 OHLCV candles and the 11th candle as I mentioned.
However, I have three problems with this script:
It does not allow me to overwrite the existing csv.
It does not run realtime and update constantly.
The 11th candle is not the latest (the candle still in formation).
Any help?
//+------------------------------------------------------------------+
#include <stdlib.mqh>
#include <stderror.mqh>
//+------------------------------------------------------------------+
//| Input Parameters Definition |
//+------------------------------------------------------------------+
extern int BarCount = 11;
extern string Pairs = "EURUSD";
extern string delimiter = ",";
//+------------------------------------------------------------------+
//| Local Parameters Definition |
//+------------------------------------------------------------------+
datetime lastExport[];
string pairs[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int init()
{
//------------------------------------------------------------------
Split(Pairs, pairs, ",");
//------------------------------------------------------------------
if (ArraySize(pairs) == 0 || StringTrimLeft(StringTrimRight(pairs[0])) == "")
{
Alert("Pairs are not entered correctly please check it...");
return (0);
}
//------------------------------------------------------------------
ArrayResize(lastExport, ArraySize(pairs));
ArrayInitialize(lastExport, 0);
//------------------------------------------------------------------
Comment("quote exporter is active :)");
//------------------------------------------------------------------
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//------------------------------------------------------------------
Comment("");
//------------------------------------------------------------------
return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int start()
{
//------------------------------------------------------------------
if (ArraySize(pairs) == 0 || StringTrimLeft(StringTrimRight(pairs[0])) == "") return (0);
//------------------------------------------------------------------
BarCount = MathMin(Bars, BarCount);
//------------------------------------------------------------------
for (int j = 0; j < ArraySize(pairs); j++)
{
if (lastExport[j] == Time[0]) continue;
lastExport[j] = Time[0];
if (StringTrimLeft(StringTrimRight(pairs[j])) == "") continue;
if (MarketInfo(pairs[j], MODE_BID) == 0) { Alert("symbol " + pairs[j] + " is not loaded"); continue; }
//------------------------------------------------------------------
string file = pairs[j] + "_" + GetTimeFrameName(0) + ".csv";
int log = FileOpen(file, FILE_CSV|FILE_WRITE, "~");
if (log < 0) { Alert("can not create/overwrite csv file " + file + "!"); continue; }
string buffer;
buffer = "Date"+delimiter+"Time"+delimiter+"Open"+delimiter+"High"+delimiter+"Low"+delimiter+"Close"+delimiter+"Volume";
FileWrite(log, buffer);
int digits = MarketInfo(pairs[j], MODE_DIGITS);
for (int i = BarCount; i >= 1; i--)
{
buffer = TimeToStr(Time[i], TIME_DATE)+delimiter+TimeToStr(Time[i], TIME_MINUTES)+delimiter+DoubleToStr(iOpen(pairs[j], 0, i), digits)+delimiter+DoubleToStr(iHigh(pairs[j], 0, i), digits)+delimiter+DoubleToStr(iLow(pairs[j], 0, i), digits)+delimiter+DoubleToStr(iClose(pairs[j], 0, i), digits)+delimiter+DoubleToStr(iVolume(pairs[j], 0, i), 0);
FileWrite(log, buffer);
}
}
//------------------------------------------------------------------
return(0);
}
//+------------------------------------------------------------------+
string GetTimeFrameName(int TimeFrame)
{
switch (TimeFrame)
{
case PERIOD_M1: return("M1");
case PERIOD_M5: return("M5");
case PERIOD_M15: return("M15");
case PERIOD_M30: return("M30");
case PERIOD_H1: return("H1");
case PERIOD_H4: return("H4");
case PERIOD_D1: return("D1");
case PERIOD_W1: return("W1");
case PERIOD_MN1: return("MN1");
case 0: return(GetTimeFrameName(Period()));
}
}
//+------------------------------------------------------------------+
void Split(string buffer, string &splitted[], string separator)
{
string value = "";
int index = 0;
ArrayResize(splitted, 0);
if (StringSubstr(buffer, StringLen(buffer) - 1) != separator) buffer = buffer + separator;
for (int i = 0; i < StringLen(buffer); i++)
if (StringSubstr(buffer, i, 1) == separator)
{
ArrayResize(splitted, index + 1);
splitted[index] = value;
index ++;
value = "";
}
else
value = value + StringSubstr(buffer, i, 1);
}
//+------------------------------------------------------------------+
A long story to go. A quick-fix of a conceptually wrong idea in an inappropriate algorithmisation listed with remarks below.
Never, indeed NEVER, do anything like this as a CustomIndicator:
Why? After some internal re-designs MetaQuote, Inc., has conformed, that all, yes ALL CustomIndicator code-execution units, that operate inside the MT4-platform, SHARE one SINGLE solo THREAD.
Never attempt to place any kind of slow ( high-latency / low-throughput ) activity like fileIO into CustomIndicator. NEVER. 10~15 ms for fileIO are killing the Real-Time-ness as TLP-durations are well under 20 ms for Majors in prime-time ( ref. picture ).
( For more details, you may check other posts on algorithmic-trading ).
The less any BLOCKING one. Alert() being such example.
The best thing for RealTime?
Better use a Script-type of MQL4 code, not a CustomIndicator.
Use a process-to-process low-latency messaging like nanomsg or zeromq.
I started to use ZeroMQ wrapper for MQL4 many years ago for moving aDataSEGMENT[] into an external AI/ML-predictor engine and to return predictions back, having a net-turn-around-time well under 80 ms, incl. the AI/ML-prediction computing.
This code if less wrong:
//+------------------------------------------------------------------+
#include <stdlib.mqh>
#include <stderror.mqh>
//+------------------------------------------------------------------+
extern int BarCount = 11;
extern string Pairs = "EURUSD";
extern string delimiter = ",";
datetime lastExport[];
string fileNAME[]; // USE INSTEAD OF REPETITIVE RE-ASSIGNMENTS
string pairs[];
//+------------------------------------------------------------------+
int init() {
//--------------------------------------------------------------
Split( Pairs, pairs, "," ); // REF. BELOW FOR A PROPER APPROACH
//--------------------------------------------------------------
if ( 0 == ArraySize( pairs )
|| "" == StringTrimLeft( StringTrimRight( pairs[0] ) )
){ Alert( "WARN: Pairs are not entered correctly please check it..." ); // !BLOCKS!
return( 0 );
}
//--------------------------------------------------------------
ArrayResize( lastExport, ArraySize( pairs ) );
ArrayInitialize( lastExport, 0 );
ArrayResize( fileNAME, ArraySize( pairs ) ); // POPULATE
for ( int j = 0; j < ArraySize( pairs ); j++ ) fileNAME = StringFormat( "%s_M1.CSV", pairs[j] );
//--------------------------------------------------------------
Comment( "INF: Script started. A quote exporter is active :)" );
//--------------------------------------------------------------
return( 0 );
}
//+------------------------------------------------------------------+
int deinit() { // USE OnDeinit(){...} SYNTAX WITH REASON PARAMETER
//--------------------------------------------------------------
Comment( "INF: Script will terminate." );
//--------------------------------------------------------------
return( 0 );
}
//+------------------------------------------------------------------+
int start() {
//--------------------------------------------------------------
if ( 0 == ArraySize( pairs )
|| "" == StringTrimLeft( StringTrimRight( pairs[0] ) )
) return( 0 );
//--------------------------------------------------------------
for ( int j = 0;
j < MathMin( ArraySize( pairs ), CHART_MAX - 1 );
j++ ) //--------------------------------------------------iterateOverAllListedINSTRUMENTs:
ChartOpen( pairs[j], PERIOD_M1 ); // enforce MT4 DataPumps to pre-load & collect QUOTEs
//------------------------------------------------------------------
while ( True ) { //-------------------------------------------------iterateINFINITELY:
ulong loopSTART = GetMicrosecondCount();
for ( int j = 0; j < ArraySize( pairs ); j++ ) { //---------iterateOverAllListedINSTRUMENTs:
if ( "" == StringTrimLeft( StringTrimRight( pairs[j] ) ) ) continue; // .LOOP-NEXT INSTRUMENT
else RefreshRates(); // .REFRESH MT4 DataPumps
if ( 0 == MarketInfo( pairs[j], MODE_BID ) ) { Alert( "symbol " + pairs[j] + " is not loaded" ); continue; } // .LOOP-NEXT INSTRUMENT // !BLOCKS!
if ( lastExport[j] == iTime( pairs[j], PERIOD_CURRENT, 0 ) ) continue; // .LOOP-NEXT INSTRUMENT
else lastExport[j] = iTime( pairs[j], PERIOD_CURRENT, 0 );
int digits = MarketInfo( pairs[j], MODE_DIGITS );
//----------------------------------------------------
int logFH = FileOpen( fileNAME[j], FILE_CSV|FILE_WRITE, delimiter );
if ( logFH == INVALID_HANDLE ) { Alert( StringFormat( "INF: Can not create / overwrite csv file(%s)! Errno(%d)", fileNAME[j], GetLastError() ) ); continue; } // !BLOCKS!
//----------------------------------------------------fileIO RTO:
FileWrite( logFH, "Date", "Time", "Open", "High", "Low", "Close", "Volume" );
for ( int i = MathMin( BarCount, iBars( pairs[j], PERIOD_CURRENT ) ); i >= 1; i-- )
FileWrite( logFH, TimeToStr( iTime( pairs[j], PERIOD_CURRENT, i ), TIME_DATE ),
TimeToStr( iTime( pairs[j], PERIOD_CURRENT, i ), TIME_MINUTES ),
DoubleToStr( iOpen( pairs[j], PERIOD_CURRENT, i ), digits ),
DoubleToStr( iHigh( pairs[j], PERIOD_CURRENT, i ), digits ),
DoubleToStr( iLow( pairs[j], PERIOD_CURRENT, i ), digits ),
DoubleToStr( iClose( pairs[j], PERIOD_CURRENT, i ), digits ),
DoubleToStr( iVolume( pairs[j], PERIOD_CURRENT, i ), 0 )
);
//----------------------------------------------------fileIO DONE
FileClose( logFH );// # a cost ~ 15-18 [ms]
}
//----------------------------------------------------------loopIO DONE
Comment( StringFormat( "INF: Last loopIO-TAT took %d [us]. Will sleep past aNewBarEVENT( M1 )... ~ %s ( + %d [s] )",
GetMicrosecondCount()-loopSTART,
TimeToStr( TimeLocal(), TIME_MINUTES ),
( 60 - MathMod( TimeLocal(), 60 ) )
)
);
Sleep( 250 + 1000 * ( 60 - MathMod( TimeLocal(), 60 ) ) );
//----------------------------------------------------------loopWAIT past aNewBarEVENT
}
//------------------------------------------------------------------
return( 0 );
}
//+------------------------------------------------------------------+
string GetTimeFrameName( int TimeFrame ) {
switch( TimeFrame ) { case PERIOD_M1: return( "M1" );
case PERIOD_M5: return( "M5" );
case PERIOD_M15: return( "M15" );
case PERIOD_M30: return( "M30" );
case PERIOD_H1: return( "H1" );
case PERIOD_H4: return( "H4" );
case PERIOD_D1: return( "D1" );
case PERIOD_W1: return( "W1" );
case PERIOD_MN1: return( "MN1" );
case 0: return( GetTimeFrameName( Period() ) );
}
}
//+------------------------------------------------------------------+
void Split( string buffer, string &splitted[], string separator ) {
string value = "";
int index = 0;
ArrayResize( splitted, 0 );
if ( StringSubstr( buffer, StringLen( buffer ) - 1 ) != separator ) buffer = buffer + separator;
for ( int i = 0;
i < StringLen( buffer );
i++ )
if ( StringSubstr( buffer, i, 1 ) == separator // a case, when (string) buffer[i] is actually a next (string) separator
|| StringLen( buffer ) == i // a case, when (string) buffer does not end with a (string) separator
){ //----------------------------------------------// ONCE A MANUAL FIX ENABLED TO PARSE A SINGLE-INSTRUMENT (string) buffer:
ArrayResize( splitted, index + 1 );
splitted[index] = StringTrimLeft( StringTrimRight( value ) );
index++;
value = "";
}
else
value = value + StringSubstr( buffer, i, 1 );
/* **************************************************************** WORTH READING THE DOCUMENTATION:
USING A BUILT-IN FUNCTION WOULD BE MUCH SIMPLER AND SAFER:
>
> int StringSplit( const string string_value, // A string to search in
> const ushort separator, // A separator using which substrings will be searched
> string &result[] // An array passed by reference to get the found substrings
> );
int SplitINSTRUMENTs( string buffer, string &splitted[], string separator ){
return( StringSplit( buffer,
StringGetCharacter( separator ),
splitted
)
); // -------------------------------------------------WOULD LOVELY FIX THE WHOLE CIRCUS
}
*/
}
//+------------------------------------------------------------------+

slick joins with function inputs

How is it possible to use inputs from the function and joins in slick
example
def query (coffeID : int) = DBAction { implicit rs =>
val implicitInnerJoin = for {
c <- coffees
s <- suppliers if ((c.supID === s.id ) && (c.cofID === coffeeID))
} yield (c.name, s.name)
-- edit typo with ====
you need to use ===
def query (coffeID : int) = DBAction { implicit rs =>
val query = for {
c <- coffees
s <- suppliers if (c.supID === s.id) && (c.cofID === coffeeID)
} yield (c.name, s.name)
query.list
}

Evaluation of nested IF in MySQL

I found this code in a WordPress plugin, but I cannot understand what does it mean or how it can be read.
Can somebody to help me underdstaint this code :?
IF(agr_sam_ads.ad_users = 0, TRUE, IF(agr_sam_ads.ad_users_reg = 1, IF(agr_sam_ads.x_ad_users = 1, NOT FIND_IN_SET("admin", agr_sam_ads.x_view_users), TRUE ....
I have not paste the whole query because it is huge. In what I am interested to be helped is the part of the Query that looks like that:
IF(expression, value, IF(expression, IF(expression, SQL Logical Query, value ...
I have not see this syntax, and I don't know where to search for that. In MySQL documentation the IF statement syntax is like that : http://dev.mysql.com/doc/refman/5.0/en/if.html and it is not looks like the one I have paste above.
Just for the users are interested in the full code, the code is here:
SELECT
agr_sam_places.id,
agr_sam_places.name,
agr_sam_places.description,
agr_sam_places.code_before,
agr_sam_places.code_after,
agr_sam_places.place_size,
agr_sam_places.place_custom_width,
agr_sam_places.place_custom_height,
agr_sam_places.patch_img,
agr_sam_places.patch_link,
agr_sam_places.patch_code,
agr_sam_places.patch_adserver,
agr_sam_places.patch_dfp,
agr_sam_places.patch_source,
agr_sam_places.trash,
(
SELECT
COUNT(*)
FROM
agr_sam_ads
WHERE
agr_sam_ads.pid = agr_sam_places.id
AND
agr_sam_ads.trash IS FALSE
) AS ad_count,
(
SELECT
COUNT(*)
FROM
agr_sam_ads
WHERE
agr_sam_ads.pid = agr_sam_places.id
AND
agr_sam_ads.trash IS FALSE
AND
(
IF(agr_sam_ads.ad_users = 0, TRUE, IF(agr_sam_ads.ad_users_reg = 1, IF(agr_sam_ads.x_ad_users = 1, NOT FIND_IN_SET("admin", agr_sam_ads.x_view_users), TRUE
)
AND
IF(agr_sam_ads.ad_users_adv = 1, (agr_sam_ads.adv_nick <> "admin"), TRUE), FALSE)))
AND
(
(
agr_sam_ads.view_type = 1
)
OR
(
agr_sam_ads.view_type = 0
AND
(
agr_sam_ads.view_pages+0 & 256
)
)
)
AND
(
agr_sam_ads.ad_cats = 0
)
AND
(
agr_sam_ads.ad_authors = 0
)
AND
IF(agr_sam_ads.ad_schedule, CURDATE() BETWEEN agr_sam_ads.ad_start_date AND agr_sam_ads.ad_end_date, TRUE)
AND
IF(agr_sam_ads.limit_hits, agr_sam_ads.hits_limit > agr_sam_ads.ad_hits, TRUE)
AND
IF(agr_sam_ads.limit_clicks, agr_sam_ads.clicks_limit > agr_sam_ads.ad_clicks, TRUE)
AND
(
agr_sam_ads.ad_weight > 0
)
) AS ad_logic_count,
(
SELECT
COUNT(*)
FROM
agr_sam_ads
WHERE
agr_sam_ads.pid = agr_sam_places.id
AND
agr_sam_ads.trash IS FALSE
AND
(
IF(agr_sam_ads.ad_users = 0, TRUE, IF(agr_sam_ads.ad_users_reg = 1, IF(agr_sam_ads.x_ad_users = 1, NOT FIND_IN_SET("admin", agr_sam_ads.x_view_users), TRUE)
AND
IF(agr_sam_ads.ad_users_adv = 1, (agr_sam_ads.adv_nick <> "admin"), TRUE), FALSE)))
AND
(
(
agr_sam_ads.view_type = 1
)
OR
(
agr_sam_ads.view_type = 0
AND
(
agr_sam_ads.view_pages+0 & 256
)
)
)
AND
(
agr_sam_ads.ad_cats = 0
)
AND
(
agr_sam_ads.ad_authors = 0
)
AND
IF(agr_sam_ads.ad_schedule, CURDATE() BETWEEN agr_sam_ads.ad_start_date AND agr_sam_ads.ad_end_date, TRUE)
AND
IF(agr_sam_ads.limit_hits, agr_sam_ads.hits_limit > agr_sam_ads.ad_hits, TRUE)
AND
IF(agr_sam_ads.limit_clicks, agr_sam_ads.clicks_limit > agr_sam_ads.ad_clicks, TRUE)
AND
IF(agr_sam_ads.ad_weight > 0, (agr_sam_ads.ad_weight_hits*10/(agr_sam_ads.ad_weight*1000)) < 1, FALSE)
) AS ad_full_count
FROM
agr_sam_places
WHERE
agr_sam_places.id = 10
AND
agr_sam_places.trash IS FALSE;
It should be read as:
IF (
expression,
valueIfExpressionIsTrue,
valueIfExpressionIsValue
)
So to take part of your code:
IF(
agr_sam_ads.ad_users = 0,
TRUE,
IF(
agr_sam_ads.ad_users_reg = 1,
2,
3
)
)
I see that you're familiar with PHP, so essentially if this was PHP, the code would be
if ($arg_sam_ads_ad_users == 0) {
return true;
} else {
if ($arg_sam_ads_ad_users_reg == 1) {
return 2;
} else {
return 3;
}
}
This is basically creating an IF-ELSE tree without using the IF _ THEN _ ELSE _ ENDIF syntax.
Take the following example:
IF(1=0, 1, IF(1=1, 1, 0))
This is the equivalent of the following with C syntax:
IF (1=0) {
1
}
ELSE IF (1=1) {
1
}
ELSE {
0
}
The more common SQL syntax is the following:
IF 1=0 THEN
1;
ELSEIF 1=1 THEN
1;
ELSE
0;
ENDIF;

Add where clause if not NULL

here is my query:
List<string> kwList = GetFilterKeywords(); // returns NULL none keyword selected
var res = from d in ctx.Books
where (kwList == null || kwList.Contains(d.Name))
select d;
Looks like it is not legit to add where clause if kwList is NULL. So my question is: Is there any way to add more where clauses to the same query in IF/ELSE IF construction?
I mean:
var res = from d in ctx.Books
select d;
if (kwList != null)
{
res.Where(d => kwList.Contains(d.Name);
}
var res = ctx.Books; // no need to write select
if (kwList != null)
res = res.Where(x => kwList.Contains(x.Name));
foreach (d in res) {
...
}
You can use the tertiary operator
var res = kwList == null ? ctx.Books : ctx.Books.Where(x => kwList.Contains(x.Name));
If you want to modify the initial linq query in subsequent case statements, make sure to reassign the initial query to the modified:
var res = ctx.Books;
if (a == b)
{
// reassign here
res = res.Where(x => kwList.Contains(x.Name));
}
else if (a == c)
res = res.Where(x => x.Id == y);

Scala: Tail recursive power function

I always get "1" as result. :(
Whats wrong with this function?
def power(base: Int, exp: Int): BigInt = {
def _power(result: BigInt, exp: Int): BigInt = exp match {
case 0 => 1
case _ => _power(result*base, exp-1)
}
_power(1, exp)
}
you have to replace so: case 0 => result
probably not relevant for OP but I used this answer as an example for something and this version works:
def power(base: Int, exp: Int): BigInt = {
def _power(result: BigInt, exp: Int): BigInt = exp match {
case 0 => 1
case 1 => result
case _ => _power(result*base, exp-1)
}
_power(base, exp)
}
Better, tail-recursive solution (stack-safe) may look like:
def power(base: Int, exp: Int): BigInt {
#scala.annotation.tailrec
def loop(x: BigInt, n: Long, kx: BigInt): BigInt = {
if (n == 1) x * kx
else {
if (n % 2 == 0)
loop(x * x, n / 2, kx)
else
loop(x, n - 1, x * kx) // O(log N) time complexity!
}
}
if (n == 0) 1
else {
val pow = loop(base, Math.abs(exp.toLong), 1)
if (n > 0) pow else 1 / pow
}
}
I'm new to Scala and I had the same task, liked the idea with match. Used your code as an example and this is what I've got
def power(base: Int, exp: Int): BigDecimal = {
if (exp == 0) 1 else {
#tailrec
def _power(result: BigDecimal, exp: Int): BigDecimal = exp match {
case 0 => result
case pos if (pos > 0) => _power(result * base, exp - 1)
case neg if (neg < 0) => _power(result / base, exp + 1)
}
_power(1, exp)
}
}
Don't know what #tailrec means yet but I guess it may be useful, IDEA hinted me with that :)