Friday, December 5, 2014

TopCoder SRM 639 500-pt

This problem can be broken down into finding the last round the player won, and then adding up the numbers before that number until the player's score is reached. The minimum number of additions are desired, so loop backwards and only add numbers


This passes all system tests and does so quickly.

There are two loops that loop a maximum of N times, where N = the last round player x won. N is between sqrt(x + 2y) and x. The worst case would be not finding N, and therefore looping all the way to X. This gives us O(x). In practice if N is found it will be close to the lower bound.

Premature optimization temptation
I wasted alot of time with this optimization so i figured i would share it, so that I can remember to avoid the premature optimization trap in the future. I always have to remind myself to get the program working first, and then optimize, but only optimize bottlenecks.

Here's an optimization when looking for the last round won (N)
1. N must <= X
2. sum(1 to N) must be = x + y
3. sum(1 to N) = N(N+1)/2 (
4. N(N + 1)/2 = x + y
5. Because of 1. sub N for x, N(N + 1)/2 = N + y
6. Algebra N^2 + N = 2N + 2y
7. Algebra N^2 = N + 2y
8. Algebra N = sqrt(N + 2y)
9. Sub x back in on the right side, giving N = sqrt(x + 2y). And that's the lower bound

Therefore, because of 1. and 9. sqrt(x + 2y) <= N <= X.

In other words start the loop at sqrt(x + 2y) and loop until X

In practice, this saves alot of looping. In case#5 where x=932599670050 and y=67400241741, this is a difference between 1.4 million loops vs 300k loops. However, i'm also outputting the performance times, and this optimization only saves a relatively small amount of time in case #5 and slows down all of the other cases. This is probably because Math.Sqrt() is an expensive operation.

Therefore this is microoptimization, and not worth it at all. I fell into this trap of optimizing before getting the code right. It's a common problem with programming, "premature optimization is the root of all evil," said the quicksort inventor. 

Thursday, December 4, 2014

System.Threading.Timer is firing before its supposed to

I'm using a System.Threading.Timer and specifying a precise time of 10:05 pm, but the timer fires (callback is called) before that time, such as 10:04:59.97 pm.

This causes problems for me because I have to recalculate the next time it should fire based on the current time, and the current time is before the time it was supposed to fire the first time.

Timers aren't guaranteed to fire exactly when you schedule them to. They will fire a little before or after the time you scheduled. For my situation it's bad when they fire before they're supposed to, because subsequent scheduling is affected by this. Therefore if the timer fires before it's supposed to i sleep the thread until its at or after its expected time. This is achieved by keeping track of the Expected Time in the callback state object.

Here's a stripped down version of my code that's used to handle this situation

        internal class TimerStateObject
            internal System.Threading.Timer refTimer = null;
            internal DateTime expectedTime;


Here's the timer init and callback functions

        private void initTimer()
            TimerStateObject timerStateObject = new TimerStateObject();
             timerStateObject.expectedTime = DateTime.Now.Add(ONE_HOUR);
             timerStateObject.refTimer = new System.Threading.Timer(new TimerCallback(timerCallBack), timerStateObject, ONE_HOUR , DISABLE_PERIOD);

        private void timerCallBack(Object timerStateObject)
            TimerStateObject state = (TimerStateObject)timerStateObject;

            state.targetTime = DateTime.Now.Add(ONE_HOUR);

            state.refTimer.Change(ONE_HOUR, DISABLE_PERIOD);

        private void waitUntilExpectedTime(TimerStateObject timerStateObj)
            DateTime now = DateTime.Now;
            while (now.CompareTo(timerStateObj.expectedTime) <= 0)
                TimeSpan diff = timerStateObj.expectedTime.Subtract(now).Add(ONE_SECOND));
                now = DateTime.Now;

Thursday, November 13, 2014

SRM 523 DIV 2

Given a string[] verify that there is a path from A to Z such that each letter is adjacent to the previous letter (not diagnol)

