├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── src ├── main.rs └── lib.rs └── LICENSE.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "add-one" 5 | version = "1.0.0" 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["o2sh "] 3 | description = "Adds one to a number" 4 | license = "MIT" 5 | name = "add-one" 6 | readme = "README.md" 7 | repository = "https://github.com/o2sh/add-one" 8 | version = "1.0.0" 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # add-one 2 | 3 | [![crate](https://img.shields.io/badge/crates.io-1.0-orange.svg)](https://crates.io/crates/add-one) 4 | [![documentation](https://img.shields.io/badge/docs-1.0-blue.svg)](https://docs.rs/add-one) 5 | 6 | > Returns n + 1. 7 | 8 | ## Example 9 | 10 | ```rust 11 | use add_one::add_one; 12 | 13 | fn main() { 14 | let mut bytes = Vec::new(); 15 | 16 | match add_one("123".as_bytes(), &mut bytes) { 17 | Ok(()) => println!("{}", std::str::from_utf8(&bytes).unwrap()), 18 | Err(e) => { 19 | eprintln!("Error: {}", e); 20 | } 21 | } 22 | } 23 | ``` 24 | 25 | or 26 | 27 | ```bash 28 | cargo install add-one 29 | add-one 12 30 | 13 31 | ``` 32 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate add_one; 2 | 3 | use add_one::add_one; 4 | use std::env; 5 | use std::io::stdout; 6 | use std::process::exit; 7 | 8 | fn main() { 9 | let mut args = env::args(); 10 | 11 | if args.next().is_none() { 12 | eprintln!("Too few arguments. Expected program name and a single argument."); 13 | return; 14 | }; 15 | 16 | let input_str = if let Some(arg) = args.next() { 17 | arg 18 | } else { 19 | eprintln!("Too few arguments. Expected a single argument."); 20 | return; 21 | }; 22 | 23 | if args.next().is_some() { 24 | eprintln!("Too many arguments. Expected a single argument."); 25 | return; 26 | }; 27 | 28 | match add_one(input_str.as_bytes(), &mut stdout()) { 29 | Ok(()) => println!(), 30 | Err(e) => { 31 | eprintln!("Error: {}", e); 32 | exit(1); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 02sh and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This add-one crate is meant to give you the opportunity to add one to a number. 2 | //! 3 | //! # Examples 4 | //! 5 | //! ``` 6 | //!extern crate add_one; 7 | //!use add_one::add_one; 8 | //! 9 | //!use std::str; 10 | //! 11 | //!fn main() { 12 | //! let mut bytes = Vec::new(); 13 | //! 14 | //! match add_one("123".as_bytes(), &mut bytes) { 15 | //! Ok(()) => println!("{}", str::from_utf8(&bytes).unwrap()), 16 | //! Err(e) => { 17 | //! eprintln!("Error: {}", e); 18 | //! } 19 | //! } 20 | //!} 21 | //! ``` 22 | //! 23 | //! ## Compatibility 24 | //! 25 | //! The `add-one` crate is tested for rustc 1.26 and greater. 26 | 27 | use std::io; 28 | 29 | pub fn add_one(digits: &[u8], output: &mut T) -> Result<(), io::Error> { 30 | // Parse the leading '-' for negative numbers. 31 | let (minus, digits) = match digits.split_first() { 32 | Some((&b'-', digits)) => (true, digits), // Negative number 33 | _ => (false, digits), 34 | }; 35 | 36 | // Validate (ASCII) digits. 37 | if digits.is_empty() || !digits.iter().all(|&c| c >= b'0' && c <= b'9' || c == b'.') { 38 | return Err(io::Error::new( 39 | io::ErrorKind::InvalidInput, 40 | "Invalid characters in input".to_string(), 41 | )); 42 | } 43 | 44 | // Remove any leading zeros. 45 | let digits = &digits[digits.iter().position(|&c| c != b'0').unwrap_or(digits.len())..]; 46 | 47 | let z = digits.iter().position(|&c| c == b'.').unwrap_or(digits.len()); // position of decimal 48 | 49 | // Find any trailing 9's (when positive) or 0's (when negative) which will carry the carry bit. 50 | let (prefix, trailing) = digits[..z].split_at( 51 | digits[..z].iter() 52 | .rposition(|&c| c != if minus { b'0' } else { b'9' }) 53 | .map_or(0, |x| x + 1) // The position *after* the last non-nine/zero. 54 | ); 55 | 56 | if let Some((&digit, prefix)) = prefix.split_last() { 57 | // The last digit before that will have to be incremented/decremented, 58 | // and anything before it is left unchanged. 59 | let new_digit = if minus { 60 | if prefix.is_empty() && trailing.is_empty() && digit == b'1' { 61 | // Special case for -1: Result is no longer negative. 62 | return output.write_all(b"0"); 63 | } 64 | output.write_all(b"-")?; 65 | digit - 1 66 | } else { 67 | digit + 1 68 | }; 69 | // Write prefix, unchanged. 70 | output.write_all(prefix)?; 71 | // Write changed digit, unless it became a leading zero. 72 | if !prefix.is_empty() || new_digit != b'0' { 73 | output.write_all(&[new_digit])?; 74 | } 75 | } else if minus && digits.len() > 0 && digits[0] == b'.' && !digits[1..].iter().all(|&d| d == b'0') { 76 | output.write_all(b"0")?; 77 | } else { 78 | output.write_all(b"1")?; 79 | } 80 | for _ in 0..trailing.len() { 81 | output.write_all(if minus { b"9" } else { b"0" })?; 82 | } 83 | if minus && z == 0 && digits.len() > 1 { 84 | // write decimal 85 | output.write_all(&digits[0..1])?; 86 | let mut iter = digits[1..].iter().rev().peekable(); 87 | let mut decimal_digits = Vec::new(); 88 | while let Some(&b'0') = iter.peek() { 89 | decimal_digits.push(*iter.next().unwrap()); 90 | } 91 | if let Some(_) = iter.peek() { 92 | decimal_digits.push(b'9' - *iter.next().unwrap() + b'0' + 1); 93 | } 94 | while let Some(_) = iter.peek() { 95 | decimal_digits.push(b'9' - *iter.next().unwrap() + b'0'); 96 | } 97 | output.write_all(decimal_digits.iter().rev().cloned().collect::>().as_slice())?; 98 | } else { 99 | output.write_all(&digits[z..])?;// prints the characters after decimal 100 | } 101 | Ok(()) 102 | } 103 | 104 | #[test] 105 | fn add_one_test_integer() { 106 | fn test(num: &str, result: &str) { 107 | use std::str::from_utf8; 108 | let mut s = Vec::new(); 109 | add_one(num.as_bytes(), &mut s).unwrap(); 110 | assert_eq!(from_utf8(&s).unwrap(), result); 111 | } 112 | test("1234", "1235"); 113 | test("0", "1"); 114 | test("9", "10"); 115 | test("19", "20"); 116 | test("99", "100"); 117 | test("99988999", "99989000"); 118 | test("99999999", "100000000"); 119 | test("0001234", "1235"); 120 | test("-9", "-8"); 121 | test("-10", "-9"); 122 | test("-100", "-99"); 123 | test("-0100", "-99"); 124 | test("-1100", "-1099"); 125 | test("-01100", "-1099"); 126 | test("-1", "0"); 127 | test("-001", "0"); 128 | test("-0", "1"); 129 | test("-000", "1"); 130 | test( 131 | "1256146513513224524524524524522452165841613615616516516", 132 | "1256146513513224524524524524522452165841613615616516517", 133 | ); 134 | test( 135 | "1237801293471034709342345050491203491230949139249123949999999", 136 | "1237801293471034709342345050491203491230949139249123950000000", 137 | ); 138 | test( 139 | "-1237801293471034709342345050491203491230949139249123940000000", 140 | "-1237801293471034709342345050491203491230949139249123939999999", 141 | ); 142 | test("-2", "-1"); 143 | } 144 | 145 | #[test] 146 | fn add_one_test_float() { 147 | fn test(num: &str, result: &str) { 148 | use std::str::from_utf8; 149 | let mut s = Vec::new(); 150 | add_one(num.as_bytes(), &mut s).unwrap(); 151 | assert_eq!(from_utf8(&s).unwrap(), result); 152 | } 153 | test("0.0", "1.0"); 154 | test("5000.0", "5001.0"); 155 | test("1139.67", "1140.67"); 156 | test("123.321", "124.321"); 157 | test("99.99", "100.99"); 158 | test("-1.0", "0"); 159 | test("2.0", "3.0"); 160 | test("000.000", "1.000"); 161 | test("-000.00", "1.00"); 162 | test("-0.9", "0.1"); 163 | test("-0.76987965465", "0.23012034535"); 164 | test("0123456789.0987654321", "123456790.0987654321"); 165 | } 166 | 167 | --------------------------------------------------------------------------------