Programming

jQuery 1.11.1 RC1 and 2.1.1 RC1 Released

jQuery Blog - Fri, 04/18/2014 - 15:30

Spring has sprung, and these release candidates for jQuery 1.11.1 and 2.1.1 are in full bloom. You know what that means? It’s time to get serious about testing! We really want our next release to be solid and reliable, and to do that we need your help. Please try out these files in your projects and pages, just a quick test would be appreciated. If you unearth any problems, let us know at bugs.jquery.com.

The beta files are located on the jQuery CDN, you can include them directly from the CDN if you like (but don’t use them in production!). As always, the 1.x branch includes support for IE 6/7/8 and the 2.x branch does not:

http://code.jquery.com/jquery-1.11.1-rc1.js
http://code.jquery.com/jquery-2.1.1-rc1.js

Here are the bugs fixed since the last official release (1.11.0 and 2.1.0):

Common to jQuery 1.11.1 RC1 and 2.1.1 RC1 Ajax Attributes Build Core Css Dimensions Event Misc jQuery 1.11.1 RC1 Css jQuery 2.1.1 RC1 Ajax Attributes Core Css Event Manipulation Selector
Categories: Programming

The Usual Suspects…now with XSS!

phpdev.blog - Fri, 04/18/2014 - 12:23

I’ve just pushed the latest update of the most recent book in the Securing PHP ebook series – The Usual Suspects – and included an entire chapter covering cross-site scripting:

Next we come to something that’s probably a bit more widely known but often misunderstood, especially when it comes to the power that it offers to the attacker. Part of the confusion comes from the name of the attack. When you think about the attack method an XSS vulnerabilities allows, the only thing “cross site” about it is that it can possibly come in as a link from another site. Other than that, a cross-site scripting vulnerability can be more closely associated with injection. In fact, the main reason a site might have a cross-site scripting issue is because of improper output escaping.

This new chapter has loads of information about the different types of XSS issues, the different contexts it can happen in and plenty of code and configuration examples of how to prevent them. If you haven’t picked up a copy of it yet, there’s no time like the present!

Don’t forget about the first book in the Securing PHP series too! Core Concepts is a great introduction to security terminology, methods and principles that can help you lay a good foundation for more secure applications.

Categories: Programming

Raymond's house rules for Easter Egg Hunts

The Old New Thing - Fri, 04/18/2014 - 10:00

One of my colleagues frustrates his family by hiding the eggs for the annual Egg Hunt way too well. "Apparently, drawers and freezers are out of bounds in the traditional egg hunt."

Here are my house rules for Easter Egg Hunts:

  • All eggs must be hidden within the implied egg-hiding area. No sneaky outliers.
  • All eggs must be at least partially observable by egg-hunters without disturbing anything. No hiding in drawers or under flowerpots, or putting them on top of a tall piece of furniture that a shorter egg-hunter cannot see.
  • However, you may still have to work to see them. They might be behind a sofa or placed above eye level. For example, you might find an egg tucked between the slats of horizontal blinds.

Personally, I like to hide eggs in plain sight. It's surprising how long it can take somebody to find a yellow egg resting brazenly on the lap of a yellow teddy bear.

Categories: Programming

How do I set a breakpoint on a function whose name contains spaces or other special characters?

The Old New Thing - Fri, 04/18/2014 - 10:00

If you use one of the command line debuggers based on the Debugger Engine, you can set a breakpoint on a function whose name contains spaces or other special characters by quoting the symbol name. The trick here is that you do not quote the entire string; you quote only the symbol name.

0:001> bu @!"CSimpleArray<wchar_t *>::CSimpleArray<wchar_t *>"

Note that the quotation marks do not go around the @! part. They go only around the symbol. (Otherwise, the debugger thinks you are setting a breakpoint action.)

Another trick for setting breakpoints is using tab autocompletion for symbols. If you type bp contoso!*Widget* and then hit Tab repeatedly, you will cycle through all the matches. (It takes a few seconds to build the list of matches, so be patient the first time you hit Tab.)

Personally, I use the x command to print out all the matches, and then cherry-pick the one I want.

0:001> x contoso!*Widget* 00af114c contoso!CreateWidget 009fe863 contoso!DestroyWidget 00a2e161 contoso!MakeWidgetReadOnly 00a93168 ... 0:001> bp 00a2e161 set breakpoint on MakeWidgetReadOnly
Categories: Programming