public class AlphabetPath
    public class Point
    public int row = -1;
    public int col = -1;
    public bool notFound()
     return (row == -1 || col == -1);
    public bool isAdj(Point p)
     Left = same row, 1 left of col
     Right = same row, 1 right of col
     Below = same col, 1 row below
     Above = same col, 1 row above
    if (p.row == row)
    if ((p.col - 1) == col)
    return true;
    else if ((p.col +1) == col)
    return true;
    else if (p.col == col)
    if ((p.row - 1) == row)
    return true;
    else if ((p.row + 1) == row)
    return true;
    return false;
    public Point find(char c, string[] letterMaze)
    Point p = new Point();
    for(int i = 0; i < letterMaze.Length; i++)
    p.col = letterMaze[i].IndexOf(c);
    if (p.col != -1)
    p.row = i;
    return p;
public string doesItExist(string[] letterMaze)
Point prev = new Point();
for(char c = 'A'; c <= 'Z'; c++)
Point p = find(c, letterMaze);
return "NO";

if (c != 'A')
return "NO";

prev = p;

return "YES";


This passed all 5 test cases. However, this is brute force and would probably fail a performance test.

This is O(rows*columns*n). I say N because this problem can be generalized to work on any sequence of letters/numbers. Using the 26 letter alphabet is just a special case. 

The primary constraint is that letters must be adjacent to the previous letter. This means we really only need to look in the adjacent spots. Therefore an optimization would be:
1) find the position of A
2) loop through B to Z, only looking in spots adjacent to the previous letter. 

Finding A would be O(rows*columns). Looping through N-1 letters and looking only in the four possible adjacent spots would be O(4*(n-1)). The constant is dropped leaving O(n-1). Combining these gives us O(rows*columns) + O(n-1). O(rows*columns) >= O(n-1), therefore it becomes O(rows*columns).

So this optimization improves the performance from O(r*c*n) to O(r*c), a factor of N. 

Lessons learned
I wasn't thinking of the performance when doing the problem at first, and ended up with a brute force algorithm. I just started coding the solution right away, doing the first solution that came to mind. I think it's normal to think of brute force first. The lesson here is that I should've thought out some solutions on paper before starting to code.

Sunday, November 9, 2014

SRM 636 Div 2 250-pt


This is one of the harder 250 point problems i've ran into. Looking at the stats this seems to be the case for others as well; only 67% correct and 195 avg points.

public int count(int[] stones)
int stonesPerBucket = getStonePerBucket(stones);

int posActions = 0;
int negActions = 0;

for(int i = 0; i < stones.Length; i++)
int xFerAmt = stonesPerBucket - stones[i];
if (xFerAmt % 2 != 0)
return -1;

if(xFerAmt < 0)
negActions += xFerAmt;
else if (xFerAmt > 0)
posActions += xFerAmt;

if (posActions + negActions == 0)
return posActions / 2;
return -1;

public int getStonePerBucket(int[] stones)
int sum = 0;
foreach(int i in stones)

int want = sum / stones.Length;

return want;


i really overthought this one at first. My initial solution, which failed many cases, looped through and tried to add/substract stones between the buckets. After thinking about it some more I realized this didn't have to be that hard. Just need the difference between the # of stones in each bucket and the required stones per bucket, and the negative differences must balance with the positive balances.

Lesson learned - need to understand the problem statement fully before jumping into the coding. In other words, need to interpret the problem and come up with a design for the solution before jumping into implementing the code.

SRM 637 Div 2 250-pt


You're given two int[] with the same length. Count the number of times the second int[] has a larger value at i than the first int[].

public int calc(int[] snuke, int[] sothe)
int win = 0;
for(int i = 0; i < snuke.Length; i++)
if (snuke[i] > sothe[i])
return win;


O(n) where n is the length of the arrays. 

SRM 638 Div 2 250-pt


Convert a word in snake case to camel case. This means remove _'s and each letter after the _ needs to be capitalized.
For example: good_morning_world -> goodMorningWorld.

public string toCamelCase(string variableName)
StringBuilder sb = new StringBuilder();
bool makeUpper = false;
foreach(char c in variableName)
if (c == '_')
makeUpper = true;
if (makeUpper)
makeUpper = false;

return sb.ToString();


Iterate through the string once. It's O(n) where n=length of string. 

Is it 'coder' or 'software engineer'?

Is it coder? Is it software engineer? Programmer? Software developer?

Those are just job titles meaning the same thing: get a problem, design the solution to that problem, code the solution, and then test the code you wrote. Depending on the size of the problem and the team these things might get partitioned out such that individuals are given a smaller problem to solve. But it always boils down to those four tasks:
1) receive requirements 
2) design the solution 
3) write the code
4) test what you wrote

