예제 #1
0
        private void txtBase64_TextChanged(object sender, EventArgs e)
        {
            // For every 4 characters in the Base64 text, there's 3 UTF-8 characters encoded.
            var plaintextLength = 3 * (txtBase64.Text.Length / 4);

            // If the Base64 text ends with '=' (the Base64 padding character), the plaintext length is one less.
            // There can be up to 2 '=' at the end of a Base64-encoded string.
            if (txtBase64.Text.EndsWith("=="))
            {
                plaintextLength -= 2;
            }
            else if (txtBase64.Text.EndsWith("="))
            {
                plaintextLength -= 1;
            }


            var numerator   = TextEncoder.GetNumeratorFromBase64(txtBase64.Text);
            var denominator = BigInteger.Pow(2, plaintextLength * 8);

            txtBigNumerator.Text   = numerator.ToString();
            txtBigDenominator.Text = denominator.ToString();

            // This is sufficient for encoding a full message with no data loss (numerator and either the denominator OR plaintext message length).

            // To encode (to turn this long numerator into chess moves, assuming each player has one of 30 moves to make):
            //   Create a new variable that represents N/D of the original message.  (This can be done with BigInteger for numerator and denominator.)
            //   Then, if we want to pull an integer value in the range [0,30),
            //     we can multiply the numerator by 30, and divide without remainder.
            // Let's do an example with real numbers.
            //   Encoded N/D:  16705/(2^16)) (the denominator will always be a power of 256.)
            //   Get first encoded digit (from 0 to 30, but not 30):
            //     Multiply the N/D by 30.  (Shortcut: Multiply N by 15, divide D by 2.)
            //     (16705*15)/(2^15)
            //     Now, figure out the first digit.  (Do the division.)
            //     250575/32768
            //     .. which is the same as: 7 + 21199/32768
            //   First digit: 7 (this is the first chess move)
            //     Repeat process for next digits.
            //     Multiply the N/D by 30.  (Shortcut: Multiply N by 15, divide D by 2.)
            //     (21199*15)/(2^14)
            //     Figure out next digit.  (Do the division.)
            //     317985/16384
            //     .. which is the same as: 19 + 6689/16384
            //   Next digit: 19 (this is the 2nd chess move)
            //     Multiply the N/D by 30.  (Shortcut: Multiply N by 15, divide D by 2.)
            //     (6689*15)/(2^13)
            //     Division:
            //     ....
            // Tools needed to make this happen:
            //   New class: BigFraction (has 2 parts: Numerator (BigInteger) and Denominator (BigInteger).)

            // To decode:
            //   For simplicity, let's assume we're seeing moves on a chess board.
            //   Each player, in turn, has X possible moves (in an ordered list), then chooses 1 of those moves.
            //   We can build up a running 'numerator' and 'denominator' to approximate the original message.
            //     Numerator (N) starts at 0, denominator (D) starts at 1.
            //     For move X out of Y:
            //       N = (N * Y) + X;
            //       D *= Y;
            //   We can derive the complete message when D >= PlaintextMessageLength.
            //   We can derive partial messages for any (N, D) because we'll have a Range of actual N we can use.
            // ^ Possible, in theory.  But difficult.  Let's find an easier way.


            // This is a tricky problem.  We don't have infinite precision.  (We don't Need infinite precision.)
            // One ultimate goal is to get back to the original BigInteger; then we'd know the original message Exactly.
            //   We know the plaintext length (given to us in the chessgame header).
            // Subgoal (nice-to-have): Decode the message one byte (move?) at a time.  (eg. move 'a4' is move 0 out of 20 for white, so ...
            //   ... the decimal is between 0.00 and 0.05 (dividing the search space into 20 chunks, and taking the first chunk.)
            //   If the next move is 'a6' (move 0 out of 20 for black), then we'd divide that space into another 20 chunks, and take the first one,
            //   which is 0.000 through 0.004.  So we know the first two digits in the decimal are '00'.  Not sure if this helps us.
            // OR: Instead of using an infinite-precision decimal, let's use a big integer.  That makes more sense.  As we get each digit,
            //   we learn more about the moves.  And we can turn each move into a range (just like before), because we know the total range
            //   of the encoded text-integer (it's 2^[messagelength]).  So we can keep track of a 'min' and 'max', and narrowing it down until we get
            //   to the final plaintext.
            //    ^ Won't work because we'd have fractional parts.  If, for example, the final number is somewhere in [0,10), and we find out that
            //    it's in chunk 2 of 6, we can't easily divide the search space.
            // Maybe revisit the infinite decimal [0-1)?  I don't want to have to make a calculation across ALL digits in the number each time we want
            //   to pop a digit.  (It would be O(n^2), and that's just terrible.  We want O(n).)

            // GetValueString

            if (chkAutoUpdate.Checked)
            {
                /*
                 * var f = new BaseTwoFraction(numerator, plaintextLength * 8);
                 * txtDecimal.Text = f.GetValueDecimalString();
                 */
                var f = new BigFraction(numerator, denominator);
                txtDecimal.Text = f.GetValueDecimalString();
            }
            else
            {
                txtDecimal.Text = "";
            }
        }
예제 #2
0
 private void txtPlaintext_TextChanged(object sender, EventArgs e)
 {
     txtBase64.Text = TextEncoder.EncodeToBase64(txtPlaintext.Text);
 }