How can I get the Windows 8 touch keyboard to display autocomplete suggestions like the Bing app?

The Old New Thing - Thu, 04/17/2014 - 10:00

A customer observed that if you use the Windows 8 Bing app with the touch keyboard, the top of the touch keyboard includes autocomplete suggestions for quick access. They wanted to know how to enable this in their own application. In the illustration below, it's the two boxes immediately above the keyboard with the words "aol" and "amazon". The ones that slide into view.

| a| SUGGESTIONS aol amazon att.net autotrader ask.com american airlines aol   amazon q w e r t y u i o p ⌫ a s d f g h j k l ' Search ↑ z x c v b n m , . ? ↑ &123 Ctrl ☻   < > ⌨ if (document.querySelectorAll) { (function() { document.getElementById("id20140417CanSlide").style.display = ""; var bing = document.getElementById("id20140417Bing"); fade = bing.querySelectorAll(".fadeIn"); pop = bing.querySelectorAll(".popIn"); slide = bing.querySelectorAll(".slideUp"); function progress(p) { var i; for (i = 0; i < fade.length; i++) { fade[i].style.opacity = p; } for (i = 0; i < pop.length; i++) { pop[i].style.opacity = p ? 1 : 0; } for (i = 0; i < slide.length; i++) { var height = parseInt(slide[i].style.height); slide[i].style.top = ((1 - p) * height) + "px"; } } progress(0); var requestAnimationFrame = window.requestAnimationFrame || function(c) { c(); } var t = 0; function schedule(delay) { setTimeout(function() { requestAnimationFrame(nextFrame); }, delay); } function nextFrame() { var delay = 25; switch (++t) { case 1: progress(0); delay = 1000; break; case 11: progress(1); delay = 3000; t = 0; break; default: progress((t - 2) / 10); break; } schedule(delay); } schedule(0); })(); }

The answer is that it's all done with mirrors.

The thing with the completion suggestions is not actually a part of the keyboard. The Bing app is simply drawing a black box with buttons at the very bottom edge of their window, so that it exactly abuts the touch keyboard. Touch events on those buttons go straight to the Bing app, and Bing programmatically inserts the appropriate word into the text box.

In other words: It's a fake!

Categories: Programming

There is no complete list of all notifications balloon tips in Windows

The Old New Thing - Wed, 04/16/2014 - 10:00

A customer wanted a complete list of all notifications balloon tips in Windows.

There is no such list. Each component is responsible for its own balloon tips and is not required to submit their list of balloon tips to any central authority for cataloging. In order to create such a list, somebody would have to go contact every component team and ask them for a list of all their balloon tips, and that component team would probably undertake a search of their code base looking for balloon tips. And figuring out the text of each balloon tip can be tricky since the text may be built dynamically. (And the customer didn't ask for an explanation of the conditions under which each balloon tip may appear, but that's probably going to be their follow-up question.)

It's like publishing instructions on how to display a message on the message board, and then somebody asking the message board manufacturer, "Please send me a list of all messages that might appear on the message board." The message board manufacturer doesn't know how its customers are using the message board. They would have to go survey their customers and ask each one to build an inventory of every message that could potentially be shown.

In other words, this customer was asking for a research project to be spun up to answer their question.

I suspected that this was a case of the for-if antipattern being applied to custom support questions, and I asked what the customer intended to do with this information.

It turns out that they didn't even want this list of balloon tips. They just had a couple of balloon tips they were interested in, and they wanted to know if there were settings to disable them.

But even though that was their stated goal, they still reiterated their request for a list of balloon tips. The customer liaison asked, "Is there a possibility is getting a list of balloon tips generated by Windows itself? Even a partial list would be helpful. Can we help with this?"

What the customer liaison can do is contact each Windows component team and ask them, "Can you give me a list of the balloon tips your component generates? Even a partial list would be helpful." I wished him luck.

The customer liaison replied, "I passed this information to the customer and will follow up if they have any further questions." No follow-up ever appeared.

Categories: Programming

Social Processes and Heartbleed, Part 1

Dr. Dobb's - Wed, 04/16/2014 - 04:12
Why is gets still with us?
Categories: Programming

jQuery Chicago Pebble Giveaway and Filing Extension

jQuery Blog - Tue, 04/15/2014 - 16:24
jQuery Conference Chicago logo