Notice I said receive the requirements, which is quite vague? That's because sometimes you're going to be the one gathering the requirements from the customer, and other times you're going to get a detailed spec saying which component you're responsible for, and everything in between.

Testing is the same way; minimally you are going to unit test the code YOU wrote. If you're a one man shop you're going to also need to do full blown testing on the entire program.

Requirements and testing are jobs that can be done by non-programmers. Sometimes the programmers have to do those jobs though. In any case a programmer/coder/SWengineer is always going to do those 4 things mentioned above no matter what your job title.

Friday, November 7, 2014

Dexterity - Alternate form will not checkin to source safe

I modified a form and can't check it into the repository. I've tried several things, including hitting Update SCC state over and over, but it always stays as "Main Product"

Sometimes you just need to bypass Dexterity to get things to work. In this case I manually added the exported alternate form into the VSS and tricked it into working.

1. Export the alternate form as a text file
2. Copy it to the VSS server
3. Add it into the repository
4. In Dex do Update SCC state. Notice that it no longer says Main Product
5. Lock the resource and check it in.

Step 5 is important because when you check in an alternate resource it only saves the parts you modified, and since we had to force it into the repository it had everything, including all the original form's fields. So locking and checking in gets it into the right state.

Wednesday, November 5, 2014

Writing a spec

Over the last few years I've been developing at a crazy pace, and I haven't had the time to write long specs. Instead, how I write specs has evolved to adapt to the pace of development.

First im making the assumption that I'm given the requirements in some form, from highly detailed specs to vague UI mockups on napkins.

Steps to write the spec
1. For each requirement break it down into concrete coding tasks. These should explain the name (ex: a function name) and what it should do (ex: insert new record into table)

2. From this list of coding tasks a design will emerge. 

3. Illustrate the design with diagrams (ex: database, classes, data flow) 

4. Review and refactor the design until duplication is minimized.

1. Instructions for writing the code.

2. Precise estimates. Because the coding tasks are concrete, ie not vague, explanations, precise estimates can be made.

3. Explanation of the design, which makes future maintenance easier.

4. Minimize design changes during coding because the design was reviewed and refactored. That doesn't mean it won't change if something was overlooked.

Friday, October 17, 2014

Scroll window won't fill from table reference in Dexterity

I have a global table reference. It's used to display session info to the user, and is populated from several modules during runtime.

When I try to fill the scrolling window from this reference using the syntax:
fill window LineScroll table(<table reference>)
it's not filling the window.

This appears to be a bug (or undocumented limitation?) in Dex.

I got around this limitation by doing a range copy from the table reference to the local table buffer.

clear table (tmpTableRef);
range clear table (tmpTableRef);
'Company ID' of table (tmpTableRef) = 'Company ID' of globals;
clear ProductID of table (tmpTableRef);
range start table (tmpTableRef) by number 1;
fill ProductID of table (tmpTableRef);
range end table (tmpTableRef) by number 1;

range copy table (tmpTableRef) to table tmpTable;

fill window LineScroll by number 1;

Saturday, October 11, 2014

Must install Web Developer Tools in VS2013 to open Web Service

The Web Developer Tools option must be installed prior to opening or creating Web projects.You can install this option by repairing your Microsoft Visual Studio installation and ensuring that 'Web Developer Tools' is checked in the list of optional components

1. Add / Remove Programs
2. Choose Microsoft Visual Studio (whatever version you're having the trouble in)
3. Click Change at the top
4. Click Modify
5. Choose Microsoft Web Developer Tools
6. Click Update

Thursday, October 2, 2014

Simple TextBox that only accepts numeric values in C#

1. Only accepts integers
2. Allows backspace and other control characters
3. Accepts pasted input
4. Rejects pasted input if it's not an integer
5. Protects against invalid data type by initializing to 0 and disallowing empty strings -- thus you don't have to check before converting to an integer
6. Selects all text automatically if it's 0. This is useful if you don't want to have to manually select all to type over the 0

4. Async select all on enter

  1.     public class NumericTextBox : TextBox
  2.     {
  3.         private string lastGoodValue = "";
  4.         protected override void OnKeyPress(KeyPressEventArgs e)
  5.         {
  6.             base.OnKeyPress(e);
  8.             if (Char.IsDigit(e.KeyChar)  || e.KeyChar == '\b' || Char.IsControl(e.KeyChar) || isPaste(e))
  9.             { }
  10.             else
  11.             {
  12.                 e.Handled = true;
  13.             }
  14.         }
  16.         private bool isPaste(KeyPressEventArgs e)
  17.         {
  18.             if ((((int)e.KeyChar == 22) || ((int)e.KeyChar == 3))
  19.             && ((ModifierKeys & Keys.Control) == Keys.Control))
  20.                 return true;
  21.             return false;
  22.         }
  24.         protected override void OnValidating(System.ComponentModel.CancelEventArgs e)
  25.         {
  26.             base.OnValidating(e);
  27.             setTextTo0IfEmpty();
  28.         }
  29.         protected override void OnTextChanged(EventArgs e)
  30.         {
  31.             base.OnTextChanged(e);
  32.             setTextTo0IfEmpty();
  33.             int dummy;
  34.             if (!Int32.TryParse(Text, out dummy))
  35.                 resetToLastGoodValue();
  36.             lastGoodValue = Text;
  38.         }
  40.         private void resetToLastGoodValue()
  41.         {
  42.             Text = lastGoodValue;
  43.             Select(Text.Length0); //otherwise the cursor goes to the beginning of the textbox
  44.         }
  46.         private void setTextTo0IfEmpty()
  47.         {
  48.             if (Text == "")
  49.             {
  50.                 Text = "0";
  51.                 SelectAll(); //otherwise you have to manually select the 0 to type over it
  53.             }
  54.         }
  55.         private void AsyncSelectAll()
  56.         {
  57.             BeginInvoke((Action)delegate
  58.             {
  59.                 SelectAll();
  60.             });
  61.         }
  62.         public NumericTextBox() : base()
  63.         {
  64.             setTextTo0IfEmpty();
  65.         }
  66.         protected override void OnEnter(EventArgs e)
  67.         {
  68.             if (Text == "0")
  69.                 AsyncSelectAll();
  70.         }
  72.         public int IntValue
  73.         {
  74.             get
  75.             {
  76.                 return Int32.Parse(this.Text);
  77.             }
  78.         }
  79.     }

Tuesday, September 30, 2014

Unit Test Template for VS2013

Starting in VS2012 Microsoft removed the Generate Unit Tests option from the context menu, because it was just too efficient. With good intentions these people were kind enough to create an almost replacement, here: Too bad it only works on public methods (in other words, it doesn't work with internal methods!).

Fine, i'll just save this unit test template to the testing project, and copy and paste every time i want to test a method. At least Microsoft won't be able to take this away from me.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestingProjectName
    public class UnitTestClassName
        public void UnitTestMethodName()



"Create Unit Test..." is missing in VS2013

The Create Unit Test option in the context menu is missing in Visual Studio 2013.

Microsoft removed that because it was tightly coupled to their unit testing product, and they wanted to open up VS to external unit testing products.


1. Tools > Extensions and Updates

2. Click Online

3. Search for Unit Test Generator

4. Click on it, download it, and install it

warning: This currently only works for public methods, so it doesn't help for internals. How I'm using it generating a template, and then modifying it so it works with the internal class.


Monday, September 22, 2014

SQL Alias doesn't work if network disconnects

I'm using a named pipe alias, and when the network disconnects I'm no longer able to connect using the alias.

My settings are as follows:
Protocol: Named Pipes.

Change the alias protocol to TCP/IP and specify the server\instance name in the Server textbox, as follows:
Protocol: TCP\IP
Confirm this is working by disabling / disconnecting from your network and trying to connect to SQL using the alias.

I only found one other post that had the same problem as me. I answered their question on stacked overflow since it worked for me.

Monday, September 15, 2014

Tuesday, September 2, 2014

Dexterity - modified Transaction template for when you require a transaction

This is from the Microsoft Dexterity Help 12.0b2 in the Transaction template page. I have modified it so that it only proceeds with the logic if it can start a transaction. 

A good, clean design has error handling separated from the business logic, so it calls DoLogic() which does the actual business logic. Errors bubble up to this error handler. This keeps the transaction / error handling away from the logic and really clarifies the business logic as well as the error handling.


{The Transaction Template}
local integer retry_count;
local integer MAXRETRIES = 3;
local long sleeptime;

retry_count = 0;
    if CanStartTransaction() then
        transaction begin;
  if DoLogic() then
                 transaction commit;
   transaction rollback;
  end if;
  warning "DoLogic() must be executed within a transaction, but one cannot be started."
    end if;

     if retry_count < MAXRETRIES then
         sleeptime = Timer_Sleep(10);
         increment retry_count;
         restart try;
     end if;
    transaction rollback;
    error "An exception occurred. Class: " + str(Exception_Class()) + " Subclass: " + str(Exception_SubClass()) + " Message:" + Exception_Message();
end try;

Friday, August 29, 2014

Dexterity open form return to is not causing the change script to run in the returned to field

I have two forms: Batch and MassAddTransactions. Batch has a button called Mass Add Transactions. This calls open form MassAddTransactions return to '(L) ProcessReturnFromMassAdd'. Then in MassAddTransactions the "Done" button does return true; The purpose of this is to signal the Batch window to fire the ProcessReturnFromMassAdd change script.

The problem is the change script is not firing!

My (L) ProcessReturnFromMassAdd field was a boolean. Changing this field to a Long Int, and then returning 0 to it caused the change script to run as expected.

This must be an undocumented requirement for using the open form return to statement, or perhaps it's a bug.

Tuesday, August 19, 2014

Get first on table returning results when it shouldn't be

I have a global script that is setting a range on a table then calling get first. This script is called multiple times during a transaction. The first time it's called it works as expected. The second, and subsequent, calls it produces strange results. The get first should not be returning anything, but it is.

Before setting the range call range clear table

Monday, August 4, 2014

Installshield prerequisite 32-bit vs 64-bit environment

For 32 bit

1. A registry entry has a certain value
2. Registry key name = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
4. Value data = x86
5. Contains

For 64 bit

1. A registry entry has a certain value
2. Registry key name = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
4. Value data = 64
5. Contains

Sunday, August 3, 2014

Project schedule and estimates

I started reading Joel on Software (the book, made from the website) a few months ago and just picked it up again recently. I left off on a chapter about specs, which I skipped over (which is probably why specs are so underused), and came upon the chapter on scheduling.

I like the simple, pragmatic method for creating a project schedule. Basically in a spreadsheet write down columns Feature | Task | Priority | Orig Est | Cur Est | Elapsed | Remaining

First, each feature needs to be broken down into tasks. Next, estimates should be given in hours. The more precise the unit of measure the more the programmer has to think about how they're going to accomplish a task. This naturally leads to developing a design. Tasks might even need to be broken down even more until they are no longer vague. This will naturally follow from precise estimates because it's easier to give estimates for concrete tasks.

So now we have each feature broken down into concrete tasks with estimates and a well thought out design. Who would've thought precise estimates could result in a well organized project?

In Pragmatic Programmer they said to use higher units of measure when giving estimates because "2 months" is alot more flexible than "50 days". When you say "50 days" they really, really expect you to be close to done in 50 days. Conversely, when you say "2 months" you can be off by a few weeks in either direction and it's fine.

Here's how I'm going to give both precise estimate and higher units of measure.
1) Use the simple spreadsheet method and break down features into tasks, giving very precise estimates in hours.

2) When it comes time to give my estimates to managers I will roll up the estimates from the spreadsheet for each feature and then translate it into a less precise unit of measure.