As we announced at the end of jQuery San Diego in February, we’re excited that the next stop for #jqcon is Chicago! In case you missed the news, we’ll be setting up shop in the City That Never Sleeps Isn’t Windy on September 12th & 13th, 2014. We’re partnering again with Bocoup to make it a four-day affair, bringing you Roost on September 11th & 12th.

Speaker Filing Extension

While we can’t do anything about today’s deadline for those of us in the US to file our tax returns, we can offer our own form of amnesty: a two-week-plus extension of our Call for Speakers until the end of April! If you got swamped in receipts – or anything else – and thought you’d missed your chance to submit, breathe a sigh of relief, and if you didn’t know the call was even open, this should hopefully provide you enough time to come up with a solid talk proposal. (And if you already have submitted, thanks!)

We’re experimenting a bit with our time slot construction in Chicago, so if you have a talk that you feel needs to go deep into technical material and run for 45 minutes or an hour, or want to lead a more hands-on-workshop type of session for even longer, we’re eager to hear about it and encourage you to to get in touch with questions about your ideas at content@jquery.org or on the #jquery-content channel on Freenode.

Join Us

Our early bird tickets have been going fast and will likely be gone before our original early-bird cutoff of May 31st, so if you’re aiming to keep another 50 bucks in your deep-dish pizza budget, you’ll want to act sooner than later!

The conference will be right downtown at the Sheraton Chicago Hotel & Towers, and we’re able to offer attendees of both jQuery Chicago and Roost a discounted rate if you register as part of our room block.

Join Us…Together!

We’ve always held that jQuery is better with friends, and if you’ve got colleagues you want to attend with or send to the conference, we have group packages available that include sponsorships and discounts. Get in touch with us for a prospectus and to figure out how to get your team to Chicago!

A “Rocky” Start

Pebble logo If the prospect of jQuery’s first foray into the Old Northwest wasn’t exciting enough, we’re psyched to inform you that we’ll be giving away classic Pebble devices throughout ticket sales. We’ll take a random draw of people who’ve bought tickets each month and select 2-3 folks who’ll receive a Pebble from us (and the kind folks at Pebble who’ve donated the devices) at the conference in September. The sooner you buy, the better your odds, so what are you waiting for? This post is over anyway!

Categories: Programming

Rvalue references in constructor: when less is more

C++ Truths - Tue, 04/15/2014 - 15:43
I've seen a recurring mistake made by well-versed C++03 programmers when they set out to use rvalue references for the first time. In fact, as it turns out, better you are at C++03, easier it is to fall in the trap of rvalue reference anti-pattern I'm gonna talk about.

Consider the following C++03 class:


class Book {
public:
Book(const std::string & title,
const std::vector<std::string> & authors,
const std::string & pub,
size_t pub_day
const std::string & pub_month,
size_t pub_year)
: _title(title),
_authors(authors),
_publisher(pub),
_pub_day(pub_day),
_pub_month(pub_month),
_pub_year(pub_year)
{}

// ....
// ....

private:
std::string _title;
std::vector<std::string> _authors;
std::string _publisher;
size_t _pub_day;
std::string _pub_month;
size_t _pub_year;
};

The Book class above is as dull as it can be. Now lets C++11'fy it! C++11 gives us shiny new rvalue references and std::move. So lets add them.


class Book {
public:
Book(const std::string & title,
const std::vector<std::string> & authors,
size_t pub_day
const std::string & pub_month,
size_t pub_year)
: _title (title),
_author (author),
_pub_day (pub_day),
_pub_month(pub_month),
_pub_year (pub_year)
{}

Book(std::string && title,
std::vector<std::string> && authors,
size_t && pub_day
std::string && pub_month,
size_t && pub_year)
: _title (std::move(title)),
_authors (std::move(authors)),
_pub_day (pub_day),
_pub_month(std::move(pub_month)),
_pub_year (pub_year)
{}

// ....
// ....

private:
std::string _title;
std::vector<std::string> _authors;
size_t _pub_day;
std::string _pub_month;
size_t _pub_year;
};

Is our constructor optimally move-enabled? It's far from it! More often that not, programmers' legit code will end up calling the old constructor instead of the the new one, which probably means lost opportunities for optimization. It could be hard to see what's wrong in the new class to an untrained eye. We've test it to see what's really wrong with it.


std::string & toUpper(std::string & s)
{
std::transform(s.begin(), s.end(), s.begin(), toupper);
return s;
}
const std::string & January()
{
static std::string jan("January");
return jan;
}
int main(void)
{
std::vector<std::string> authors { "A", "B", "C" };
Book b1("Book1", authors, 1, "Jan", 2012); // old c-tor

size_t year = 2012
Book b2("Book2", { "A", "B", "C" }, 1, "Jan", year); // old c-tor

std::string month = "Mar";
Book b3("Book3", { "Author" }, 1, toUpper(month), 2012); // old c-tor

Book b4("Book4", { "Author" }, 1, January(), 2012); // old c-tor

std::string book = "Book";
Book b5(std::move(book), std::move(authors), 1, std::move(month), year); // old-ctor

Book b6("Book", { "Author" }, 1, "Jan", 2012); // new c-tor!
}

It may come as a surprise that in all but one case above, the old constructor is called. Only in the last case, the new-ctor is called, which makes the minimum number of copies. So what's the gotcha?

As it turns out, the Book constructors are stopping the compiler from doing a better job. The first constructor takes all the parameters as const reference and the second one takes all the parameters as rvalue reference. Unless and until all the the parameters passed to the constructor are temporary objects (rvalues) or literals, the second constructor does not kick in. This implies lost optimization opportunities for parameters that are temporary (so can be moved) but won't be moved because some other parameter botched the soup. These two constructors give you very limited options: all-or-nothing.

In case of b1, authors is an lvalue and it does not bind to an rvalue ref.
In case of b2, year is an lvalue and does not bind to an rvalue ref.
In case of b3, toUpper returns a string reference; Does no good.
In case of b4, January returns a const string reference; same story!
In case of b5, year is an lvalue that prevents calling the second constructor although programmer tries to explicitly move all the string and vector parameters. In reality, the actual moves do not happen and if the remaining program depends on the moves being successful may not be very happy.

Only in case of b6, all actual parameters are rvalues or literals. Therefore, the "all-rvalue-ref" constructor is called. Note that temporary string objects will be implicitly created where string literals are used.

So lets fix it. What we really need is just one constructor that accepts all the parameters by value. As a side-effect, the overall code is much simpler.


class Book {
public:
Book(std::string title,
std::vector<std::string> authors,
size_t pub_day
std::string pub_month,
size_t pub_year)
: _title (std::move(title)),
_authors (std::move(authors)),
_pub_day (pub_day),
_pub_month(std::move(pub_month)),
_pub_year (pub_year)
{}

// ....
// ....
};

That's it! This is our the constructor that works optimally. All strings, vectors, integral types are passed by value. Within the constructor, the pass-by-value parameters that are on the stack-frame must be moved to the respective data members. The reason is that the lifetime of pass-by-value parameters is anyways limited to the lifetime of the constructor call itself. We want to tie their lifetime to the object and not the constructor.

This constructor does no more deep copies of than necessary. And that number really depends on how it is called. Due to pass-by-value semantics, each object individually is an opportunity for the compiler to perform a move when a temporary is involved. Lets revisit our b1 to b4 objects.

In case of b1, except for authors all other parameters are copied and moved once.
In case of b2, all strings and vectors are copied and moved once. That's what matters.
In case of b3, toUpper returns an string reference; everything else copied and moved only once.
In case of b4, January returns a const string reference; everything else copied and moved once.
In case of b5, strings are vector are moved as expected and in fact the b5 object the local parameters are created using move-constructor and moved again into the data member. Hence the object is created without any deep copies (like zero-copy).

Using pass-by-value also opens other opportunities to eliminate temporaries when functions return by value and are used as actual parameters. However, I won't discuss that in detail here.

Finally, I want to conclude saying that this whole thing assumes that a move is much cheaper than a deep-copy. This is generally true for std::vector and std::string where memory is allocated dynamically. For small strings however small-string-optimization may make copy and move practically equivalent.
Categories: Programming

Continuous Development: The New Maintenance Reality

Dr. Dobb's - Tue, 04/15/2014 - 11:13
The Internet of Things will add so much programmability to devices that keeping software current will become a never-ending task.
Categories: Programming

The gradual erosion of the car trip experience, part 2

The Old New Thing - Tue, 04/15/2014 - 10:00