Let's say I am given a new project with 3 features. The first thing I'm going to do is write these 3 features into the spreadsheet in the format mentioned above. Then I'm going to go through each feature one at a time and figure out the steps needed to complete this feature. While doing that I'm going to write down the estimates. If I can't give a good estimate for one of the tasks then I need to break that task down further. As a boring example:
Feature 1 has 4 tasks for a total of 36 hours
Feature 2 has 10 tasks for a total of 60 hours
Feature 3 has 2 tasks for a total of 8 hours

I wouldn't present those numbers to a manager. First, i would translate it into a less precise unit of measure.
And remember, 1 day = 8 hours. 1 week = 5 days = 40 hours. Don't estimate with overtime taken into consideration, because then you're going to be expected to work overtime!

36 hours is roughly 1 week. 60 hours is 1.5 weeks. And 8 hours is 1 day. Those are the times I would give to a manager. Now the manager has good information for deciding what to tell the customer, and ultimately for deciding which features to keep and which to cut.

So that's how I'm going to use the precise project schedule estimates and also give the less precise estimates to managers.

Thursday, July 17, 2014

Dynamics GP - "The Deployment has exceeded the maximum request length allowed by the target server" when installing reports


When installing reports in Dynamics GP i'm running into the following error

Navigate to the Reporting Services folder and add maxRequestLength="20690" to the httpRuntime node. The folder should be located here: \Program Files\Microsoft SQL Server\MSRS10.[SSRSInstanceName]\Reporting Services\ReportManager

Monday, July 14, 2014

How to see who has files open in shared

I went to remove sharing from a shared folder because I'm migrating the data to another server. Windows said something like "there are N users with opened files. By removing this share you will boot them"

This is on Windows Server 2003, probably different in newer versions...

1. Computer Management
2. Shared Folders
3. Open Files

From there you can see who has what open. In my case it was me with 7 sessions opened. So I right-clicked all of my sessions and closed the file connection. Then was able to remove Sharing from that folder without complaints.


Tuesday, July 1, 2014

In Dexterity - Passing in a checkbox for a boolean parameter results in it flipping the value

I'm passing a checkbox field, specifically CMTransactionObject:'Auto Post To GL', to a script which is expecting a boolean. 'Auto Post To GL' is a checkbox type.

I put warning messages, like this warning str(CMTransactionObject:'Auto Post To GL') before the script is called, and then as the first statement in the script itself.

Before script - 1
In script - 0

Explicitly cast the checkbox to a boolean.

Way 1-
local boolean value = CMTransactionObject:'Auto Post To GL';

call script, value;

Way 2-
call script, boolean(CMTransactionObject:'Auto Post To GL');
This way should not be done with inout parameters, only in.

This actually seems to be caused by passing a local variable to two different parameters, one with in one with inout

Sunday, June 29, 2014

notepad++ regex replace group

I am updating legacy code where assignments can be done in two ways:
Way 1 - set field to value;
Way 2 - field = value;

I'm used to Way 2, so i want to mass update Way 1 to Way 2 in order to make it more readable.

Using Notepad++ using the Replace tool with regex.

1. In Search Mode choose Regular expression. Make sure "Wrap around" is checked.

2. Capture groups are done by using parentheses. So in Find what put set (.+) to (.+);

3. Then you can use the value from the capture group by using \#. So in Replace with put \1 = \2;


Saturday, June 14, 2014

Adapter, decorator, and facade patterns

Decorator: wraps the interface of a class to add functionality. This can be done by delegating or subclassing.

Adapter: maps one interface to an expected interface. This is so client code doesn't need to be changed when an interface changes. This can be done by subclassing the expected interface and wrap-and-map the unexpected interface.

Facade: take a complex interface, which may be composed of several classes, and simplify the interface with a Facade class that hides lots of the details.

Friday, June 13, 2014

Trigger register error #2 - SY_INVALID_SCRIPT

I want to contain all code for a certain module in one form. When i go to register a focus trigger against 'Save Button' on a form, and set the processing script to a script on my form, it is giving me registration error #2, which means the processing script is invalid.

Apparently you cannot specify a script that is contained in a form for a focus trigger. It has to be a global script.

No wonder this legacy code has 100's of global scripts, it's forced. This had lead to an epic mess because these scripts can't be logically organized.

Thursday, June 12, 2014

Trigger won't register in Dynamics GP

In Startup i'm checking if multicurrency is registered:
if 'Module Registered'[MCUR] of globals then
{register triggers}
end if;

but it is never registering the triggers, even though MC is registered!

Apparently the Module Registered global field doesn't get populated before the login takes place. This means the conditional above is never going to succeed, thus resulting in the trigger registration never getting hit.

Instead of putting the check here, put it in the trigger procedures themselves. 

Wednesday, June 11, 2014

Warning dialog won't wrap text in Dynamics GP

The warning dialog text doesn't wrap

warning "The card must have the option Accepted from Customers checked";

Put a period at the end!

warning "The card must have the option Accepted from Customers checked."; <--- period

There was an error in this gadget