When I learned that my nieces were heading out on a road trip, I asked, "Are you going to sing songs?"

My eldest niece looked at me as if I were from Mars, then replied, "No, we bring electronics."

Categories: Programming

Microspeak: bar check

The Old New Thing - Tue, 04/15/2014 - 10:00

A bar check sounds like the sort of thing you receive at the end of a long evening of drinking, but that's not what a bar check is.

Among the things that happen at ship room meetings is reviewing each bug that has a proposed fix and deciding whether to accept or reject the fix.

Another thing that happens at ship room meetings is the bar check: The person representing the bug describes the issue and what is known about it so far and asks for a preliminary assessment from the ship room as to whether this is the sort of bug they would approve if a fix were available, in other words, whether it meets the bug bar. If the answer is No, then there is no need to go to the effort of developing a fix for it right now, since you know it would get rejected anyway.

Categories: Programming

Lightweight Virtual Environments in Python 3.4

Dr. Dobb's - Tue, 04/15/2014 - 07:03
Customizing Python's virtual environments for projects with conflicting library requirements or different Python versions is now easy in Python 3.3 and 3.4.
Categories: Programming

Inside an Automated State Machine

Dr. Dobb's - Tue, 04/15/2014 - 04:45
I was pleased to see that the generated code was reasonably easy to understand
Categories: Programming

Tail Call Optimization and Java

Dr. Dobb's - Tue, 04/15/2014 - 04:39
Java 8 requires functional languages to optimize their own tail calls for the time being. What exactly does that involve?
Categories: Programming

Microsoft Python Tools for Visual Studio 2.1 Beta

Dr. Dobb's - Tue, 04/15/2014 - 04:19
Great for "exploratory coding"; new tools also support Bottle and Flask web frameworks
Categories: Programming

Common optimizations

Andrzej's C++ blog - Mon, 04/14/2014 - 10:06
Language designers, compiler and library vendors make a great effort to make your programs run faster and faster. This post is a tour of some common performance optimizations in C++. Consider the following code that deals with std::string: std::string is … Continue reading →
Categories: Programming

Common optimizations

Andrzej's C++ blog - Mon, 04/14/2014 - 10:06
Language designers, compiler and library vendors make a great effort to make your programs run faster and faster. This post is a tour of some common performance optimizations in C++. Consider the following code that deals with std::string: std::string is … Continue reading →
Categories: Programming

The geeky thrill of discovering that two things are really the same thing, just with different labels

The Old New Thing - Mon, 04/14/2014 - 10:00

Today's post about binomial coefficients was intended to be a warm-up for Catalan numbers, but it turns out Eric Lippert already covered them, first in the context of binary trees, then in the context of arbitrary trees and forests, and then again in the context of matched parentheses. Another way of seeing the correspondence between forests and matched parentheses is simply to consider each { as an XML open-tag and each } as an XML end-tag.

One thing to take away from the enumeration of objects controlled by Catalan numbers is that when you see multiplication in a recurrence relation, that typically corresponds to a nested loop. (We saw this ourselves when we studied Stirling numbers of the second kind.)

The correspondence between binary trees and arbitrary forests is done by simply renaming variables: left­Child and right­Child turn into first­Child and next­Sibling.

Renaming variables also reveals an interesting equivalence between the two algorithms for reversing a linked list. One technique is to do link rewriting:

Node *Reverse(Node *head) { Node *prev = nullptr; while (head) { // The node we are rewriting Node *current = head; // Advance to next node before // we overwrite the outbound pointer head = current->next; // Repoint to previous node current->next = prev; // Advance the trailing pointer prev = current; } return prev; }

Another technique is to pop nodes off one list while pushing them onto another.

Node *Reverse(Node *head) { Node *result = nullptr; while (head) { // Pop Node *current = head; head = current->next; // Push current->next = result; result = current; } return result; }

But if you look more closely at the two versions, you'll see that they are not really two algorithms. They are the same algorithm, just with different comments and variable names!

One of my colleauges used this as an interview question and guided candidates through both algorithms, only to discover later that they were actually the same algorithm, merely viewed through different-colored glasses.

Categories: Programming

Enumerating subsets with binomial coefficients

The Old New Thing - Mon, 04/14/2014 - 10:00

Inspired by the Little Program which enumerates set partitions, I decided to do the binomial coefficients this week. In other words, today's Little Program generates all subsets of size k from a set of size n.

As before, the key is to interpret a recurrence combinatorially. In general, when a recurrence is of the form A + B, it means that at the recursive step, you should do A, followed by B. In our case, the recurrence is C(n, k) = C(n − 1, k) + C(n − 1, k − 1). The combinatorial interpretation of the recurrence is to look at how you can go from a set of size n to a set of size n − 1 by studying the effect of removing element n. If element n is not part of the subset, then what's left is a subset of size k. If it is part of the subset, then removing it leaves a subset of size k − 1.

Therefore, our algorithm goes like this:

  • Handle base cases.
  • Otherwise,
    • Recursively call C(n − 1, k) and pass the results through.
    • Recursively call C(n − 1, k − 1) and add element n to each of the results.

As usual, once we spelled out what we're going to do, actually doing it is pretty straightforward.

function Subsets(n, k, f) { if (k == 0) { f([]); return; } if (n == 0) { return; } Subsets(n-1, k, f); Subsets(n-1, k-1, function(s) { f(s.concat(n)); }); };

The first test catches the vacuous base case where you say, "Please show me all the zero-sized subsets of a set of size n." The answer is "There is exactly one zero-sized subset, called the empty set."

The second test catches the other base cases where you say, "Please show me all the k-sized subsets¹ of the empty set." This can't be done if k > 0, because the only subset of the empty set is the empty set itself, and its size is not k.

The meat of the recurrence is pretty much what we said. First, we generate all the k-sized subsets from a set of size n-1 and pass them through. Then we generate all the k-1-sized subsets from a set of size n-1 and add the element n to them.

We can test out the function by logging the results to the console.

Subsets(5, 3, logToConsole);

For style points, we can accumulate the results in helper parameters. This records the pending work in parameters instead of closures, which makes the code easier to port to languages which don't support closures. (And probably helps the efficiency a bit too.)

function AccumulateSubsets(n, k, f, chosen) { if (k == 0) { f(chosen); return; } if (n == 0) { return; } AccumulateSubsets(n-1, k, f, chosen); AccumulateSubsets(n-1, k-1, f, [n].concat(chosen)); }; function Subsets(n, k, f) { AccumulateSubsets(n, k, f, []); }

(I prepend n to chosen for extra style points, since it causes the results to be enumerated in a prettier order.)

As with Stirling numbers, we can use a destructive recursion to reduce memory allocation, if we can count on the callback not modifying the result. I'll leave that as an exercise, because I've got something even better up my sleeve: Getting rid of the recursion entirely!

Let's consider the case of enumerating all the subsets of size k for a fixed k known at compile-time. Let's say k is 3. You can structure this as a series of nested loops.

function Subsets3(n, f) { for (var i = 1; i <= n - 2; i++) { for (var j = i + 1; j <= n - 1; j++) { for (var k = j + 1; k <= n; k++) { f([i, j, k]); } } } }

The outer loop chooses the first element, the middle loop chooses the second element, and the inner loop chooses the last element. This clearly generalizes to bigger subsets; you just need more loop variables.

With this interpretation, you can see how to get from one subset to the next subset: You increment the last element, and if that's not possible without violating the loop constraint, then you back out one level and try incrementing the next-to-last element (and restarting any inner loops), and so on, backing out until you finally find an index that can be incremented (or give up).

function NextSubsetSameSize(s, n) { var k = s.length; // look for an index that can be incremented for (i = k - 1; i >= 0; i--) { // can this index be incremented? if (s[i] < n - k + i + 1) { // increment it s[i]++; // reset all inner loops while (++i < k) s[i] = s[i-1] + 1; return true; } } return false; }

The loop on i looks for the highest index that can be incremented. The loop bounds depend on which index you are studying, since lower indices need to leave enough room for higher indices, but can you figure out the formula by looking at the pattern in Subset3. Once we find an index with room, we increment it and reset all the subsequent indices to their initial values. If we can't find an index to increment, then we report failure.

// Enumerate all subsets of size 3 from a set of size 5 var s = [1, 2, 3]; // initial subset do { console.log(JSON.stringify(s)); } while (NextSubsetSameSize(s, 5)); Note

¹ In math circles, the phrase k-sized subsets is typically abbreviated as k-subsets, but I chose to spell it out here because the shorthand takes some getting used to.

Categories: Programming